Utilisation de tuyaux dans ngModel sur des éléments INPUT dans Angular

144

J'ai un champ HTML INPUT.

<input 
    [(ngModel)]="item.value" 
    name="inputField" 
    type="text" 
/>

et je veux mettre en forme sa valeur et utiliser un tube existant:

.... 
[(ngModel)]="item.value | useMyPipeToFormatThatValue" 
....

et obtenez le message d'erreur:

Impossible d'avoir un tube dans une expression d'action

Comment puis-je utiliser des tuyaux dans ce contexte?

solitaire
la source

Réponses:

215

Vous ne pouvez pas utiliser les opérateurs d'expression de modèle (tube, navigateur de sauvegarde) dans l'instruction de modèle:

(ngModelChange)="Template statements"

(ngModelChange) = "item.value | useMyPipeToFormatThatValue = $ event"

https://angular.io/guide/template-syntax#template-statements

Comme les expressions de modèle, les instructions de modèle utilisent un langage qui ressemble à JavaScript. L'analyseur d'instruction de modèle diffère de l'analyseur d'expressions de modèle et prend spécifiquement en charge à la fois l'affectation de base (=) et les expressions de chaînage (avec; ou,).

Cependant, certaines syntaxes JavaScript ne sont pas autorisées :

  • Nouveau
  • opérateurs d'incrémentation et de décrémentation, ++ et -
  • affectation d'opérateur, telle que + = et - =
  • les opérateurs bit à bit | et &
  • les opérateurs d'expression de modèle

Vous devriez donc l'écrire comme suit:

<input [ngModel]="item.value | useMyPipeToFormatThatValue" 
      (ngModelChange)="item.value=$event" name="inputField" type="text" />

Exemple de Plunker

yurzui
la source
3
Quelqu'un peut-il expliquer pourquoi il doit être divisé comme ça? J'essaye de lier une date à une entrée avec la date de type: [(ngModel)] = "model.endDate | date: 'y-MM-dd'" et le tuyau ne fonctionnera pas. Cependant, si je supprime la syntaxe banane et que j'utilise la syntaxe séparée ci-dessus, cela fonctionne bien.
Blake Rivell
Cela a-t-il vraiment fonctionné? cela n'a pas fonctionné pour moi. il dit Impossible d'avoir un tube dans une expression d'action
NoStressDeveloper
4
Cela a fonctionné pour moi! @BlakeRivell "[]" lie la propriété à sens unique depuis la source de données pour afficher la cible à ce stade, vous pouvez modifier la façon dont elle est affichée avec un tube. Lors de l'utilisation de la liaison "()", c'est l'inverse, changer le format serait inutile ici. Donc, je suppose que c'est pourquoi la banane dans une boîte "[()]" ne fonctionne pas avec un tuyau et les fendre est la voie à suivre. Vous pouvez en savoir plus ici: angular.io/docs/ts/latest/guide/…
Mike Bovenlander
8
Attention, dans l'exemple, le tuyau ne fonctionne que dans une seule direction. Disons que item.valuec'est un nombre, et que vous utilisez DatePipepour le convertir en une chaîne de date. Lorsque la date est modifiée, le $eventsera également une chaîne de date et ne rentrera pas dans item.valueVous devez inverser ce que le tube a fait dans votre (ngModelChange)expression - c'est-à-dire ramener la chaîne de date à un nombre.
Tuupertunut
3
@Protagonist (ngModelChange)="updateItemValue($event)", puis créez une updateItemValue(date: string)méthode et à l'intérieur. item.value = someConversionFunction(date); Maintenant, si vous demandez ce que vous devez utiliser comme fonction de conversion, je ne sais pas. Peut Date.parse()- être pourrait fonctionner.
Tuupertunut
111
<input [ngModel]="item.value | useMyPipeToFormatThatValue" 
      (ngModelChange)="item.value=$event" name="inputField" type="text" />

La solution ici est de diviser la liaison en une liaison unidirectionnelle et une liaison d'événement - que la syntaxe [(ngModel)]englobe en fait. []est une syntaxe de liaison unidirectionnelle et ()une syntaxe de liaison d'événement. Lorsqu'il est utilisé ensemble - [()]Angular reconnaît cela comme un raccourci et connecte une liaison bidirectionnelle sous la forme d'une liaison unidirectionnelle et d'une liaison d'événement à une valeur d'objet composant.

