angular - 如何修复 : Angular 7 (SmartAdmin template) form validation stops working after navigation

标签 angular angular7 angular-reactive-forms smartadmin angular2-form-validation

我正在尝试对使用智能管理模板(来自 wrapbootstrap 的主题)的 Angular 7 应用程序执行表单验证。

我的问题是它在浏览器第一次刷新时按预期工作,甚至在我导航到不包含其他表单的组件时也是如此。 当我导航到一个组件时,问题就出现了,该组件还包含一个带有自己的验证选项的表单。

此外,表单的实际“有效性”状态仍然按预期工作。它只是不在表单上显示 Bootstrap 类和消息。

我尝试过重置表单、重置任何异步/非异步验证器以及我能想到的任何其他内容。

最后,在组件之间的导航过程中没有任何错误。

这是我处理导航的主要模块 (main.routing.ts):

import { Routes, RouterModule } from '@angular/router';
import { ModuleWithProviders } from "@angular/core";
import { MainLayoutComponent } from '@app/shared/layout/app-layouts/main-layout.component';

import { MainComponent } from "./main.component";
import { SettingsComponent } from '../settings/settings.component';
import { GetAccountsComponent } from '../administration/get-accounts/get-accounts.component';
import { GetUsersComponent } from '../administration/get-users/get-users.component';
import { CreateAccountComponent } from '../administration/create-account/create-account.component';
import { CreateUserComponent } from '../administration/create-user/create-user.component';

import { GetTeamsComponent } from '../user/get-teams/get-teams.component';
import { GetGamesComponent } from '../user/get-games/get-games.component';
import { CreateTeamComponent } from '../game/create-team/create-team.component';
import { CreateRoundComponent } from '../game/create-round/create-round.component';
import { CreateRoundBetComponent } from '../game/create-round-bet/create-round-bet.component';
import { CreateGameComponent } from '../game/create-game/create-game.component';

export const mainRoutes: Routes = [
  {
    path: '',
    component: MainLayoutComponent,
    children: [
      {
        path: "",
        redirectTo: "dashboard",
        pathMatch: "full"
      },
      {
        path: "dashboard",
        component: MainComponent,
        data: { pageTitle: "Dashboard" }
      },
      {
        path: "settings",
        component: SettingsComponent,
        data: { pageTitle: "Settings" }
      },
      {
        path: "administration/getusers",
        component: GetUsersComponent,
        data: { pageTitle: "Get Users" }
      },
      {
        path: "administration/getaccounts",
        component: GetAccountsComponent,
        data: { pageTitle: "Get Accounts" }
      },
      {
        path: "administration/createaccount",
        component: CreateAccountComponent,
        data: { pageTitle: "Create Account" }
      },
      {
        path: "administration/createuser",
        component: CreateUserComponent,
        data: { pageTitle: "Create User" }
      },
      {
        path: "user/getteams",
        component: GetTeamsComponent,
        data: { pageTitle: "Get Teams" }
      },
      {
        path: "user/getgames",
        component: GetGamesComponent,
        data: { pageTitle: "Get Games" }
      },
      {
        path: "game/createteam",
        component: CreateTeamComponent,
        data: { pageTitle: "Create Team" }
      },
      {
        path: "game/createround",
        component: CreateRoundComponent,
        data: { pageTitle: "Create Round" }
      },
      {
        path: "game/createroundbet",
        component: CreateRoundBetComponent,
        data: { pageTitle: "Create Round Bet" }
      },
      {
        path: "game/creategame",
        component: CreateGameComponent,
        data: { pageTitle: "Create Game" }
      }
    ]
  }
];

export const mainRouting: ModuleWithProviders = RouterModule.forChild(mainRoutes);

这是一个表单示例 (create-team.component.html):

