Helpex - Trao đổi & giúp đỡ Đăng nhập
4

Gặp sự cố khi trình xác thực tùy chỉnh Angular 2 hoạt động bằng cách sử dụng phương pháp từ sách nấu ăn.

Mục tiêu chính của tôi là học Angular (có nghĩa là, tôi luôn là một noob).

Tôi đang cố gắng có trường nhập văn bản chỉ cho phép các số nguyên trong một phạm vi nhất định (1-26). Tôi nghĩ rằng tôi sẽ thông minh và viết một trình xác thực có danh sách các số và phạm vi tùy ý (ví dụ: "1,3,5,7,11-19,100-200") và kiểm tra xem giá trị đã cho là một trong số các giá trị cho phép.

Vấn đề của tôi là trình xác thực tùy chỉnh (hàm ẩn danh được trả về bởi allowedNumericValuesValidator?) Không bao giờ được gọi. (Nhưng lưu ý rằng requiredtrình xác thực được xây dựng chạy rất tốt.) Đối với vấn đề đó, cũng không phải bất kỳ phương thức nào được định nghĩa AllowedNumericValuesDirectivekhi được gọi. Bản thân mã nguồn của trình xác thực được tải, nhưng đó là điều mà mọi thứ diễn ra.

Sử dụng Angular 2.2.3, angle-cli 1.0.0-beta.22-1. Trình duyệt là Chrome 55.0.2883.95 (64-bit)

Nguồn tại https://github.com/JohnL4/Diaspora , nhưng tôi sẽ cố gắng đưa các phần có liên quan vào đây.

Đây là những gì tôi đã làm:

Trình xác thực của tôi trông như thế này:

import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } from '@angular/forms';

const SELECTOR: string = 'allowedNumericValues'; // <---------------- breakpoint here

class Range
{
   constructor ( public Low: number, public High: number) {}
}

export function allowedNumericValuesValidator( anAllowedValuesSpec: string): ValidatorFn
{
   let errors: string[] = [];   // (<---- breakpoint here) Errors from parsing allowed values specification
   let ranges : Range[] = [];   // Allowed ranges, used in validation.
   let rangeSpecs = anAllowedValuesSpec.split( /\s*,\s*/);
   for (let r of rangeSpecs)
   {
      let ends : string[] = r.split( /\s*-\s*/);
      if (ends.length == 1)
      {
         let end : number = Number(ends[0]);
         if (isNaN( end))
            errors.push( r + " is NaN");
         else
            ranges.push( new Range( end, end));
      }
      else if (ends.length == 2)
      {
         let low:number = Number(ends[0]);
         let high:number = Number(ends[1]);
         if (isNaN( low) || isNaN( high))
            errors.push( r + " has NaN");
         else
            ranges.push( new Range( low, high));
      }
      else
         errors.push( r + " has bad syntax");
   }
   if (errors.length > 0)
      throw new Error( errors.join( "; "));

   return (control: AbstractControl): {[key: string]: any} => {
      const numberToBeValidated = control.value; // <---------------- breakpoint here
      const num = Number( numberToBeValidated);
      if (isNaN( num))
         return {SELECTOR: {numberToBeValidated}};
      let isGood: boolean = false;
      for (let r of ranges)
      {
         if (r.Low <= num && num <= r.High)
         {
            isGood = true;
            break;
         }
      }
      return isGood ? null : {SELECTOR: {numberToBeValidated}};
   };
}

@Directive({
   selector: '[' + SELECTOR + ']', // Note: not using extra '[ngForm]' or '[ngModel]' here because the cookbook example doesn't.
   providers: [{provide: NG_VALIDATORS, useExisting: AllowedNumericValuesDirective, multi: true}]
})
export class AllowedNumericValuesDirective implements Validator, OnChanges
{
   @Input() allowedNumericValues: string;
   private valFn = Validators.nullValidator; // <---------------- breakpoint here

   ngOnChanges( changes: SimpleChanges): void
   {
      const change = changes[ SELECTOR];
      if (change)
      {
         const val: string = change.currentValue;
         this.valFn = allowedNumericValuesValidator( val);
      }
      else
         this.valFn = Validators.nullValidator;
   }

   validate( control: AbstractControl): {[key: string]: any}
   {
      return this.valFn( control);
   }
}

Nếu tôi đặt một breakpoint vào const SELECTORbài tập, nó sẽ bị tấn công (callstack là khoảng nửa tá __webpack_require__lệnh gọi), nhưng không có gì được gọi sau đó (không có điểm dừng nào khác bị đánh, cũng không có console.log()câu lệnh nào tôi đưa vào được gọi.

Của tôi shared.module.ts, trong cùng sharedthư mục với trình xác thực, trông giống như sau:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedComponent } from './shared.component'; // angular-cli stuck this in here; I'm not sure I need it.
import { AllowedNumericValuesDirective } from './allowed-numeric-values.directive';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: [SharedComponent, AllowedNumericValuesDirective]
})
export class SharedModule { }

Của tôi app.module.tstrông như thế này (tôi có 4 thành phần, nhưng tôi chỉ quan tâm đến "params" một và ba thành phần còn lại đang hoạt động tốt):

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule }   from '@angular/router';

import { SharedModule } from './shared/shared.module'; 
import { AppComponent } from './app.component';
import { ClusterDetailsComponent } from './cluster-details/cluster-details.component';
import { DotComponent } from './dot/dot.component';
import { GeneratorParamsComponent } from './generator-params/generator-params.component';
import { TabsComponent } from './tabs/tabs.component';
import { XmlComponent } from './xml/xml.component';

