Form Validators are Not working on first load need to refresh view

so i have been using form validators on my register page … so when i run on browser using ionic serve the validators wont work on first launch unless i refresh the page once and then they start working . the same thing is happening on the build apk as well and over their i can not refresh even restarting the app wont help over their what can be the issue ?
my register page html code

<ion-header>
  <ion-toolbar color="primary">
    <ion-title>Register</ion-title>
    <ion-buttons slot="start">
      <ion-back-button></ion-back-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>
 
<ion-content class="ion-padding">
  <form class="form" [formGroup]="validations_form"  (ngSubmit)="tryRegister(validations_form.value)">
 
    <ion-item>
      <ion-label  position="floating" color="primary">Email</ion-label>
      <ion-input type="text" formControlName="email"></ion-input>
    </ion-item>
    <div class="validation-errors">
      <ng-container *ngFor="let validation of validation_messages.email">
        <div class="error-message" *ngIf="validations_form.get('email').hasError(validation.type) && (validations_form.get('email').dirty || validations_form.get('email').touched)">
          {{ validation.message }}
        </div>
      </ng-container>
    </div>
 
    <ion-item>
      <ion-label  position="floating" color="primary">Password</ion-label>
      <ion-input type="password" formControlName="password" class="form-controll" required></ion-input>
    </ion-item>
    <div class="validation-errors">
      <ng-container *ngFor="let validation of validation_messages.password">
        <div class="error-message" *ngIf="validations_form.get('password').hasError(validation.type) && (validations_form.get('password').dirty || validations_form.get('password').touched)">
          {{ validation.message }}
        </div>
      </ng-container>
    </div>

    <ion-item>
      <ion-label  position="floating" color="primary">Name</ion-label>
      <ion-input type="text" formControlName="name" class="form-controll" required></ion-input>
    </ion-item>
    <div class="validation-errors">
      <ng-container *ngFor="let validation of validation_messages.name">
        <div class="error-message" *ngIf="validations_form.get('name').hasError(validation.type) && (validations_form.get('name').dirty || validations_form.get('name').touched)">
          {{ validation.message }}
        </div>
      </ng-container>
    </div>


    <ion-item><ion-label  position="floating" color="primary">Age</ion-label>
      <ion-input type="tel" formControlName="age" class="form-controll" required></ion-input>
    </ion-item>
    <div class="validation-errors">
      <ng-container *ngFor="let validation of validation_messages.age">
        <div class="error-message" *ngIf="validations_form.get('age').hasError(validation.type) && (validations_form.get('age').dirty || validations_form.get('age').touched)">
          {{ validation.message }}
        </div>
      </ng-container>
    </div>
    <ion-item><ion-label  position="floating" color="primary">Mobile Number</ion-label>
      <ion-input type="tel" formControlName="mobilenumber" class="form-controll" required></ion-input>
    </ion-item>
    <div class="validation-errors">
      <ng-container *ngFor="let validation of validation_messages.mobilenumber">
        <div class="error-message" *ngIf="validations_form.get('mobilenumber').hasError(validation.type) && (validations_form.get('mobilenumber').dirty || validations_form.get('mobilenumber').touched)">
          {{ validation.message }}
        </div>
      </ng-container>
    </div>
    <ion-item><ion-label  position="floating" color="primary">Address</ion-label>
      <ion-input type="text" formControlName="address" class="form-controll" required></ion-input>
    </ion-item>
    <div class="validation-errors">
      <ng-container *ngFor="let validation of validation_messages.address">
        <div class="error-message" *ngIf="validations_form.get('address').hasError(validation.type) && (validations_form.get('address').dirty || validations_form.get('address').touched)">
          {{ validation.message }}
        </div>
      </ng-container>
    </div>
    
 
    
    <ion-button  class="submit-btn" type="submit"  [disabled]="!validations_form.valid">Register</ion-button>
    <label class="error-message">{{errorMessage}}</label>
    <label class="success-message">{{successMessage}}</label>
  </form>
  <p class="go-to-login">Already have an account? <a (click)="goLoginPage()">Try to Log In.</a></p>
</ion-content>

My register.page.ts code