<form id="checkout-form"
        name="createTeamForm"
        class="smart-form"
        [saUiValidate]="validationOptions"
        novalidate="novalidate"
        [formGroup]="createTeamForm"
        (ngSubmit)="onSubmit()">

    <fieldset>
      <div class="row">
        <section class="col col-4">
          <label class="select">
            <select name="firstPerson"
                    formControlName="firstPerson">
              <option value="0"
                      selected=""
                      disabled="">First Person</option>
              <option value="{{user.id}}"
                      *ngFor="let user of users">{{user.email}}</option>
            </select> <i></i> </label>
        </section>
        <section class="col col-4">
          <label class="select">
            <select name="secondPerson"
                    formControlName="secondPerson">
              <option value="0"
                      selected=""
                      disabled="">Second Person</option>
              <option value="{{user.id}}"
                      *ngFor="let user of users">{{user.email}}</option>
            </select> <i></i> </label>
        </section>
      </div>
    </fieldset>

    <footer>
      <button type="submit"
              class="btn btn-primary">
        Create Team
      </button>
    </footer>
  </form>

以及包含验证选项的 .ts 文件:

import { Component, OnInit } from '@angular/core';
import { ApiService } from '@app/core/services';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';

@Component({
  selector: 'app-create-team',
  templateUrl: './create-team.component.html',
  styleUrls: ['./create-team.component.css']
})
export class CreateTeamComponent implements OnInit {
  public teamCreateSuccess: boolean;

  public users: any;
  public hasSubmitted: boolean;

  public errorMessage: string;
  public successMessage: string;

  public validationOptions = {
    rules: {
      firstPerson: {
        required: true
      },
      secondPerson: {
        required: true
      }
    },

    // Messages for form validation
    messages: {
      firstPerson: {
        required: 'Please select the first person'
      },
      secondPerson: {
        required: 'Please select the second person'
      },
    },
    submitHandler: this.onSubmit
  };

  createTeamForm: FormGroup

  constructor(
    private apiService: ApiService
  ) { }

  ngOnInit() {
    console.log('Create Team Loaded');

    this.apiService.userGetUsers()
      .subscribe(
        data => {
          this.users = data;
          // console.log(this.roles);
        },
        error => {
          this.onCreateTeamError(error);
        }
      );

    this.teamCreateSuccess = null;
    this.hasSubmitted = null;

    this.createTeamForm = new FormGroup({
      firstPerson: new FormControl('0', Validators.required),
      secondPerson: new FormControl('0', Validators.required),
    });
  }

  onSubmit() {
    this.hasSubmitted = true;

    if (this.createTeamForm) {
      console.log(this.createTeamForm.status);
      console.log(this.createTeamForm.controls);

      if (this.createTeamForm.status == 'VALID') {
        this.apiService.createTeam(this.createTeamForm).pipe(first())
          .subscribe(
            data => {
              this.onCreateTeamSuccess(data);
            },
            error => {
              //console.log("failed identity check");
              this.onCreateTeamError(error);
              //console.log(error);
            }
          );
      }
    }
  }

  onCreateTeamSuccess(data: any) {
    this.teamCreateSuccess = true;
    this.successMessage = "Successfully created team with Id: " + data.id;
    console.log(data);
  }

  onCreateTeamError(error: any) {
    this.teamCreateSuccess = false;

    this.errorMessage = "Failed to create team due to error: " + error.error;
    console.log(error);
  }
}

编辑:为了提供更多见解,这里是 SmartAdmin 模板使用的自定义验证:

import { Directive, Input, ElementRef } from "@angular/core";

@Directive({
  selector: "[saUiValidate]"
})
export class UiValidateDirective {
  @Input() saUiValidate: any;

  constructor(private el: ElementRef) {
    Promise.all([
      import("jquery-validation/dist/jquery.validate.js"),
      import("jquery-validation/dist/additional-methods.js")
    ])
    .then(() => {
      this.attach();
    });
  }

