How does ngModelChange works without providing model name in ngModel directive - javascript

How does ngModelChange() works?
(ngModelChange) is the #Output of ngModel directive. It fires when the model changes. You cannot use this event without ngModel directive
but I am not sure, how does(ngModelChange) it works, if I am use ngModelChange() event, even i am not providing model name to ngModel.
<input #gb type="text" pInputText class="ui-widget ui-text" **ngModel**
(ngModelChange)="functionName($event)">

Yes, ngModelChange() work without providing model name to ngModel.
cause of this happen, (ngModelChange) is the #Output of ngModel directive.
when insert some value in input that time emitEvent is become true which is by default false (so it not fire page load on initial time).
_this.updateValueAndValidity({ emitEvent: false });
you can find at \#angular\forms\esm5\forms.js ► line no 3850
If emitEvent is true, this
change will cause a valueChanges event on the FormControl to be emitted. This defaults
to true (as it falls through to updateValueAndValidity).
If emitViewToModelChange is true, an ngModelChange event will be fired to update the
model. This is the default behavior if emitViewToModelChange is not specified.
If emitModelToViewChange is true, the view will be notified about the new value
via an onChange event.
now question is that why get same value in $event which is inserted in input instead of ture, that cause
FormControl.prototype.setValue = /**
function (value, options) {
var _this = this;
if (options === void 0) { options = {}; }
(/** #type {?} */ (this)).value = this._pendingValue = value;
if (this._onChange.length && options.emitModelToViewChange !== false) {
this._onChange.forEach(function (changeFn) { return changeFn(_this.value, options.emitViewToModelChange !== false); });
}
this.updateValueAndValidity(options);
};
same file line no 3911 to 3919

In the Source code ngModelChange is just an event emitter.
#Output('ngModelChange') update = new EventEmitter();
It fires when the viewToModelUpdate function is executed.
viewToModelUpdate(newValue: any): void {
this.viewModel = newValue;
this.update.emit(newValue);
}
ngModel can be anything and does not have a direct link to anything else. In the code it is declared and it is only used in a function called ngOnChanges
#Input('ngModel') model: any;
ngOnChanges(changes: SimpleChanges) {
this._checkForErrors();
if (!this._registered) this._setUpControl();
if ('isDisabled' in changes) {
this._updateDisabled(changes);
}
if (isPropertyUpdated(changes, this.viewModel)) {
this._updateValue(this.model);
this.viewModel = this.model;
}
}
I could be wrong here but it looks to me that ngModel is not the single source of truth but this.viewModel seems to be, because of this ngModel does not need a value for ngModelChange to work as it opporates seporatetly from the ngModel value.
Hope this helps.

you try it without ngModel
<select (change)="changed($event)">
<option *ngFor="let data of allData" [value]="data.id">
{{data.name}}
</option>
</select>
changed(e){
//event comes as parameter and then find data manually
//by using e.target.data
}
OR BY ID
<inputtype="text" #byid (change)="onChange(byid.value)" />
onChange(title:string){
alert(title);
}
You can try by passing id into input

Related

Vuejs Datepicker enable a disabled element

