Attribute directive with ngModel to change field value

做~自己de王妃 提交于 2019-11-27 20:02:19

问题


I want to change (force) input field values while typing using a attribute Directive. With it I would like to create directives like uppercase, lowercase, maxlength, filterchar, etc. to be used on input fields on forms. I found this example: Angular 2 Attribute Directive Typescript Example but this doesn't seem to work. Maybe it did for an earlier build of Angular2. It is however exactly what I would like to do.

When I create a directive like this:

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

And use it on a form like this:

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

(and register NgModel as a provider). I get an

undefined this.model.value.

I can use $event.target.value = $event.target.value.toUpperCase() (when passing $event with the onInputChange()) and that works for the view (it does show the input as uppercase. But it doesn't update the bind field "field.name".

So how to create an Angular2 attribute directive that does this?

-- EDIT --

After some further investigation I managed to get what I want. The answer Günter provided is closer to my original intention and perhaps better. But here is another way:

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

As I said I'm not sure if this is also a good way to do this so comments are welcome.


回答1:


update

This approach doesn't work properly. See @RyanHow's answer for a better solution.

original

@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




回答2:


Although Günter's answer looks promising, there is a bug in that the final value in the model has the last entered letter in lowercase.

See here:

https://plnkr.co/edit/SzxO2Ykg2pKq1qfgKVMH

Please use the answer provided in the question. It works correctly.

@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




回答3:


I have faced the same issue, where I need to create the custom select in Angular with select2. I have created following directive thing to achieve this with attribute directive and 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());
      }
    });
  }
}

with following usage

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



回答4:


The requirement I had to create a directive to trim the leading and trailing spaces for text input. my solution:

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


来源:https://stackoverflow.com/questions/36106350/attribute-directive-with-ngmodel-to-change-field-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!