import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
import { AuthenticationService } from './../authentication.service';
import { NavController, MenuController } from '@ionic/angular';
import { ServiceService } from '../service.service';

 
@Component({
  selector: 'app-register',
  templateUrl: './register.page.html',
  styleUrls: ['./register.page.scss'],
})
export class RegisterPage implements OnInit {
 
 
  validations_form: FormGroup;
  errorMessage: string = '';
  successMessage: string = '';
  users: any;
  userName: string;
  userAge: any;
  userAddress: string;
  usermobileNumber: any;
  userEmail : any;
  data : any = {};

  validation_messages = {
   'email': [
     { type: 'required', message: 'Email is required.' },
     { type: 'pattern', message: 'Enter a valid email.' }
   ],
   'password': [
     { type: 'required', message: 'Password is required.' },
     { type: 'minlength', message: 'Password must be at least 5 characters long.' }
   ],
   'age': [
     { type: 'required', message: 'Age is required.' },
     { type: 'minlength', message: 'Age must be at least 1 characters long.' }
   ],
   'mobilenumber': [
     { type: 'required', message: 'Mobile Number is required.' },
     { type: 'minlength', message: 'Mobile Number must be at least 10 characters long.' }
   ],
   'address': [
     { type: 'required', message: 'Address is required.' },
    
   ],
   'name': [
    { type: 'required', message: 'Name is required.' },

  ]
   
 };
  
 
  constructor(
    private navCtrl: NavController,
    private authService: AuthenticationService,
    private formBuilder: FormBuilder,
    public menuCtrl: MenuController,
    public service: ServiceService
  ) {
   // this.menuCtrl.enable(false);

  }
  


   
 
  ngOnInit(){
    this.validations_form = this.formBuilder.group({
      email: new FormControl('', Validators.compose([
        Validators.required,
        Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
      ])),
      password: new FormControl('', Validators.compose([
        Validators.minLength(5),
        Validators.required
      ])),
      
      name: new FormControl('', Validators.compose([
       
        Validators.required
      ])),
      mobilenumber: new FormControl ('', Validators.compose([
        Validators.required,
        Validators.minLength(10),
        Validators.maxLength(10)
       

      ])),
      age: new FormControl ('', Validators.compose([
        Validators.required,
        Validators.minLength(1)

      ]))
      ,
      address: new FormControl ('', Validators.compose([
        Validators.required,
       
      ]))
    });
  }

    
    
    
 
  tryRegister(value){
   this.data = {};
    this.data['Name'] = this.validations_form.value.name;
    this.data['Age'] = this.validations_form.value.age;
    this.data['Address'] = this.validations_form.value.address;
    this.data['MobileNumber'] = this.validations_form.value.mobilenumber;
    this.data['Email'] = this.validations_form.value.email;
    this.menuCtrl.enable(true);
    this.authService.registerUser(value,this.data)
     .then(res => {
       console.log(res);
       this.errorMessage = "";
       this.successMessage = "Your account has been created. Please log in.";
       
     }, err => {
       console.log(err);
       this.errorMessage = err.message;
       this.successMessage = "";
     })
    
  }
 
  goLoginPage(){
    this.navCtrl.navigateBack('');
  }
 
 
}

I am also running into this problem, did you ever find a reason/solution for it?

My versions:
Ionic 5.0.0
Angular 9.1.6

ultimately I had to create a new application and copy my code to that it that’s how I proceeded( it’s not a solution but I had no time left for submission)
can I ask u to share ur code? I might be able to help on that.

Yeah it should be pretty simple and I have used code like this before without any issues.

Here is the html

