I have the following form field which works fine. By that, I mean that when I type, paste, etc into the field, the fooObj.expDate
gets updated just fine in real time and validation occurs. I have the pre-tag to make this obvious to myself.
<pre>{{fooObj.someDate | json}}</pre>
<div class="form-group inline-form__input">
<label for="someDate">Some Date</label>
<input tabindex="2"
type="tel"
class="form-control"
maxlength="7"
placeholder="MM/YY"
formControlName="someDate"
name="someDate"
[(ngModel)]="fooObj.someDate"
someDate>
</div>
However, I have that someDate
directive on this field. This directive intercepts paste events. It cancels the paste event, does some fancy formatting of the input and then does this:
setTimeout(() => {
this.target.value = 'lol fancy date';
}, 3000);
target.value
is my someDate
field. The value gets updated fine inside the input box (I see it change on the screen inside the input). However, fooObj.someDate
is NOT updated and validation does not occur. E.g. setting target value in a timeout does NOT trigger the same validation/object update as typing a key / pasting / any other javascript event.
Angular docs don't have much useful to say on this:
Angular updates the bindings (and therefore the screen) only if the app does something in response to asynchronous events, such as keystrokes. This example code binds the keyup event to the number 0, the shortest template statement possible. While the statement does nothing useful, it satisfies Angular's requirement so that Angular will update the screen.
So, how do I trigger an update of the field from a directive on that field?
Edit: I tried triggering an event on the element as recommended in the comments by using the code found here on my element: How can I trigger an onchange event manually?
Runs fine, but doesn't force the field to update:
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
this.target.dispatchEvent(evt);
}
else
this.target.fireEvent("onchange");
Also, here is where I am getting the idea of synthetic events not triggering "normal" actions as a keyDown or whatever would (I am really hoping I am misreading or they are wrong for this use case, but it didn't work with trying to re-issue a paste event): https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces
NOTE: Synthetic events do not have default actions. In other words, while the script above will fire a paste event, the data will not actually be pasted into the document.
I don't know the details of your directive, however I can guess as to your intentions. To start with, we're going to subscribe to the valueChanges
observable of our control and eschew the two way binding directly on the control just to avoid excessive writes and checks:
form.html
<input tabindex="2"
type="tel"
class="form-control"
maxlength="7"
placeholder="MM/YY"
formControlName="someDate"
name="someDate"
someDate />
form.ts
Here is where we subscribe (it can move out of the constructor, depends on when you're making the form).
constructor() {
this.myForm = new FormGroup({
someDate: new FormControl(''),
});
this.myForm.controls['someDate'].valueChanges.subscribe(
value => this.fooObj.someDate = value;
);
}
some-date.directive.ts
The directive will write the value into the control and the subscription to the valueChanges
will then update our model. This works on a paste event and every other event (so you can limit the events targeted, but I wanted to at least make sure pasting worked).
@Directive({
selector: '[someDate]'
})
export class SomeDateDirective{
constructor(private el: ElementRef, private control : NgControl) {
}
@HostListener('input',['$event']) onEvent($event){
$event.preventDefault();
let data = $event.clipboardData.getData('text');
setTimeout(() => {
this.control.control.setValue(data.toUpperCase());
}, 3000);
}
}
Change to paste
instead of input
to capture only onpaste
events. It's a little weird with preventDefault()
as the input effectively disappears for a while.
Here's a plunker: http://plnkr.co/edit/hsisILvtKErBBOXECt8t?p=preview
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With