La raison pour laquelle vous ne pouvez pas utiliser [()]avec un tube est que les tubes ne fonctionnent qu'avec des liaisons unidirectionnelles. Par conséquent, vous devez séparer le canal pour n'opérer que sur la liaison unidirectionnelle et gérer l'événement séparément.

Voir Syntaxe du modèle angulaire pour plus d'informations.

KnowHoper
la source
1
Comment ajouter l'expression de condition comme | nombre: «3.2-5»?
Protagoniste
14
<input [ngModel]="item.value | currency" (ngModelChange)="item.value=$event"
name="name" type="text" />

Je voudrais ajouter un autre point à la réponse acceptée.

Si le type de votre contrôle d'entrée n'est pas du texte, le tube ne fonctionnera pas.

Gardez cela à l'esprit et économisez votre temps.

Tibin Thomas
la source
veuillez envisager d'ajouter plus d'informations dans votre réponse
Inder
1
vérifiez la bibliothèque angulaire ngx-locale-mask que j'ai créée pour masquer la zone de saisie pour une devise particulière en fonction de la localisation angulaire
Tibin Thomas
6

J'ai essayé les solutions ci-dessus, mais la valeur qui va au modèle était la valeur formatée puis retournée et me donnant des erreurs currencyPipe. Alors je devais

  [ngModel]="transfer.amount | currency:'USD':true"
                                   (blur)="addToAmount($event.target.value)"
                                   (keypress)="validateOnlyNumbers($event)"

Et sur la fonction de addToAmount -> changer le flou car le ngModelChange me posait des problèmes de curseur.

removeCurrencyPipeFormat(formatedNumber){
    return formatedNumber.replace(/[$,]/g,"")
  }

Et en supprimant les autres valeurs non numériques.

validateOnlyNumbers(evt) {
  var theEvent = evt || window.event;
  var key = theEvent.keyCode || theEvent.which;
  key = String.fromCharCode( key );
  var regex = /[0-9]|\./;
  if( !regex.test(key) ) {
    theEvent.returnValue = false;
    if(theEvent.preventDefault) theEvent.preventDefault();
  }
cabaji99
la source
nous avons également essayé la réponse choisie pour Percent pipe et écrit une méthode comme toDecimal () pour le (ngModelChange), et les 2 méthodes se poursuivent. vous ne pouvez donc pas taper plus d'un chiffre. surprenant qu'il soit tellement voté
Angela P
1

Ma solution est donnée ci-dessous ici searchDetail est un objet.

<p-calendar  [ngModel]="searchDetail.queryDate | date:'MM/dd/yyyy'"  (ngModelChange)="searchDetail.queryDate=$event" [showIcon]="true" required name="queryDate" placeholder="Enter the Query Date"></p-calendar>

<input id="float-input" type="text" size="30" pInputText [ngModel]="searchDetail.systems | json"  (ngModelChange)="searchDetail.systems=$event" required='true' name="systems"
            placeholder="Enter the Systems">
Bhasker le navigateur
la source
0

vous devez utiliser [ngModel] au lieu d'une liaison de modèle bidirectionnelle avec [(ngModel)]. puis utilisez l'événement de changement manuel avec (ngModelChange). il s'agit d'une règle publique pour toutes les entrées bidirectionnelles dans les composants.

parce que le tuyau sur l'émetteur d'événement est faux.

hamid_reza hobab
la source
0

en raison de la liaison bidirectionnelle, pour éviter les erreurs de:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was 
checked.

vous pouvez appeler une fonction pour changer de modèle comme ceci:

<input [ngModel]="item.value" 
  (ngModelChange)="getNewValue($event)" name="inputField" type="text" />


import { UseMyPipeToFormatThatValuePipe } from './path';

constructor({
    private UseMyPipeToFormatThatValue: UseMyPipeToFormatThatValuePipe,
})

getNewValue(ev: any): any {
    item.value= this.useMyPipeToFormatThatValue.transform(ev);
}

ce sera bien s'il existe une meilleure solution pour éviter cette erreur.

Mohammad Reza Mrg
la source