Как исправить: проверка формы Angular 7 (шаблон SmartAdmin) перестает работать после навигации - PullRequest
3 голосов
/ 01 апреля 2019

Я пытаюсь выполнить проверку формы в приложении Angular 7, использующем шаблон Smart Admin (тема из wrapbootstrap).

Моя проблема заключается в том, что он работает, как и ожидалось, при первом обновлении браузера,или даже когда я перехожу к компоненту, который не включает другую форму.Проблема возникает, когда я перехожу к компоненту, который также включает форму с собственными параметрами проверки.

Кроме того, фактический статус «Действительность» формы все еще работает, как и ожидалось.Он просто не показывает классы начальной загрузки и сообщения в форме.

Я попытался сбросить форму, сбросить все асинхронные / не асинхронные валидаторы и все остальное, о чем я мог подумать.

Наконец, нет ошибок или чего-либо еще во время навигации между компонентами.

Это мой основной модуль, который обрабатывает навигацию (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: мне удалось найти обходной путь для этой проблемы, хотя мне это не нравится.Кажется, что когда вызывается пользовательская проверка пользовательского интерфейса, компонент еще не отображается (я предполагаю, что он как-то связан с асинхронной работой).Решением было добавить в компонент проверки 'setTimeout' в 0 мс, как показано ниже:

  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);
      });
  }

Если кто-то может придумать лучшее решение, было бы очень полезно :)

Будем рады услышать ваши идеи.

...