Używanie potoków w ngModel na elementach INPUT w Angular

144

Mam pole HTML INPUT.

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

i chcę sformatować jego wartość i użyć istniejącego potoku:

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

i otrzymaj komunikat o błędzie:

W wyrażeniu akcji nie może znajdować się potok

Jak mogę używać rur w tym kontekście?

Samotny
źródło

Odpowiedzi:

215

Nie możesz używać operatorów wyrażeń szablonowych (potok, nawigator zapisywania) w instrukcji szablonu:

(ngModelChange)="Template statements"

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

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

Podobnie jak wyrażenia szablonowe, instrukcje szablonowe używają języka, który wygląda jak JavaScript. Parser instrukcji template różni się od parsera wyrażeń szablonu i obsługuje w szczególności zarówno podstawowe przypisanie (=), jak i wyrażenia łańcuchowe (z; lub,).

Jednak niektóre składnie JavaScript są niedozwolone :

  • Nowy
  • operatory inkrementacji i dekrementacji, ++ i -
  • przypisanie operatora, na przykład + = i - =
  • operatory bitowe | i &
  • operatory wyrażeń szablonu

Więc powinieneś napisać to w następujący sposób:

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

Przykład Plunkera

yurzui
źródło
3
Czy ktoś może wyjaśnić, dlaczego należy to podzielić w ten sposób? Próbuję powiązać datę z danymi wejściowymi z datą typu: [(ngModel)] = "model.endDate | date: 'y-MM-dd'", a potok nie będzie działał. Jeśli jednak pozbędę się składni bananów i użyję podzielonej składni powyżej, działa dobrze.
Blake Rivell
Czy to naprawdę zadziałało? to nie zadziałało dla mnie. jest napisane, że nie można mieć potoku w wyrażeniu akcji
NoStressDeveloper
4
To zadziałało dla mnie! @BlakeRivell "[]" wiąże właściwość w jedną stronę ze źródła danych do celu wyświetlania. W tym momencie możesz zmienić sposób wyświetlania za pomocą potoku. Podczas korzystania z wiązania "()" jest odwrotnie, zmiana formatu byłaby tutaj bezużyteczna. Więc myślę, że właśnie dlatego banan jest w pudełku "[()]" nie działa z fajką, a ich rozłupywanie jest najlepszym rozwiązaniem. Możesz przeczytać więcej na ten temat tutaj: angular.io/docs/ts/latest/guide/…
Mike Bovenlander
8
Uważaj, w tym przykładzie rura działa tylko w jednym kierunku. Powiedzmy, że item.valuejest to liczba i używasz jej DatePipedo konwersji na ciąg daty. Kiedy data jest edytowana, $eventbędzie również ciągiem daty i nie będzie pasować z powrotem. item.valueMusisz odwrócić to, co zrobił potok w twoim (ngModelChange)wyrażeniu - tj. Zamienić łańcuch daty z powrotem na liczbę.
Tuupertunut
3
@ Protagonist (ngModelChange)="updateItemValue($event)", a następnie stwórz updateItemValue(date: string)metodę i wewnątrz niej item.value = someConversionFunction(date); Teraz, jeśli pytasz, czego użyć jako funkcji konwersji, nie wiem. Może Date.parse()może zadziałać.
Tuupertunut
111
<input [ngModel]="item.value | useMyPipeToFormatThatValue" 
      (ngModelChange)="item.value=$event" name="inputField" type="text" />

Rozwiązaniem jest tutaj podzielenie powiązania na powiązanie jednokierunkowe i powiązanie zdarzenia - które w [(ngModel)]rzeczywistości obejmuje składnia . []jest składnią powiązań jednokierunkowych i składnią powiązań ()zdarzeń. Gdy używane razem - [()]Angular rozpoznaje to jako skrót i łączy dwukierunkowe powiązanie w postaci powiązania jednokierunkowego i powiązania zdarzenia z wartością obiektu składnika.

Powodem, dla którego nie można używać [()]z potokiem, jest to, że rury działają tylko z wiązaniami jednokierunkowymi. Dlatego należy oddzielić potok, aby działał tylko na jednokierunkowym powiązaniu i oddzielnie obsługiwał zdarzenie.

Aby uzyskać więcej informacji, zobacz Składnia szablonów kątowych .

KnowHoper
źródło
1
Jak dodać wyrażenie warunku, takie jak | liczba: „3.2-5”?
Protagonista
14
<input [ngModel]="item.value | currency" (ngModelChange)="item.value=$event"
name="name" type="text" />

Chciałbym dodać jeszcze jeden punkt do zaakceptowanej odpowiedzi.

Jeśli typ kontrolki wprowadzania danych nie jest tekstem, potok nie będzie działał.

Pamiętaj o tym i oszczędzaj czas.

Tibin Thomas
źródło
uprzejmie rozważ dodanie więcej informacji w swojej odpowiedzi
Inder
1
sprawdź bibliotekę kątową ngx-locale-mask, którą utworzyłem, aby zamaskować pole wprowadzania dla określonej waluty w oparciu o kątowe ustawienia regionalne
Tibin Thomas
6

Wypróbowałem powyższe rozwiązania, ale wartości, które trafiają do modelu, były wartościami sformatowanymi, a następnie zwracały i wyświetlały mi błędy currencyPipe. Więc musiałem

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

A na funkcji addToAmount -> zmiana przy rozmyciu, ponieważ ngModelChange dawał mi problemy z kursorem.

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

I usunięcie innych wartości nienumerycznych.

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
źródło
wypróbowaliśmy również wybraną odpowiedź dla potoku Percent i napisaliśmy metodę taką jak toDecimal () dla (ngModelChange), a dwie metody ścigają się nawzajem. więc nie możesz wpisać więcej niż 1 cyfra. zaskakujące, że jest tak bardzo przychylny
Angela P
1

Moje rozwiązanie jest podane poniżej tutaj searchDetail jest obiektem ..

<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
źródło
0

musisz użyć [ngModel] zamiast dwukierunkowego wiązania modelu z [(ngModel)]. następnie użyj zdarzenia zmiany ręcznej z (ngModelChange). jest to zasada publiczna dla wszystkich dwukierunkowych danych wejściowych w komponentach.

ponieważ potok na emiterze zdarzeń jest nieprawidłowy.

hamid_reza hobab
źródło
0

Ze względu na dwukierunkowe wiązanie, aby zapobiec błędom:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was 
checked.

możesz wywołać funkcję, aby zmienić model w następujący sposób:

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

będzie dobrze, jeśli istnieje lepsze rozwiązanie, aby zapobiec temu błędowi.

Mohammad Reza Mrg
źródło