I have 2 instances of the <date-picker>, in which the first captures the start date (starts_at) and the second captures the end date (ends_at).
As per the design I have in mind, I would like to disable the ends_at instance and then re-enable it once the starts_at has been applied.
This is my starts_at instance:
<date-picker v-model="starts_at" #input="enableEndsAt()" valueType="format"></date-picker>
This is my ends_at instance:
<date-picker id="endsAt" v-model="ends_at" disabled #input="checkDate()" valueType="format"></date-picker>
This is what I have tried so far. When you change the input, it calls the enableEndsAt() function, which looks like this:
enableEndsAt(){
if(this.starts_at === "") {
return
}
var element = document.querySelector('#endsAt')
element.removeAttribute('disabled')
console.log(element)
}
You can use disabled as a reactive property:
<date-picker id="endsAt"
v-model="ends_at"
:disabled="disableDatePicker"
#input="checkDate()"
valueType="format"></date-picker>
So, to use this, you could create a property disableDatePicker in your data declaration, and then set it inside your method:
data(){ return { disableDatePicker: false }},
...
methods: { ....
enableEndsAt(){
if(this.starts_at === "") {
return
}
this.disableDatePicker = true;
}
Assuming your <date-picker> can accept disabled property, you can simply bind it to a condition:
<date-picker id="endsAt" v-model="ends_at" :disabled="this.starts_at === ''" #input="checkDate()" valueType="format"></date-picker>
so you don't need to put #input="enableEndsAt()" on your starts_at date picker, and you don't need enableEndsAt() function

vue.js element selected by focus is not reactive

I have a listener to check what input was selected at last to add some kind of string/variable later into it.
created: function () {
document.addEventListener('focusin', this.focusChanged);
}
focusChanged(event) {
if (event.target.id !== 'variable-search') {
this.lastElement = event.target;
}
}
This seems to work fine, and when I click on an input field this.lastElement gets updated with the focused element. All these inputs have a v-model which can be a string in an object or just a plain string.
Now the thing is when I try to update the value by:
this.lastElement.value += variable;
Vue won't detect its changes, also in the Vue Developer tools the string won't get updated. But in the input field it does get updated. So this should be a reactivity thing.
When I add a new character into the input field (v-model) it does update again. So it's just when I update the string by this.lastElement it won't register its changes.
The thing is that the input fields are dynamic, so I don't know how many input fields are here and how many lists etc. So I need Vue to re-render the variable after the value of lastElement is updated.
Edit
I just tried it with an #focus here an example
<input v-model="testVar" #focus="lastElement = testVar">
If I update lastElement later on it doesn't update it for testVar but just for lastElement.
Changing values in DOM elements programmatically does not cause DOM events to fire. v-model relies on input (or change when using .lazy) events to update its bound variable. If you dispatch those events when you update the value in an input, the variable will react.
new Vue({
el: '#app',
data: {
items: ['one','two','three']
},
methods: {
addAddress() {
this.lastElement.value += 'address';
this.lastElement.dispatchEvent(new Event('input'));
this.lastElement.dispatchEvent(new Event('change'));
},
focusChanged(event) {
this.lastElement = event.target;
}
}
})
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div v-for="item, index in items">
<input v-model="items[index]" #focus="focusChanged">
{{item}}
</div>
<button type="button" #click="addAddress">+address</button>
</div>
You could add a ref attribute to each of the inputs and use the ref to update their values. For example, an input element could be:
<input v-model="testVar" ref="input1" id="input1" #focus="focusChanged">
In your methods:
methods: {
focusChanged(event) {
if (event.target.id !== 'variable-search') {
this.lastElement = event.target.id;
}
},
}
And where you want to update the value: this.$refs[this.lastElement].value += variable;

Angular2 remove click event binding after first click

In my application I have a button with a click even on it:
<button class="btn btn-default" (click)="doSomething()">
From within the doSomething method, is there any way to remove the (click) event from the button (so the user can't trigger the functionnality anymore?).
I tried to set a disabled prop on the button but it doesn't change Angular2 behavior.
I tryed to use (click)="doSomething($event) and then
doSomething($event) {
// My method logic goes here
...
...
console.log('Method Logic');
//Attempt to overwrite click event
let target = event.target || event.srcElement || event.currentTarget;
this.renderer.listen(target, 'click', (event) => {
console.log('clic block');
});
}
But It doesn't "replace" the click event. So after that, on click, both original logic and the "click block" console log are triggered!
Method 1:
You can set a boolean variable, so if the user calls the function, boolean value changes and the user will be able to call the function on click again but actually nothing will happen.
bool: boolean = true;
doSomething($event) {
if (this.bool) {
// My method logic goes here
...
...
console.log('Method Logic');
this.bool = false;
}
}
Method 2:
You can add a condition to your html component, if specified variable (in this case bool) is true, the function is going to be executed, but only once because the bool variable will be set to false and the click function will execute nothing (null) from now on.
bool: boolean = true;
doSomething($event) {
// My method logic goes here
...
...
console.log('Method Logic');
this.bool = false;
}
(click)="bool ? doSomething($event) : null"
The downside of just adding a guard variable for the execution is that the actual event listener is still in place and will trigger Angular's change detection when clicked, even though it doesn't do anything.
To actually remove the event listener, you have to add it via the component's Renderer. This will return a function that removes the event listener when called:
import {Component, AfterViewInit, Renderer, ViewChild, ElementRef} from '#angular/core';
#Component({
template: `<button #button>...</button>`
})
export class SampleComponent implements AfterViewInit {
#ViewChild('button') button: ElementRef;
private cancelClick: Function;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
this.cancelClick = this.renderer.listen(this.button.nativeElement, 'click',
($event: any) => this.handleClick($event));
}
handleClick($event: any) {
this.cancelClick();
// ...
}
}
If your goal is to just remove the event listener when the event is fired for the first time, this can be implemented as a plugin to Angular's event system. I added it as a part of my ng2-events utility library [source], which lets you now do the following:
<button (once.click)="handleClick($event)">...</button>
For people dealing with Observable / Subject for handling some events :
<button (click)="clickEvents$.next($event)">
class MyComponent {
clickEvents$ = new Subject<MouseEvent>();
firstClick$ = this.clickEvents.take(1); // Observable of first click
}

