J'ai rencontré le même problème il y a quelque temps et je souhaite partager un exemple minimal qui fonctionne avec Angular 2+.
Pour les versions plus récentes d'Angular, il existe une approche simplifiée (faites défiler vers le bas) !
Angular 2
Supposons que vous souhaitiez utiliser le code suivant n'importe où dans votre application :
<app-input-slider [(ngModel)]="inputSliderValue"></app-input-slider>
-
Créez maintenant un composant appelé InputSlider
.
-
En el input-slider.component.html
ajoutez ce qui suit :
<input type="range" [(ngModel)]="value" (ngModelChange)="updateChanges()">
-
Maintenant nous devons faire un peu de travail dans le input-slider.component.ts
fichier :
import {Component, forwardRef, OnInit} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";
@Component({
selector: "app-input-slider",
templateUrl: "./input-slider.component.html",
styleUrls: ["./input-slider.component.scss"],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => InputSliderComponent),
multi: true
}]
})
export class InputSliderComponent implements ControlValueAccessor {
/**
* Holds the current value of the slider
*/
value: number = 0;
/**
* Invoked when the model has been changed
*/
onChange: (_: any) => void = (_: any) => {};
/**
* Invoked when the model has been touched
*/
onTouched: () => void = () => {};
constructor() {}
/**
* Method that is invoked on an update of a model.
*/
updateChanges() {
this.onChange(this.value);
}
///////////////
// OVERRIDES //
///////////////
/**
* Writes a new item to the element.
* @param value the value
*/
writeValue(value: number): void {
this.value = value;
this.updateChanges();
}
/**
* Registers a callback function that should be called when the control's value changes in the UI.
* @param fn
*/
registerOnChange(fn: any): void {
this.onChange = fn;
}
/**
* Registers a callback function that should be called when the control receives a blur event.
* @param fn
*/
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
}
Bien sûr, vous pourriez ajouter plus de fonctionnalités et de contrôles de valeur en utilisant cette classe, mais j'espère que cela vous donnera quelques idées.
Explication rapide :
L'astuce consiste à ajouter le fournisseur NG_VALUE_ACCESSOR
sur le décorateur de la classe et implémenter ControlValueAccessor
.
Ensuite, nous devons définir les fonctions writeValue
, registerOnChange
y registerOnTouched
. Ces deux derniers sont directement appelés lors de la création du composant. C'est pourquoi nous avons besoin de variables (par exemple onChange
y onTouched
- mais vous pouvez les nommer comme vous le souhaitez.
Enfin, nous devons définir une fonction qui permet au composant de savoir qu'il doit mettre à jour le ngModel sous-jacent. Je l'ai fait avec la fonction updateChanges
. Il doit être invoqué chaque fois que la valeur change, soit de l'extérieur (c'est pourquoi il est appelé en writeValue
), ou de l'intérieur (c'est pourquoi il est appelé depuis le fichier html ngModelChange
).
Angular 7+ (en anglais)
Bien que la première approche fonctionne toujours pour les versions les plus récentes, vous préférerez peut-être la version suivante qui nécessite moins de saisie.
Auparavant, vous pouviez obtenir une liaison bidirectionnelle en ajoutant quelque chose comme ceci dans le composant externe :
<app-input-slider [inputSliderValue]="inputSliderValue" (inputSliderValueChange)="inputSliderValue = $event"></app-input-slider>
Angular a implémenté un sucre syntaxique pour cela, donc vous pouvez maintenant écrire
<app-input-slider [(inputSliderValue)]="inputSliderValue"></app-input-slider>
si vous suivez les étapes ci-dessous.
-
Créez un composant appelé InputSlider
.
-
En el input-slider.component.html
ajoutez ce qui suit :
<input type="range" [(ngModel)]="inputSliderValue (ngModelChange)="inputSliderValueChange.emit(inputSliderValue)">
-
Maintenant nous devons faire un peu de travail dans le input-slider.component.ts
fichier :
import {Component, forwardRef, OnInit} from "@angular/core";
@Component({
selector: "app-input-slider",
templateUrl: "./input-slider.component.html",
styleUrls: ["./input-slider.component.scss"],
providers: []
})
export class InputSliderComponent {
/**
* Holds the current value of the slider
*/
@Input() inputSliderValue: string = "";
/**
* Invoked when the model has been changed
*/
@Output() inputSliderValueChange: EventEmitter<string> = new EventEmitter<string>();
}
Il est important que la propriété de sortie (EventEmitter) ait le même nom que la propriété d'entrée avec la chaîne ajoutée Change
.
Si nous comparons les deux approches, nous constatons ce qui suit :
- La première approche vous permet d'utiliser
[(ngModel)]="propertyNameOutsideTheComponent"
comme si le composant était un élément de formulaire quelconque.
- Seule la première approche vous permet d'utiliser directement la validation (pour les formulaires).
- Mais la première approche nécessite plus de codage dans la classe du composant que la deuxième approche.
- La deuxième approche vous permet d'utiliser une liaison bidirectionnelle sur votre propriété avec la syntaxe suivante
[(propertyNameInsideTheComponent)]="propertyNameOutsideTheComponent"
1 votes
Cela pourrait Aide
1 votes
@Vikas Oui, il y a une démo qui fonctionne, merci.