Uso de tuberías dentro de ngModel en elementos INPUT en angular

143

Tengo un campo HTML INPUT.

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

y quiero formatear su valor y usar una tubería existente:

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

y recibe el mensaje de error:

No se puede tener una tubería en una expresión de acción

¿Cómo puedo usar tuberías en este contexto?

Solitario
fuente

Respuestas:

213

No puede usar operadores de expresión de plantilla (tubería, guardar navegador) dentro de la declaración de plantilla:

(ngModelChange)="Template statements"

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

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

Al igual que las expresiones de plantilla, las declaraciones de plantilla usan un lenguaje que se parece a JavaScript. El analizador de sentencias de plantilla difiere del analizador de expresiones de plantilla y admite específicamente tanto la asignación básica (=) como las expresiones de encadenamiento (con; o,).

Sin embargo, cierta sintaxis de JavaScript no está permitida :

  • nuevo
  • operadores de incremento y decremento, ++ y -
  • asignación de operador, como + = y - =
  • los operadores bit a bit | y &
  • los operadores de expresiones de plantilla

Entonces debes escribirlo de la siguiente manera:

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

Ejemplo de Plunker

yurzui
fuente
3
¿Alguien puede explicar por qué tiene que dividirse así? Estoy tratando de vincular una fecha a una entrada con tipo date: [(ngModel)] = "model.endDate | date: 'y-MM-dd'" y la tubería no funcionará. Sin embargo, si elimino la sintaxis de banana y uso la sintaxis dividida anterior, funciona bien.
Blake Rivell
¿Esto realmente funcionó? No funcionó para mí. dice No puede tener una tubería en una expresión de acción
NoStressDeveloper
44
Esto funcionó para mí! @BlakeRivell "[]" enlaza la propiedad en un sentido desde el origen de datos para ver el destino en ese punto, puede cambiar cómo se muestra con una tubería. Cuando se usa el enlace "()", es al revés que cambiar el formato sería inútil aquí. Así que supongo que es por eso que el plátano en una caja "[()]" no funciona con una tubería y dividirlos es el camino a seguir. Puede leer más sobre esto aquí: angular.io/docs/ts/latest/guide/…
Mike Bovenlander
8
Tenga en cuenta que, en el ejemplo, la tubería solo funciona en una dirección. Digamos que item.valuees un número, y lo usa DatePipepara convertirlo en una cadena de fecha. Cuando se edita la fecha, $eventtambién será una cadena de fecha y no se ajustará de nuevo. item.valueDebe invertir lo que hizo la tubería en su (ngModelChange)expresión, es decir, volver la cadena de fecha a un número.
Tuupertunut
3
@Protagonista (ngModelChange)="updateItemValue($event)", luego cree un updateItemValue(date: string)método y dentro de él. item.value = someConversionFunction(date); Ahora, si pregunta qué debe usar como función de conversión, no lo sé. Quizás Date.parse()podría funcionar.
Tuupertunut
111
<input [ngModel]="item.value | useMyPipeToFormatThatValue" 
      (ngModelChange)="item.value=$event" name="inputField" type="text" />

La solución aquí es dividir el enlace en un enlace unidireccional y un enlace de evento, que en [(ngModel)]realidad abarca la sintaxis . []es una sintaxis de enlace unidireccional y ()es una sintaxis de enlace de eventos. Cuando se usan juntos: [()]Angular reconoce esto como una forma abreviada y conecta un enlace bidireccional en forma de enlace unidireccional y un enlace de evento a un valor de objeto componente.

La razón por la que no puede usar [()]una tubería es que las tuberías funcionan solo con enlaces unidireccionales. Por lo tanto, debe dividir la tubería para operar solo en el enlace unidireccional y manejar el evento por separado.

Consulte Sintaxis de plantilla angular para obtener más información.

KnowHoper
fuente
1
¿Cómo agrego la expresión de condición como | número: '3.2-5'?
Protagonista
14
<input [ngModel]="item.value | currency" (ngModelChange)="item.value=$event"
name="name" type="text" />

Me gustaría agregar un punto más a la respuesta aceptada.

Si el tipo de control de entrada no es texto, la tubería no funcionará.

Tenlo en cuenta y ahorra tiempo.

Tibin Thomas
fuente
considere agregar más información en su respuesta
Inder
1
compruebe la biblioteca angular ngx-locale-mask que hice para enmascarar el cuadro de entrada para una moneda en particular basada en la localización angular
Tibin Thomas
6

Probé las soluciones anteriores, pero el valor que va al modelo fue el valor formateado y luego regresó y me dio errores de currencyPipe. Entonces tuve que

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

Y en la función de addToAmount -> cambio en desenfoque porque el ngModelChange me estaba dando problemas con el cursor.

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

Y eliminando los otros valores no numéricos.

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
fuente
también probamos la respuesta elegida para Percent pipe y escribimos un método como toDecimal () para (ngModelChange), y los 2 métodos se persiguen entre sí. así que no puedes escribir más de 1 dígito. sorprendente que haya votado tanto
Angela P
1

Mi solución se proporciona a continuación aquí searchDetail es un objeto.

<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 The Navigator
fuente
0

debe usar [ngModel] en lugar del enlace de modelo bidireccional con [(ngModel)]. luego use el evento de cambio manual con (ngModelChange). Esta es una regla pública para todas las entradas bidireccionales en los componentes.

porque la tubería en el emisor de eventos está mal.

hamid_reza hobab
fuente
0

debido a la unión bidireccional, para evitar errores de:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was 
checked.

puede llamar a una función para cambiar el modelo de esta manera:

<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);
}

será bueno si hay una mejor solución para evitar este error.

Mohammad Reza Mrg
fuente