Ionic v4 form ngSubmit not triggering

Hello guys, I am having difficulties trying to implement a simple login form.

@Component({
  selector: "app-login",
  templateUrl: "./login.page.html",
  styleUrls: ["./login.page.scss"]
})
export class LoginPage implements OnInit {
  constructor(private service: AuthenticationService) {}
  user = {};

  login() {
    this.service.login();
  }

  onSubmit() {
    console.log(this.user);
  }
}
<form (ngSubmit)="onSubmit()">
    <ion-item>
      <ion-label position="floating">Username</ion-label>
      <ion-input
        type="text"
        [(ngModel)]="user.username"
        name="username"
      ></ion-input>
    </ion-item>
    <ion-item>
      <ion-label position="floating">Password</ion-label>
      <ion-input
        type="text"
        [(ngModel)]="user.password"
        name="password"
      ></ion-input>
    </ion-item>
    <ion-button type="submit" expand="block">Sign up</ion-button>
  </form>

When I click the sign up button I donā€™t see anything in the console. What am I missing here?

Ok just in case someone else encounters the same issue, I restarted the server and it worked just fine after relaunching the app again, weird.

Even if the problem is solved, I think the better way to do it is like that:

<form #user="ngForm" (ngSubmit)="onSubmit(user)">

And in your onSubmit function you can access the fields like this:
user.username or user.password.

Why is this a better way? Also with this way do I need to import an Angular component in my app.module.ts?

Hello there, following your advice I decided to use Reactive Forms instead but I am getting the following error in the console:

Canā€™t bind to ā€˜formGroupā€™ since it isnā€™t a known property of ā€˜formā€™

Hereā€™s my code:

import { Component, OnInit } from "@angular/core";
import { FormGroup, FormControl } from "@angular/forms";
import { AuthenticationService } from "../../services/authentication.service";

@Component({
  selector: "app-login",
  templateUrl: "./login.page.html",
  styleUrls: ["./login.page.scss"]
})
export class LoginPage implements OnInit {
  constructor(private service: AuthenticationService) {}
  loginForm = new FormGroup({
    username: new FormControl(),
    password: new FormControl()
  });
  user = {};

  onSubmit() {
    console.log("submitted");
  }

  ngOnInit() {
    //console.log("Checking");
  }
}

I also imported the ReactiveFormsModule to my app.module.ts

import { ReactiveFormsModule } from "@angular/forms";
...
imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    IonicStorageModule.forRoot(),
    HttpClientModule,
    ReactiveFormsModule
  ],
<form [formGroup]="loginForm">
    <ion-item>
      <ion-label position="floating">Username</ion-label>
      <ion-input type="text" formControlName="username"></ion-input>
    </ion-item>
    <ion-item>
      <ion-label position="floating">Password</ion-label>
      <ion-input type="text" formControlName="password"></ion-input>
    </ion-item>
    <ion-button type="submit" expand="block">Sign up</ion-button>
    <ion-button expand="block" color="primary" [routerLink]="['/register']"
      >Register</ion-button
    >
  </form>

What am I doing wrong?

edit: NVM, I just had to add ReactiveFormsModule to my login.module.ts file imports instead of my app.module.ts

you donā€™t need to do it that way. that what you had was already correct. Just change your form to this:

<form #user="ngForm" (ngSubmit)="onSubmit(user)">

and in the .ts file

onSubmit(loginData) {
    console.log(loginData.username);
    console.log(loginData.password);
  }

For simple forms like this, you donā€™t need neccesserly formGroups

#user=ā€œngFormā€ gives you a variable for that view and makes it easer to handle the data

I see, how would you add validation this way? i.e, showing div right below the input showing the error if there is an error.

you can add this

<ion-button type="submit" [disabled]="!user.form.valid">Login</ion-button>

to check if the input fields are valid and in the same way you can use ngIF to show a div or a message or something else.

I did it like this:

<form #form="ngForm" (ngSubmit)="onSubmit(form)">
    <ion-item>
      <ion-label position="floating">Username</ion-label>
      <ion-input
        type="text"
        name="username"
        #username="ngModel"
        ngModel
        required
        minlength="5"
        maxlength="50"
      ></ion-input>
    </ion-item>
    <div *ngIf="username.touched && !username.valid">
      <div *ngIf="username.errors.required">Username is required</div>
      <div *ngIf="username.errors.minlength">
        Username should be minimum
        {{ username.errors.minlength.requiredLength }} characters
      </div>
    </div>
    <ion-item>
      <ion-label position="floating">Password</ion-label>
      <ion-input
        type="text"
        name="password"
        #password="ngModel"
        ngModel
        required
        minlength="5"
        maxlength="50"
      ></ion-input>
    </ion-item>
    <div *ngIf="password.touched && !password.valid">
      <div *ngIf="password.errors.required">password is required</div>
      <div *ngIf="password.errors.minlength">
        password should be minimum
        {{ password.errors.minlength.requiredLength }} characters
      </div>
    </div>
    <ion-button type="submit" expand="block">Sign up</ion-button>
    <ion-button expand="block" color="primary" [routerLink]="['/register']"
      >Register</ion-button
    >
  </form>

Would you say this is a good way to implement a login form validation?

Also do you think I should use ReactiveForms for the registration form, I ask this because Iā€™d probably need to check asynchronously if the user exist in the database or not and display a loader while checking, although this may be unnecessary.

I didnā€™t run your code but seems to be ok.
And if I remember it right, you can solve the asynchronous check with (ngModelChange) in your username input field without using ReativeForm.

Would you be able to provide an example of that? Thanks for the help BTW.

Iā€™ll be at my desc in few hours and will get back to you.
meanwhile you can have a look on (ngModelChange). uncle google will help you :slight_smile:

1 Like

.html

<ion-input (ngModelChange)="checkUsername(form)"
				type="text"
				name="username"
				#username="ngModel"
				ngModel
				required
				minlength="5"
				maxlength="50"
		></ion-input>

and .ts file:

checkUsername(formData) {
        let username = formData.controls.username.value;
        if (username.length > 5) {
            console.log('Do Something with: ' + username);
        }
    }

cheers

So basically in this function is where I would call the server to check if the username already exists. Then I think I have to use the pending property of the formControl to show the loader image.

Thanks, appreciate it.