Директива атрибута с ngModel для изменения значения поля

Я хочу изменить (принудительно) значения поля ввода при вводе с помощью атрибута Directive. С его помощью я хотел бы создать такие директивы, как прописные, строчные, maxlength, filterchar и т. Д., Которые будут использоваться в полях ввода в формах. Я нашел этот пример: Angular 2 Attribute Directive TypeScript Example но похоже, что это не работает. Возможно, так было с более ранней сборкой Angular2. Однако именно этим я и хотел бы заниматься.

Когда я создаю такую ​​директиву:

import {Directive} from 'angular2/core';
import {NgModel} from 'angular2/common';

@Directive({ 
selector: '[ngModel][uppercase]', 
host: {
    '(input)' : 'onInputChange()'
      }
})
export class UppercaseDirective{

constructor(public model:NgModel){}

onInputChange(){
    var newValue = this.model.value.toUpperCase();
    this.model.valueAccessor.writeValue(newValue);
    this.model.viewToModelUpdate(newValue);
    }
}

И используйте его в такой форме:

<input type="text" class="form-control" [(ngModel)]="field.name" ngControl="name" #name="ngForm" required uppercase>

(и зарегистрируйте NgModel в качестве провайдера). Я получаю

undefined this.model.value.

Я могу использовать $event.target.value = $event.target.value.toUpperCase() (при передаче $event с onInputChange()), и это работает для представления (он показывает ввод в верхнем регистре. Но он не обновляет поле привязки "field.name".

Итак, как создать директиву атрибута Angular2, которая делает это?

- ИЗМЕНИТЬ -

После некоторого дополнительного расследования мне удалось получить то, что я хочу. Ответ Гюнтера ближе к моему первоначальному замыслу и, возможно, лучше. Но вот другой способ:

import {Directive, Input, Output, EventEmitter} from 'angular2/core';

@Directive({ 
selector: '[ngModel][uppercase]',
host: {
"(input)": 'onInputChange($event)'
    }
})
export class UppercaseDirective{
@Output() ngModelChange:EventEmitter<any> = new EventEmitter()
value: any

onInputChange($event){
    this.value = $event.target.value.toUpperCase()
    this.ngModelChange.emit(this.value)
    }
}

Как я уже сказал, я не уверен, что это тоже хороший способ сделать это, поэтому комментарии приветствуются.


person majodi    schedule 19.03.2016    source источник


Ответы (4)


обновить

Этот подход не работает должным образом. См. Ответ @ RyanHow для лучшего решения.

оригинал

@Directive({ 
  selector: '[ngModel][uppercase]',
  providers: [NgModel],
  host: {
    '(ngModelChange)' : 'onInputChange($event)'
  }
})
export class UppercaseDirective{
  constructor(private model:NgModel){}

  onInputChange(event){
    this.model.valueAccessor.writeValue(event.toUpperCase());
  }
}

Plunker

person Günter Zöchbauer    schedule 19.03.2016
comment
Это именно то, что я пытался сделать, Гюнтер, спасибо! Это действительно имеет смысл. Тем временем я нашел другой способ сделать это. Я отредактирую свой вопрос этим вариантом. Я не уверен, что это тоже хороший способ. Может, тебе стоит взглянуть на это. - person majodi; 20.03.2016
comment
Конечно, просто напишите комментарий после того, как добавили свой ответ, и я получу уведомление. - person Günter Zöchbauer; 20.03.2016
comment
Я попробовал @Output() ngModelChange:EventEmitter установить значение, но у меня это не сработало: D. Я думаю, что использование ngModelChange для @Input() имеет то преимущество, что оно работает для всех видов элементов ввода, которые охватываются ngModel, а также с браузерами, в которых используются разные события (в настоящее время из-за этого возникают проблемы с входами select и radio - по крайней мере, когда ngModel проблемы исправлены. Думаю, мне бы хотелось, чтобы комбинация моего @Input() и вашего @Output() лучше всего работала. - person Günter Zöchbauer; 20.03.2016
comment
Я пробовал комбинацию, но это вызывает бесконечные циклы: - / - person Günter Zöchbauer; 20.03.2016
comment
Я еще немного поэкспериментирую, используя оба варианта. На данный момент я счастлив, что узнал достаточно, чтобы как-то все исправить. thnx. - person majodi; 20.03.2016
comment
stackoverflow.com/questions/35826325/ - person Kody; 04.03.2017
comment
Спасибо за помощь .. хорошее решение :) - person Abhijeet; 16.03.2018

Хотя ответ Гюнтера выглядит многообещающим, есть ошибка, заключающаяся в том, что окончательное значение в модели содержит последнюю введенную букву в нижнем регистре.

Глянь сюда:

https://plnkr.co/edit/SzxO2Ykg2pKq1qfgKVMH

Пожалуйста, используйте ответ, указанный в вопросе. Работает корректно.

@Directive({ 
selector: '[ngModel][uppercase]',
host: {
"(input)": 'onInputChange($event)'
    }
})
export class UppercaseDirective{
@Output() ngModelChange:EventEmitter<any> = new EventEmitter()
value: any

onInputChange($event){
    this.value = $event.target.value.toUpperCase()
    this.ngModelChange.emit(this.value)
    }
}

https://plnkr.co/edit/oE3KNMCG7bvEj8FV07RV

person Ryan How    schedule 01.06.2016
comment
Мой Plunker работает нормально. Как я могу воспроизвести проблему, о которой вы говорите? - person Günter Zöchbauer; 01.06.2016
comment
В вашем плунжере, если вы поместите привязку, например. ‹H2› Здравствуйте, {{field.name}} ‹/h2›. В нем последняя буква в нижнем регистре. - person Ryan How; 02.06.2016
comment
Кроме того, в angular rc1 обновление модели запускает событие ngModelChange для повторного запуска, и вы получаете бесконечный цикл. - person Ryan How; 02.06.2016
comment
Что касается исходного вопроса (который я не могу прокомментировать: /), в rc1 исходный код будет работать при использовании $ event.target.value.toUpperCase (), потому что обновление модели вызывает событие изменения модели. Кажется, кое-что изменилось! - person Ryan How; 02.06.2016
comment
@RyanHow: У меня оба PLNKR не работают. Я экспериментировал с этим подходом, и он сработал. Но что я могу сделать, если я хочу иметь исходные входные значения в поле ввода, но измененные значения в модели? При таком подходе у меня все еще есть обратная связь от модели к исходным данным. - person westor; 13.07.2016
comment
@westor: Не знаю, почему они не работают. Они даже не загружаются для меня сейчас :(. Вы можете использовать геттеры и сеттеры в своей модели, чтобы создать эффект форматеров и парсеров, которые angular2 поддерживает из коробки. Не знаю, как это сделать из директивы атрибутов. Я у меня та же проблема, но я не разбирался в ней с тех пор, как эта часть заработала. Нужны специалисты здесь, привет! - person Ryan How; 15.07.2016
comment
@Ryan, как это работает у меня частично. он обновил значение моего текстового поля, но он также выдает ошибку 'ExpressionChangedAfterItHasBeenCheckedError', пожалуйста, скажите мне, почему это, вероятно, произошло - person MeVimalkumar; 05.03.2018
comment
@Vicky Извините, Angular так сильно изменился с тех пор, и я подозреваю, что это больше не работает. - person Ryan How; 06.03.2018

Я столкнулся с той же проблемой, когда мне нужно создать собственный выбор в Angular с помощью select2. Я создал следующую директиву, чтобы добиться этого с помощью директивы атрибутов и ngModel.

import {ElementRef, Directive, EventEmitter, Output, Input} from '@angular/core';
import {NgModel} from "@angular/forms";
declare let $;
@Directive({
  selector: '[custom-select]',
  providers: [NgModel]
})
export class CustomSelectComponent{
  $eventSelect:any;
  @Output() ngModelChange:EventEmitter<any> = new EventEmitter();
  @Input() set ngModel(value:any){
    //listen to the input value change of ngModel and change in the plugin accordingly.
    if(this.$eventSelect){
      this.$eventSelect.val(value).trigger('change',{fromComponent:true});
    }
  }
  constructor(private elementRef: ElementRef) {}
  ngOnInit(){
    this.$eventSelect = $(this.elementRef.nativeElement);
    this.$eventSelect.select2({minimumResultsForSearch:-1});
    this.$eventSelect.on("change.select2", (event,data)=> {
      //listen to the select change event and chanage the model value 
      if(!data || !data.fromComponent){ //dont change model when its chagned from the input change event
        this.ngModelChange.emit(this.$eventSelect.val());
      }
    });
  }
}

со следующим использованием

<select custom-select [(ngModel)]="protocol.type">
  <option value="1">option1</option>
  <option value="1">option2</option>
</select>
person Dinesh Dabhi    schedule 30.12.2017

Требование Мне пришлось создать директиву для обрезки начальных и конечных пробелов для ввода текста. мое решение:

import { Directive, ElementRef, HostListener, Output, EventEmitter } from '@angular/core';
import { NgModel } from "@angular/forms";

@Directive({
    selector: '[text-trim]',
    providers: [NgModel]
})

export class TextInputTrimDirective {

@Output() ngModelChange: EventEmitter<any> = new EventEmitter();

constructor(private el: ElementRef) {}

@HostListener('change') onInputChange() {
    const value = this.el.nativeElement.value.trim();
    this.ngModelChange.emit(value);
}
}
person Ali Jamal    schedule 18.01.2019