tablas pasar parametros otro formularios entre datos comunicacion componentes componente javascript html angular typescript angular5

javascript - pasar - Manejar errores de forma usando componentes Angular 5-TypeScript



pasar parametros entre componentes angular 4 (9)

Aquí hay una parte del código que usé en la biblioteca para generar formularios dinámicos.

Esto es FormError.ts que se usa para obtener mensajes de error y personalizados si queremos.

import { AbstractControl } from "@angular/forms"; type ErrorFunction = (errorName: string, error: object) => string; export type ErrorGetter = string | { [key2: string]: string } | ErrorFunction; export class FormError { constructor(private errorGetter?: ErrorGetter) { } hasError(abstractControl: AbstractControl) { return abstractControl.errors && (abstractControl.dirty || abstractControl.touched); } getErrorMsgs(abstractControl: AbstractControl): string[] { if (!this.hasError(abstractControl)) return null; let errors = abstractControl.errors; return Object.keys(errors).map(anyError => this.getErrorValue(anyError, errors[anyError])); } getErrorValue(errorName: string, error: object): string { let errorGetter = this.errorGetter; if (!errorGetter) return predictError(errorName, error); if (isString(errorGetter)) return errorGetter; else if (isErrorFunction(errorGetter)) { let errorString = errorGetter(errorName, error); return this.predictedErrorIfEmpty(errorString, errorName, error) } else { let errorString = this.errorGetter[errorName]; return this.predictedErrorIfEmpty(errorString, errorName, error) } } predictedErrorIfEmpty(errorString: string, errorName: string, error: object) { if (errorString == null || errorString == undefined) return predictError(errorName, error); return errorString; } } function predictError(errorName: string, error: object): string { if (errorName === ''required'') return ''Cannot be blank''; if (errorName === ''min'') return `Should not be less than ${error[''min'']}`; if (errorName === ''max'') return `Should not be more than ${error[''max'']}`; if (errorName === ''minlength'') return `Alteast ${error[''requiredLength'']} characters`; if (errorName === ''maxlength'') return `Atmost ${error[''requiredLength'']} characters`; // console.warn(`Error for ${errorName} not found. Error object = ${error}`); return ''Error''; } export function isString(s: any): s is string { return typeof s === ''string'' || s instanceof String; } export function isErrorFunction(f: any): f is ErrorFunction { return typeof f === "function"; }

Mensajes personalizados

class FormError { constructor(private errorGetter?: ErrorGetter) { } }

Ahora ErrorGetter es como

type ErrorFunction = (errorName: string, error: object) => string; type ErrorGetter = string | { [key2: string]: string } | ErrorFunction;

  1. Si queremos un error constante para cualquier error, entonces debería ser como

    new FormError(''Password is not right'')

  2. Si queremos un error constante para un error específico, entonces debería ser como

    new FormError({required:''Address is necessary.''})

    Para otros errores irá en predicción de error.

  3. Si queremos usar la función para un error específico, debería ser como

    new FormError((errorName,errorObject)=>{ if(errorName==''a'') return ''2'';})

    Para otros errores irá en predicción de error.

  4. Modifique la función predictError de acuerdo a su necesidad.

Componente FormError

form-error.html

<ng-container *ngIf="formError.hasError(control)"> <div class=''form-error-message'' *ngFor=''let error of formError.getErrorMsgs(control)''>{{error}}</div> </ng-container>

form-error.scss

form-error { .form-error-message { color: red; font-size: .75em; padding-left: 16px; } }

form-error.ts

@Component({ selector: ''form-error'', templateUrl: ''form-error.html'' }) export class FormErrorComponent { @Input() formError: FromError; @Input() control: AbstractControl; }

Uso

<form-error [control]=''thatControl'' ></form-error>

Obviamente, FormError no es el mejor diseño. Modifica como quieras.

Actualmente estoy trabajando en un formulario en Angular 5 / Typescript de varios campos (más de 10 campos), y quería manejar los errores de manera más adecuada sin duplicar el código en mi página html.

Aquí hay un ejemplo de un formulario:

<form [formGroup]="myForm"> <label>Name</label> <input type="text" formControlName="name"> <p class="error_message" *ngIf="myForm.get(''name'').invalid && (myForm.submitted || myForm.get(''name'').dirty)">Please provide name</p> <label>Lastname</label> <input type="text" formControlName="lastname"> <p class="error_message" *ngIf="myForm.get(''lastname'').invalid && (myForm.submitted || myForm.get(''lastname'').dirty)">Please provide email</p> <label>Email</label> <input type="text" formControlName="email"> <p class="error_message" *ngIf="myForm.get(''email'').hasError(''required'') && (myForm.submitted || myForm.get(''email'').dirty)">Please provide email</p> <p class="error_message" *ngIf="myForm.get(''email'').hasError(''email'') && (myForm.submitted || myForm.get(''email'').dirty)">Please provide valid email</p> </form>

En mi caso, tengo dos tipos de validación para mi formulario:

  • Validación de HTML: requerido, maxSize, ... etc.
  • Validación posterior: por ejemplo, cuenta no válida, tamaño del archivo cargado, ... etc.

Trato de usar una directiva como se menciona aquí

<form [formGroup]="myForm"> <label>Name</label> <input type="text" formControlName="name"> <div invalidmessage="name"> <p *invalidType="''required''">Please provide name</p> </div> <label>Lastname</label> <input type="text" formControlName="lastname"> <div invalidmessage="lastname"> <p *invalidType="''required''">Please provide lastname</p> </div> <label>Email</label> <input type="text" formControlName="email"> <div invalidmessage="email"> <p *invalidType="''required''">Please provide email</p> <p *invalidType="''email''">Please provide valid email</p> </div> </form>

Pero incluso con esta solución, el código siempre está duplicado y no hay capacidad para manejar ambos tipos de validación.

¿Tienes otro enfoque? ¿Los componentes de uso son apropiados en este caso? Si es así, ¿cómo puede hacerlo?

Gracias de antemano por su inversión.


La mejor manera es implementar ControlValueAccessor s personalizado para cada tipo de entrada, combinando <label> , <input> y algunas etiquetas para mostrar un mensaje de error (en mi proyecto simplemente uso el atributo de title para este propósito) en un solo componente.

Todos los que acceden al valor deben implementar la misma interfaz o extender la clase abstracta base, proporcionando métodos para establecer y borrar mensajes de error y cualquier otro método al que desee llamar desde directivas de validación.

Además, deberá implementar directivas de validación personalizadas para cada tipo de validación (tuve que volver a implementar incluso el required y maxlength ), los validadores deben devolver los objetos de error de manera uniforme, es decir, para el validador de {email: "Invalid email address"} . Las directivas del validador pueden obtener referencia a sus @Inject(NG_VALUE_ACCESSOR) controls:AbstractFormComponent<any>[] valores de control mediante inyección: @Inject(NG_VALUE_ACCESSOR) controls:AbstractFormComponent<any>[] (generalmente en matriz con un elemento, AbstractFormComponent es su clase base para los accesores), use esta referencia para establecer o borrar el acceso mensaje de error.

También puede implementar dos tipos adicionales de directivas de validación: sync y async, que pueden recibir la función de validación a través de @Input ie [async]="loginValidatorFn" , donde loginValidatorFn se define en la clase de componente y devuelve Observable<ValidationErrors> .

Este es un código real de nuestra aplicación:

<div class="input" [caption]="''SSN: ''" name="ssn" type="text" [(ngModel)]="item.ssn" [async]="memberSsnValidatorFn" required></div>


Para hacer que el código de la plantilla sea claro y evitar el código duplicado de los mensajes de validación, debemos cambiarlos para que sean más reutilizables. Aquí, crear una directiva personalizada que agregue y elimine el bloque de código de mensaje de validación es una opción (que se muestra en la siguiente demostración ).

Mostrar / Ocultar mensajes de validación

En la directiva, podemos acceder al control de formulario de host de la directiva y agregar / eliminar el mensaje de validación basado en el estado de validación de la misma mediante la suscripción a su evento valueChanges .

@Directive(...) export class ValidatorMessageDirective implements OnInit { constructor( private container: ControlContainer, private elem: ElementRef, // host dom element private control: NgControl // host form control ) { } ngOnInit() { const control = this.control.control; control.valueChanges.pipe(distinctUntilChanged()).subscribe(() => { this.option.forEach(validate => { if (control.hasError(validate.type)) { const validateMessageElem = document.getElementById(validate.id); if (!validateMessageElem) { const divElem = document.createElement(''div''); divElem.innerHTML = validate.message; divElem.id = validate.id; this.elem.nativeElement.parentNode.insertBefore(divElem, this.elem.nativeElement.nextSibling); } } else { const validateMessageElem = document.getElementById(validate.id); if (validateMessageElem) { this.elem.nativeElement.parentNode.removeChild(validateMessageElem); } } }) }); } }

Validar opciones

La directiva agrega y elimina el mensaje de validación basado en los errores de validación correspondientes. Entonces, el último paso que deberíamos hacer es decirle a la directiva qué tipos de errores de validación se deben ver y qué mensajes se deben mostrar, ese es el campo @Input por el cual transportamos las opciones de validación a la directiva.

Entonces simplemente podemos escribir el código de la plantilla de la siguiente manera:

<form [formGroup]="form"> <input type="text" formControlName="test" [validate-message]="testValidateOption"><br/> <input type="number" formControlName="test2" [validate-message]="test2ValidateOption"> </form>

Consulte la demo trabajo.


Para la validación de html escribiría un control de formulario personalizado que básicamente será una envoltura alrededor de una entrada. También escribiría validadores personalizados que devuelven un mensaje de error (los validadores integrados devuelven un objeto, creo). Dentro de tu control de formulario personalizado puedes hacer algo como esto:

<div *ngIf="this.formControl.errors"> <p>this.formControl.errors?.message</p> </div>

Para el validador de back-end puede escribir un validador asíncrono .


Podría crear un componente personalizado ValidationMessagesComponent :

Modelo :

<p class="error_message" *ngIf="form.get(controlName).hasError(''required'') && (form.submitted || form.get(controlName).dirty)">Please provide {{controlName}}</p> <p class="error_message" *ngIf="form.get(controlName).hasError(''email'') && (form.submitted || form.get(controlName).dirty)">Please provide valid {{controlName}}</p> ...other errors

Y con las entradas:

@Input() controlName; @Input() form;

Entonces úsalo así:

<validation-messages [form]="myForm" controlName="email"></validation-messages>


Puede mover los errores de validación a un componente y pasar formControl.errors como una propiedad de entrada. De esta manera todos los mensajes de validación pueden ser reutilizados. Aquí hay un ejemplo en StackBlitz . El código utiliza material angular, pero aún así debería ser útil incluso si no lo está.

validation-errors.component.ts

import { Component, OnInit, Input, ChangeDetectionStrategy } from ''@angular/core''; import { FormGroup, ValidationErrors } from ''@angular/forms''; @Component({ selector: ''validation-errors'', templateUrl: ''./validation-errors.component.html'', styleUrls: [''./validation-errors.component.css''], changeDetection: ChangeDetectionStrategy.OnPush }) export class ValidationErrorsComponent implements OnInit { @Input() errors: ValidationErrors; constructor() {} ngOnInit() {} }

validation-errors.component.html

<ng-container *ngIf="errors && errors[''required'']"> Required</ng-container> <ng-container *ngIf="errors && errors[''notUnique'']">Already exists</ng-container> <ng-container *ngIf="errors && errors[''email'']">Please enter a valid email</ng-container>

Para los mensajes de validación posteriores, establezca el error manualmente en el control de formulario.

const nameControl = this.userForm.get(''name''); nameControl.setErrors({ "notUnique": true });

Para utilizar el componente de validación en el formulario:

<form [formGroup]="userForm" (ngSubmit)="submit()"> <mat-form-field> <input matInput placeholder="name" formControlName="name" required> <mat-error *ngIf="userForm.get(''name'').status === ''INVALID''"> <validation-errors [errors]="userForm.get(''name'').errors"></validation-errors> </mat-error> </mat-form-field> <mat-form-field> <input matInput placeholder="email" formControlName="email" required> <mat-error *ngIf="userForm.get(''email'').status === ''INVALID''"> <validation-errors [errors]="userForm.get(''email'').errors"></validation-errors> </mat-error> </mat-form-field> <button mat-raised-button class="mat-raised-button" color="accent">SUBMIT</button> </form>


Puede usar este repo que tiene mensajes de validación predeterminados y también puede personalizarlos.

ejemplo de uso será así

<form [formGroup]="editorForm" novalidate> <label>First Name</label> <input formControlName="firstName" type="text"> <ng2-mdf-validation-message [control]="firstName" *ngIf="!firstName.pristine"></ng2-mdf-validation-message> </form>


Tenía el mismo requisito, a nadie le gusta volver a escribir el mismo código dos veces.

Esto se puede hacer creando controles de formulario personalizados. La idea es que cree sus controles de formulario personalizados, tenga un servicio común que genere un objeto formControl personalizado e inyecte los validadores adecuados según el tipo de datos proporcionado en el objeto FormControl.

¿De dónde viene el tipo de datos?

Tenga un archivo en sus activos o en cualquier lugar que contenga tipos como este:

[{ "nameType" : { maxLength : 5 , minLength : 1 , pattern : xxxxxx, etc etc } } ]

Esto puede leerlo en su ValidatorService y seleccionar el DataType apropiado con el que puede crear sus Validators y regresar a su Control de formulario personalizado.

Por ejemplo ,

<ui-text name="name" datatype="nameType" [(ngModel)]="data.name"></ui-text>

Esta es una breve descripción de esto en un alto nivel de lo que hice para lograrlo. Si necesita información adicional con esto, comente. Estoy fuera, por lo que no puedo proporcionarle el código base en este momento, pero mañana podría actualizar la respuesta.

ACTUALIZACIÓN para la parte que muestra el error

Puede hacer 2 cosas por él, vincular el validador de formControl con un div dentro del control y alternarlo con *ngIf="formControl.hasError(''required )" `, etc.

Para que un Mensaje / Error se muestre en otro lugar genérico, como un Tablero de mensajes, es mejor colocar el marcador del Tablero de mensajes en algún lugar del Componente principal que no se elimine durante el enrutamiento (discutible en función del requisito) y hacer que el componente escuche un evento MessageEmit el cual su ErrorStateMatcher de su formControl se activará cuando sea necesario (según el requisito).

Este es el diseño que utilizamos y funcionó bastante bien, puede hacer mucho con estos controles de forma una vez que comience a personalizarlos.


Demo

Puede inyectar NgForm y acceder a la directiva FormControlName través de @ContentChild dentro de un componente de validación personalizado para lograr la reutilización:

@Component({ selector: ''[validator]'', template: ` <ng-content></ng-content> <div *ngIf="formControl.invalid"> <div *ngIf="formControl.errors.required && (form.submitted || formControl.dirty)"> Please provide {{ formControl.name }} </div> <div *ngIf="formControl.errors.email && (form.submitted || formControl.dirty)"> Please provide a valid email </div> <div *ngIf="formControl.errors.notstring && (form.submitted || formControl.dirty)"> Invalid name </div> </div> `}) export class ValidatorComponent implements OnInit { @ContentChild(FormControlName) formControl; constructor(private form: NgForm) { } ngOnInit() { } }

Para usarlo, envolvería todos sus controles de formulario (que tienen un nombre de control de formulario) con un elemento HTML y agregaría un atributo de validador:

<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate> <div [formGroup]="myForm"> <label>Name</label> <div validator> <input type="text" formControlName="name"> </div> <label>Lastname</label> <div validator> <input type="text" formControlName="lastname"> </div> <label>Email</label> <div validator> <input type="text" formControlName="email"> </div> </div> <button type="submit">Submit</button> </form>

Esto funcionará para validadores síncronos y asíncronos.