Using Pipes Within ngModel on Input Elements in Angular
Got an error: Cannot have a pipe in an action expression.
The issue
My model has a property “startDate”, which stores Unix timestamp. When I display, I’d like to show only the full year, like 2018. In Angular, we could achieve this easily using pipe. I did it this way:
<input [(ngModel)]="model.startYear | date: 'yyyy'" >
However, I got an error: Cannot have a pipe in an action expression.
The Reasons
- In Angular,
[()]
is two-way binding. []
is one-way attribute binding, which binds data from component to the view. The real-time date transform, which how pipe works, happens here.()
is one-way event binding, which passes data from view to component. Pipe function doesn’t work here.- Therefore, I have to split
[()]
into[]
and()
, and only use pipe in[]
.
A Stackoverflow solution:
<input [ngModel]="item.value | somePipeMethod" (ngModelChange)="item.value=$event" />
This solution works well. But it has some drawbacks.
- I don’t like
(ngModelChange)
. Imagine a user type in “1234” in the input, four times the model got changed. - Like I said at the beginning, my model’s property “startDate” is Unix timestamp, I need to transfer user input, which is a string, to Unix timestamp. The logic here is simple, however, I fell into a big trouble. Because of the
(ngModelChange)
, user’s input converts to Unix timestamp into real time. If a user types in “201”, model’s startDate is a negative number due to the nature of Unix timestamp. In the input, it shows something like 021, which is a disaster.
My workaround
<input value="{\\{node.startDate | date: 'yyyy' }\\}"
(blur)="saveProjectYear(node, startYear.value)" >
- In order to show current format, we only use value attribute so that we could pipe it with the right format
- Onblur, we save this object using a method. It’s almost the same as the one