@NgModule({
   declarations: [
      AppComponent,
      ClusterDetailsComponent,
      DotComponent,
      GeneratorParamsComponent,
      TabsComponent,
      XmlComponent
   ],
   imports: [
      BrowserModule,
      FormsModule,
      HttpModule,
      SharedModule, // I don't need to put the validator in the `declarations` property above, do I?
      RouterModule.forRoot([
         {
            path: '',           // Initial load.
            redirectTo: '/params',
            pathMatch: 'full'
         },
         {
            path: 'params',
            component: GeneratorParamsComponent
         },
         {
            path: 'details',
            component: ClusterDetailsComponent
         },
         {
            path: 'xml',
            component: XmlComponent
         },
         {
            path: 'dot',
            component: DotComponent
         }
      ])
   ],
   providers: [],
   bootstrap: [AppComponent]
})
export class AppModule { }

generator-params.component.html trông như thế này:

<p></p>

<form #parmsForm="ngForm" class="form-horizontal">  <!-- "form-horizontal" is Bootstrap class -->
  <div class="form-group">                           <!-- "form-group" is Bootstrap class -->
    <label for="numSystems" class="col-sm-3 control-label"> <!-- "control-label" is the class for labels in HORIZONTAL forms. -->
      Number of systems in cluster
    </label>
    <div class="col-sm-2">
      <input id="numSystems" name="numSystems" type="text" class="form-control"
             required maxlength="2" allowedNumericValues="1-26"
             [(ngModel)]="numSystems">
    </div>
    <div *ngIf="formErrors.numSystems" class="col-sm-6 alert alert-danger">
      {{ formErrors.numSystems }}
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-offset-3 col-sm-9">
      <div class="checkbox">    <!-- "checkbox" is Bootstrap class -->
        <label for="slipstreamsHighLow">
          <input id="slipstreamsHighLow" name="slipstreamsHighLow" type="checkbox" />
          Slipstreams Differentiated Between High & Low Slipknots
        </label>
      </div>
  </div></div>
  <div class="form-group">
    <div class="col-sm-offset-3 col-sm-9">
    <button id="goBtn" (click)="generateCluster()" class="btn btn-default btn-warning"
            title="Obviously, this will hammer your existing cluster. Be sure you have it backed up or otherwise saved, or that you don't care."
            >
      Go!
    </button>
    <button id="revertBtn" class="btn btn-default" (click)="revertParams()">Revert</button>
    </div>
  </div>
</form>

Và cuối cùng, generator-params.component.tstrông như thế này:

import { Component, OnInit, ViewChild } from '@angular/core';
import { FormsModule, NgForm } from '@angular/forms';

import { Cluster } from '../cluster';

@Component({
  selector: 'app-generator-params',
  templateUrl: './generator-params.component.html',
  styleUrls: ['./generator-params.component.css']
})
export class GeneratorParamsComponent implements OnInit {

   private numSystems: string; // = "6";
//   get numSystems() : string { return this._numSystems; }
//   set numSystems( value: string) { this._numSystems = value; }

   parmsForm: NgForm;
   @ViewChild( 'parmsForm') currentForm: NgForm;

   formErrors = {
      'numSystems': ''
   };

   validationMessages = {
      'numSystems': {
         'required': "A number of systems is required",
         'allowedNumericValues': "Value must be one of the allowed numeric values"
      }
   };

   private _cluster: Cluster;

   constructor(aCluster: Cluster)
   {
      this._cluster = aCluster;
      if (aCluster && aCluster.numSystems)
         this.numSystems = aCluster.numSystems.toString();
      // this.strSystems = this.numSystems.toString();
      // this.numSystems = "6"; // aCluster.numSystems.toString();
   }

   ngOnInit()
   {
   }

   /** See form validation cookbook "recipe"
    */
   ngAfterViewChecked()
   {
      this.formChanged();
   }

   public generateCluster()
   {
      // this._cluster = new Cluster( this.numSystems); // Don't new up, just update in place?
      this._cluster.numSystems = Number( this.numSystems);
      // this.cluster.generateSystems();
   }

   public revertParams()
   {
      this.numSystems = this._cluster.numSystems.toString();
   }

   formChanged()
   {
      if (this.currentForm === this.parmsForm) return;
      this.parmsForm = this.currentForm;
      if (this.parmsForm)
         this.parmsForm.valueChanges.subscribe( data => this.onValueChanged( data));
   }

   onValueChanged( data?: any)
   {
      if (!this.parmsForm) return;
      const form = this.parmsForm.form;

      for (const field in this.formErrors)
      {
         this.formErrors[field] = '';
         const control = form.get( field);
         if (control && control.dirty && !control.valid)
         {
            const messages = this.validationMessages[field];
            for (const key in control.errors)
            {
               this.formErrors[field] += messages[key] + ' ';
            }
         }
      }
   }
}

Tôi nghĩ rằng tôi đã kết nối mọi thứ một cách chính xác theo sách dạy nấu ăn, nhưng rõ ràng là tôi đã bỏ sót một số thứ, vì trình xác thực tùy chỉnh không kích hoạt.

Bất cứ ai có thể cho tôi biết những gì tôi đã bỏ lỡ?

Cảm ơn.

4 hữu ích 1 bình luận 1.8k xem chia sẻ
1

Tất cả các thành phần, đường ống, chỉ thị mà bạn khai báo trong mô-đun của mình mà bạn muốn chia sẻ với các mô-đun khác, bạn cũng cần thêm chúng vào exports

@NgModule({
  declarations: [ MyDirective ],
  exports: [ MyDirective ]
})
export class SharedModule {}
1 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ angularjs validation angular , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading