angular - navigationend - ¿Cómo enlazar de forma bidireccional mi propio RxJS Subject a un[(ngModel)]?
title angular 6 (2)
¿Existe una forma breve y sencilla de pasar un Subject
RxJS o un Subject
BehaviorSubject
a una directiva Angular 2 para un enlace de dos vías? El largo camino para hacerlo sería el siguiente:
@Component({
template: `
<input type="text" [ngModel]="subject | async" (ngModelChange)="subject.next($event)" />
`
})
Me gustaría poder hacer algo como esto:
@Component({
template: `
<input type="text" [(ngModel)]="subject" />
`
})
Creo que la tubería async
es solo unidireccional, así que no es suficiente. ¿Angular 2 proporciona una forma corta y simple de hacer esto? Angular 2 usa RxJS también, así que esperaba que hubiera alguna compatibilidad inherente.
¿Podría tal vez crear una nueva directiva similar a ngModel
para hacer esto posible?
Comencé a buscar algo como esto para integrar controles de formulario con mi biblioteca ng-app-state . Si usted es del tipo que disfruta hacer un código muy genérico, similar a una biblioteca, siga leyendo. Pero cuidado, esto es largo! Al final, deberías poder usar esto en tus plantillas:
<input [subjectModel]="subject">
He hecho una prueba de concepto para la primera mitad de esta respuesta, y la segunda mitad creo que es correcta, pero tenga en cuenta que no se prueba nada del código real escrito en esta respuesta. Lo siento, pero eso es lo mejor que tengo para ofrecer ahora. :)
Puede escribir su propia directiva llamada subjectModel
para conectar un tema a un componente de formulario. Las siguientes son las partes esenciales, menos las cosas como la limpieza. Se basa en la interfaz ControlValueAccessor
, por lo que Angular incluye los adaptadores necesarios para conectar esto a todos los elementos de formulario HTML estándar, y funcionará con cualquier control de formulario personalizado que se encuentre en la naturaleza, siempre y cuando utilicen ControlValueAccessor
(que es el práctica recomendada).
@Directive({ selector: ''[subjectModel]'' })
export class SubjectModelDirective {
private valueAccesor: ControlValueAccessor;
constructor(
@Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[],
) {
this.valueAccessor = valueAccessors[0]; // <- this can be fancier
}
@Input() set subjectModel(subject: Subject) {
// <-- cleanup here if this was already set before
subject.subscribe((newValue) => {
// <-- skip if this is already the value
this.valueAccessor.writeValue(newValue);
});
this.valueAccessor.registerOnChange((newValue) => {
subject.next(newValue);
});
}
}
Podríamos detenernos aquí, y podrás escribir esto en tus plantillas:
<input [subjectModel]="subject" [ngDefaultControl]>
Ese extra [ngDefaultControl]
existe para causar manualmente que angular proporcione el ControlValueAccessor
necesario para nuestra directiva. Otros tipos de entradas (como botones de selección y opciones de radio) necesitarían una directiva adicional diferente. Esto se debe a que Angular no adjunta ngModel
valor automáticamente a cada componente del formulario, solo aquellos que también tienen un ngModel
, formControl
o formControlName
.
Si desea hacer un esfuerzo adicional para eliminar la necesidad de esas directivas adicionales, esencialmente tendrá que copiarlas en su código, pero modificar sus selectores para que se activen para su nuevo subjectModel
. Esta es la parte totalmente no probada, pero creo que podrías hacer esto:
// This is copy-paste-tweaked from
// https://angular.io/api/forms/DefaultValueAccessor
@Directive({
selector: ''input:not([type=checkbox])[subjectModel],textarea[subjectModel]'',
host: {
''(input)'': ''_handleInput($event.target.value)'',
''(blur)'': ''onTouched()'',
''(compositionstart)'': ''_compositionStart()'',
''(compositionend)'': ''_compositionEnd($event.target.value)''
},
providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultSubjectModelValueAccessor extends DefaultValueAccessor {}
El crédito por mi comprensión de esto va a ngrx-forms , que emplea esta técnica.
Lo más cercano que puedo pensar es en utilizar un Control de control:
import { FormControl } from ''@angular/forms'';
@Component({
template: ''<input [formControl]="control">''
})
class MyComponent {
control = new FormControl('''');
constructor(){
this.control.valueChanges.subscribe(()=> console.log(''tada''))
}
}