Attribute directive with ngModel to change field value
Solution 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());
}
}
Solution 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
Solution 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>
Related videos on Youtube
majodi
Updated on July 09, 2022Comments
-
majodi almost 2 years
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 anundefined this.model.value.
I can use
$event.target.value = $event.target.value.toUpperCase()
(when passing$event
with theonInputChange()
) 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.
-
majodi about 8 yearsThis is exactly what I was trying to do Günter, thnx! This really makes sense. In the mean time I found another way of doing this. I will edit my question with this variation. I'm not sure if this is also a good way. Maybe you could have a look at it.
-
Günter Zöchbauer about 8 yearsSure, just write a comment after you added your answer, so I get notified.
-
Günter Zöchbauer about 8 yearsI tried the
@Output() ngModelChange:EventEmitter
to set the value but this didn't work for me :D. I think usingngModelChange
for the@Input()
has the advantage that it works for all kinds of input elements that are covered byngModel
and also with browsers where different events are used (there are currently issues with select and radio inputs because of this - at least when thengModel
issues are fixed. I guess I'd like a combination of my@Input()
and your@Output()
best if it is actually working. -
Günter Zöchbauer about 8 yearsI tried the combination but that causes endless loops :-/
-
majodi about 8 yearsI will experiment a bit more using both options. For now I'm happy to have learned enough to get it right somehow. thnx.
-
Günter Zöchbauer almost 8 yearsMy Plunker seems to work fine. How can I reproduce the issue you mention?
-
Ryan How almost 8 yearsIn your plunker, if you put a binding eg. <h2>Hello {{field.name}}</h2>. It has a lowercase last letter.
-
Ryan How almost 8 yearsAlso, in angular rc1 updating the model triggers the ngModelChange event to trigger again and you get an infinite loop.
-
Ryan How almost 8 yearsAnd in respect to the original question (which I can't comment on :/), in rc1 the initial code would work if using $event.target.value.toUpperCase() because updating the model emits the model change event. Seems a few things have changed!
-
westor almost 8 years@RyanHow: The plnkrs are both not running for me. I experimented with that approach, and it's working. But what can I do, if I want to have original input values in input field, but modified values in model? With this approach I still have "feedback" from the model back to the input.
-
Ryan How almost 8 years@westor: Not sure why they aren't working. They don't even load for me now :(. You can use getters and setters on your model to give the effect of formatters and parsers which angular2 supports out of the box. Not sure how to do it from an attribute directive though. I have the same problem but haven't looked into it since I got this part working. Need some experts here hey!
-
Kody about 7 years
-
MeVimalkumar about 6 years@RyanHow it works for me partially. it updated my textbox value but it also gives me an error 'ExpressionChangedAfterItHasBeenCheckedError' could pls tell me why this would probably be occurred
-
Ryan How about 6 years@Vicky Sorry, Angular has changed so much since here and I suspect this doesn't work anymore.
-
Abhijeet about 6 yearsThanks for your help .. nice solution :)