  attach() {
    const $form = $(this.el.nativeElement);
    const validateCommonOptions = {
      rules: {},
      messages: {},
      errorElement: "em",
      errorClass: "invalid",
      highlight: (element, errorClass, validClass) => {
        $(element)
          .addClass(errorClass)
          .removeClass(validClass);
        $(element)
          .parent()
          .addClass("state-error")
          .removeClass("state-success");
      },
      unhighlight: (element, errorClass, validClass) => {
        $(element)
          .removeClass(errorClass)
          .addClass(validClass);
        $(element)
          .parent()
          .removeClass("state-error")
          .addClass("state-success");
      },

      errorPlacement: (error, element) => {
        if (element.parent(".input-group").length) {
          error.insertAfter(element.parent());
        } else {
          error.insertAfter(element);
        }
      }
    };

    $form
      .find("[data-smart-validate-input], [smart-validate-input]")
      .each(function() {
        var $input = $(this),
          fieldName = $input.attr("name");

        validateCommonOptions.rules[fieldName] = {};

        if ($input.data("required") != undefined) {
          validateCommonOptions.rules[fieldName].required = true;
        }
        if ($input.data("email") != undefined) {
          validateCommonOptions.rules[fieldName].email = true;
        }

        if ($input.data("maxlength") != undefined) {
          validateCommonOptions.rules[fieldName].maxlength = $input.data(
            "maxlength"
          );
        }

        if ($input.data("minlength") != undefined) {
          validateCommonOptions.rules[fieldName].minlength = $input.data(
            "minlength"
          );
        }

        if ($input.data("message")) {
          validateCommonOptions.messages[fieldName] = $input.data("message");
        } else {
          Object.keys($input.data()).forEach(key => {
            if (key.search(/message/) == 0) {
              if (!validateCommonOptions.messages[fieldName])
                validateCommonOptions.messages[fieldName] = {};

              var messageKey = key.toLowerCase().replace(/^message/, "");
              validateCommonOptions.messages[fieldName][
                messageKey
              ] = $input.data(key);
            }
          });
        }
      });

    $form.validate($.extend(validateCommonOptions, this.saUiValidate));
  }
}

编辑 2:我已经设法找到解决此问题的方法,尽管我有点不喜欢它。似乎在调用自定义 UI Validation 时,组件尚未呈现(我猜它与异步运行有关)。 解决方案是在验证组件中添加一个 0ms 的“setTimeout”,如下所示:

  constructor(private el: ElementRef) {
    Promise.all([
      import("jquery-validation/dist/jquery.validate.js"),
      import("jquery-validation/dist/additional-methods.js")
    ])
      .then(() => {
        setTimeout(_ => {
          this.attach();
        }, 0);
      });
  }

如果有人能想到更好的解决方案,将不胜感激:)

期待听到您的想法。

最佳答案

所以,我遇到了和你一样的问题,也尝试了你的方法。虽然它确实有效,my friend, Scott注意到通过将逻辑移动到 ngOnInit() 方法(无需您的修改),它也可以工作。我认为这与 ngOnInit 在调用之前等待组件完全呈现和初始化这一事实有关,这反过来又确保了绑定(bind)可用。

这是我用来让它工作的相关代码(attach() 方法之前的所有代码)。这是在 ui-validate.directive.ts 中:

import { Directive, Input, ElementRef, OnInit } from "@angular/core";

@Directive({
  selector: "[saUiValidate]"
})
export class UiValidateDirective implements OnInit {
  @Input() saUiValidate: any;

  constructor(private el: ElementRef) { }

  ngOnInit(){
    Promise.all([
      import("jquery-validation/dist/jquery.validate.js"),
      import("jquery-validation/dist/additional-methods.js")
    ])
    .then(() => {
      this.attach();
    });
  }

关于angular - 如何修复 : Angular 7 (SmartAdmin template) form validation stops working after navigation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55450097/

相关文章:

angular - 创建抽象类 DomSanitizer 的实例

css - 带有 bootstrap4 的 Angular 7 粘性页脚

angular - 有没有办法将 Angular 数据表与每列 "angular way"的过滤器一起使用?

javascript - 返回 Angular react 形式的所选属性内的所有对象值

Angular SSR - 引用错误 : Element is not defined

javascript - 在 ngFor 中使用过滤器函数有好处吗?

javascript - FormGroup对象的控件和get方法有什么区别?

javascript - Angular2 react 形式: Validate fields inside *ngIf

javascript - Angular 2条件属性不起作用

angular - 无法读取 SafeSubscriber._next 未定义的属性 'push'