<ion-content>
	<div class="flex-content-wrap flex-center">
		<ion-card class="login-card">
			<ion-card-content>
				<form [formGroup]="loginForm" (keyup.enter)="login()">
			    <ion-grid>
			      <ion-row color="primary" class="ion-justify-content-center">
			        <ion-col class="ion-align-self-center" size-md="6" size-lg="5" size-xs="12">
			          <div class="ion-text-center">
			            <h2>Login</h2>
			          </div>
			          <div class="ion-padding">
			            <ion-item>
			              <ion-input name="email" type="email" placeholder="your@email.com" formControlName="email" required>
			              </ion-input>
			            </ion-item>
			            <ion-item>
			              <ion-input name="password" type="password" placeholder="Password" formControlName="password" required>
			              </ion-input>
			            </ion-item>
			          </div>
			          <div class="ion-padding">
			            <ion-button type="submit" [disabled]="loginForm.invalid" expand="block" (click)="login()">Login</ion-button>

			            <div class="flex-row forgot-register">
			            	<a (click)="forgotPassword()">Forgot Password</a>
			            	<div class="divider"></div>
			            	<a (click)="goToRegister()">Create Account</a>
			            </div>
			          </div>
			        </ion-col>
			      </ion-row>
			    </ion-grid>
			  </form>
			</ion-card-content>
		</ion-card>
	</div>
</ion-content>

And here is the ts file

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';

import { 
	AlertController,
	LoadingController,
	ModalController } from '@ionic/angular';

import { ForgotPasswordComponent } from './forgot-password/forgot-password.component';

import { AuthService } from '@app/_core/services/auth/auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.page.html',
  styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
	loginForm: FormGroup;

  constructor(private auth: AuthService,
  						private formBuilder: FormBuilder,
  						private alertCtrl: AlertController,
  						private loadingCtrl: LoadingController,
  						private modalCtrl: ModalController,
  						private router: Router) {
  	this.loginForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required]
    });
  }

  ngOnInit() {
  	this.auth.user.subscribe(user => {
      if (user) {
        this.router.navigate(['/']);
      }
    });
  }

  async login() {
    if (this.loginForm.valid) {
      const loader = await this.loadingCtrl.create({
        cssClass: 'no-background'
      });
      await loader.present();

      try {
        await this.auth.signIn(this.loginForm.value);

        this.loginForm.patchValue({email: '', password: ''});
      } catch (err) {
        if (err.message) {
          const alert = await this.alertCtrl.create({
            header: 'Login Error',
            message: err.message,
            buttons: ['OK']
          });

          await alert.present();
        }
      }

      this.loadingCtrl.dismiss();
    }
  }

  goToRegister() {
    this.router.navigate(['register']);
  }

  async forgotPassword() {
  	const modal = await this.modalCtrl.create({
      component: ForgotPasswordComponent
    });

    return await modal.present();
  }

}

If anyone else has run into this error, I still have not been able to solve it. I have tried to to watch statusChanges on the form, use the change detector and use a variable to set the disabled attribute without any success, none of it fires on first load, but it will start to fire if I open the developer console in browser without a reload.

this.loginForm.statusChanges.pipe(takeUntil(this._onDestroy)).subscribe(observer => {
    console.log(observer);
    this.valid = observer == 'VALID' ? true : false;
    this.ref.markForCheck();
});

I’ve had to just remove the disabled attribute on the button and check if the form is invalid during the submit function. While I don’t like this solution it does work this way.

Hi @tljesse i have the same exact issue on first login form with a disabled button. Change detection is not triggered in few cases and it is not easy to replicate it. Did you find any solution?

Then does it work? I never seen this issue

This might be not a proper solution but can give a try.

try to paste the below code inside the constructor instead of ngOnInit()

this.validations_form = this.formBuilder.group({
email: new FormControl(’’, Validators.compose([
Validators.required,
Validators.pattern(’^[a-zA-Z0-9_.±]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$’)
])),
password: new FormControl(’’, Validators.compose([
Validators.minLength(5),
Validators.required
])),

  name: new FormControl('', Validators.compose([
   
    Validators.required
  ])),
  mobilenumber: new FormControl ('', Validators.compose([
    Validators.required,
    Validators.minLength(10),
    Validators.maxLength(10)
   

  ])),
  age: new FormControl ('', Validators.compose([
    Validators.required,
    Validators.minLength(1)

  ]))
  ,
  address: new FormControl ('', Validators.compose([
    Validators.required,
   
  ]))
});

Actually, I found how to replicate the issue. It is related to change detection with lazy loading routes only on a production build (only on capacitor and ios). Now I rewrite routing with direct component loading and everything seems to work now.