Returning true doesn't allow default action in Knockout "event" binding

According to the Knockout docs for the "event" binding:
By default, Knockout will prevent the event from taking any default action.
....
However, if you do want to let the default action proceed, just return true from your event handler function.
This doesn't seem to be working for me. Here's my view:
<input data-bind="event: { 'keydown': function(d, e) { onInputKeydown(e); } }" />
and the event handler in my ViewModel:
onInputKeydown = function(e) {
console.log(e.which);
return true;
}
This handler is preventing any characters from being entered into the <input> element. What am I doing wrong?
The function in your ViewModel is indeed returning true, but the wrapper that you specify in your view isn't propagating the return value of that function. Try this (note the addition of return):
<input data-bind="event: { 'keydown': function(d, e) { return onInputKeydown(e); } }" />
I think the issue is how you bind the function. Please try the following instead:
<input data-bind="event: { keydown: onInputKeydown }" />
Knockout will pass the parameter data and event to your function.
onInputKeydown = function(data, event) {
console.log(event.which);
return true;
}
Yes it is true that you need to return true from your function. I also found that if I hit a breakpoint in my function, the keydown event seems to ignore that I had returned true. Disable all breakpoints and it works fine.

How to prevent property change on Angular JS

I'm using AngularJs on my project and i've a property on my viewModel that is connected to a dropdown (< select >)
that dropdown have a empty value witch is selected by default, what i want is to prevent user to select that empty value after he select some other value.
ive started to look to $watch, but i dont know if there is some way to cancel the "changing oof that property", some thing like this:
$scope.$watch('myProp', function (newVal, oldVal, scope) {
if (newVal) { scope.preventDefault(); }
}
any idea, this is the base idea, on a more advanced development i need to ask users for a confirmation.
any ideas?
what i want is to prevent user to select that empty value after he select some other value
This should happen automatically for you, as long as you don't assign the ng-model property a value initially. So using the <select> shown below, don't initialize $scope.selected_year in your controller:
<select ng-model="selected_year" ng-options="year for year in years"></select>
When the list displays initially, Angular will have added an option like this to the HTML, since $scope.selected_year is not currently set to a valid option/value:
<option value="?" selected="selected"></option>
After selecting a valid choice, that option will magically disappear, so the user will not be able to select it again. Try it in this fiddle.
If the ng-model property already has a valid value assigned when the select list is first displayed, then you can assign a controller function to the undocumented ng-change parameter:
<select ... ng-change="preventUserFromDoingXzy()">
Inside function preventUserFromDoingXzy() you can do what you need to do to control what the user can select, or modify the model.
You can just add ng-required to the select.
If there is no initial value to the model then an empty option will be added and on change to a valid value it will remove the empty option
EDITED jsFiddle to revert to previous value and to include the ng-change directive.
From the docs:
The expression is not evaluated when the value change is coming from the model.
This is useful in not interfering with change listeners and creating an infinite loop when reverting the old value in the $apply function
Controller
$scope.options = [{value: 'abc'},{value: 'def'}];
var confirmDialog = function(newVal, yes, no) {
// obviously not a good way to ask for the user to confirm
// replace this with a non blocking dialog
//the timeout is only for the confirm since it's blocking the angular $digest
setTimeout(function() {
c = confirm('Is it ok? [' + newVal.value + ']');
if(c) {
yes();
}
else {
no();
}
}, 0);
};
//Asking for confirmation example
function Ctrl($scope) {
$scope.options = [{value: 'abc'},{value: 'def'}];
$scope.select = undefined;
var oldSelect = undefined;
$scope.confirmChange = function(select) {
if(oldSelect) {
confirmDialog(select,
function() {
oldSelect = select;
},
function() {
$scope.$apply(function() {$scope.select = oldSelect;});
});
}
else {
oldSelect = select;
}
}
}
Template
<div ng-controller="Ctrl">
<select ng-model="select" ng-options="o.value for o in options"
ng-required ng-change="confirmChange(select)">
</select>
</div>
Probably the easiest, cleanest thing to do would be adding an initial option and setting disabled on it:
<option value="?" selected="selected" disabled></option>
Actually, it is easier to remove the empty value. Suppose you have a list of options:
$scope.options = [{value: ''}, {value: 'abc'},{value: 'def'}];
and a select:
<select ng-model="select" ng-options="o.value for o in options"></select>
Then $watch the model:
$scope.$watch('select', function(value) {
if (value && value !== '') {
if ($scope.options[0].value === '') {
$scope.options = $scope.options.slice(1);
}
}
}, true);
See it in action here.
PS Don't forget the objectEquality parameter in the $watch or it won't work!

Categories

Resources