AngularJS : Detect form changes - javascript

I'm developing an angular app, where I have to check the form changes, and perform some operation depending upon the changes. I know, this can be achieved using $scope.watch but that would cost me performance.
What I'm trying to achieve
I've a pre-populated form and I will take the form data as a reference using angular.copy() and compare with original data.
I had also tried $parsers to check, but failed. So, is there any way to accomplish this?

You can use ng-change.
From the docs - "Evaluate the given expression when the user changes the input. The expression is evaluated immediately, unlike the JavaScript onchange event which only triggers at the end of a change (usually, when the user leaves the form element or presses the return key)."
Difference between ng-change and $watch

Did you try with ng-change ? It call a function when value of ng-model change
<input type="search" ng-change="changeValue()" ng-model="test" name="test"/>
If you want to know wich field call the function you can add the name as string parameter
<input type="search" ng-change="changeValue('field1')" ng-model="field1" name="field1"/>
In the controller don't forget to put the function changeValue in the scope
$scope.changeValue = function(fieldname){
switch (fieldname){
case 'field1' : .... break;
case 'field2' : .... break;
case 'field3' : .... break;
...
}
}

When you compare the copied object against the original form data, and you want to check for changes, you can use angular.equals.
angular.equals(formObj, formObjCopy)
This will do a deep compare of the object and its nested sub-properties, and return true if the object hasn't changed.

Related

check if input/value in b-form-selected

I have a b-form-select and when I choose one option I want to enable my button. But I don't know how to check if there is a value in my selection.
Thanks for helping me out !
<b-form-select v-model="data.Name" :value="data.Name"></b-form-select>
<b-button :disabled="!validDataAdded"></b-button>
computed: {
validDataAdded: function(){
return //Check here
},
}
In order to answer that question first we need to make some things clear.
v-model uses the :value attribute under the hood as a way to communicate with the component and pass down values. That been said you dont need to add a :value attribute to your select component. If you need to set a initial value you simply need to set an initial value to your v-model.
If you want to check if there is a value to your selection you just need to check if the v-model has a value. Remember that a v-model corresponds to a property (either part of data or computed section).
In you example you need to check if data.Name contains a value or not.
Check out this sandbox where I create an example for you according you code-snippet.
Last but not least the way that you have written your v-model it seems that you have an object called data inside your data property

What is the performance of checking an attribute vs checking a return value of a function?

I have a list of items in a component:
list: Array<MyType>;
The user can select and deselect elements on click:
toggleItem(item: MyType) {
if (this.selection.has(item)) {
this.selection.delete(item);
return;
}
this.selection.add(item);
}
The selected items are stored in a Set:
selected: Set<MyType> = new Set();
Now I need to toggle a CSS class and a title-attribute depending whether an element is selected or not:
<button class="toggle"
type="button"
[ngClass]="{'selected': selection.has(item)}"
[title]="selection.has(item) ? 'Select' : 'Deselect'"
(click)="toggleItem(item)">
{{ item.title }}
</button>
Now I've read somewhere that it is a bad idea, to evaluate function calls as Angular periodically will call them for change detection, like here:
[ngClass]="{'selected': selection.has(item)}"
They say that it's better to check a variable or a member of the object instead, like:
[ngClass]="{'selected': item.selected}"
Is is true and does it decrease performance the way I use it currently? Should I add a property to each item that is set when it's added to or removed from the Set?
Whenever Angular performs change detection, it checks whether anything that's variable and is in the template has changed.
Now, the check is pretty straight forward in case of model variables as Angular can simply read values of them in order to check for a change.
But that's not really the case with a function. With functions, Angular can only determine if the variable has changed by calling the function itself.
Now it doesn't make much of a difference if the function is a one-liner return of a value.
But in case the function has complex logic, it will essentially kill all the performance. As every time the change detection cycle runs, Angular will call that function in order to check for changes and detect them.
Hence, it's not really recommended to write functions in data-binding syntax in templates.
NOTE: I've also written a Medium Article about this. I'd suggest you check it out. It's mainly about Angular Reactive Forms performance. But it will help you better understand what I said above.

Changing `ng-required` calls `ng-change`

I have a strange behavior in my app using AngularJS 1.5.8:
plunker (https://plnkr.co/edit/zaHVJeK8hdxk2gaOL9Pf?p=preview) and video(http://recordit.co/eszvfdfC9S)
step 1. At the beginning changing ng-required doesn't call ng-change function
step 2. After making changes in input AND removing them (input is empty) ng-required DOES call ng-change function
expected behavior?
step 2. After making changes in input AND removing them (input is empty) ng-required SHOULD NOT call ng-change function. As it was at the beginning, and as it is when input has some value
Please let me know if it's a bug or not. If not then why changing ng-required calls ng-change NOT always or even at all?
ANSWER IS FOUND-------------------------------------------------------------------------------------------------------
NgModelController has two properties: $viewValue (value entered by
user) and $modelValue (value bound to your model). When the value
entered by the user fails validation, NgModelController sets the model
to undefined.
In AngularJS 1.3, they added the ng-model-options directive. It lets you
configure how/when the model gets updated. You can use the
allowInvalid option to prevent your model from being set to undefined:
ng-model-options="{allowInvalid: true}"
You should add
ng-model-options="{allowInvalid: true}"
So the final result will be
<input type="text"
ng-change="$ctrl.onChange()"
ng-required="$ctrl.isRequired"
ng-model-options="{allowInvalid: true}"
ng-model="$ctrl.val"
/>
This is happening because the ng-required is changing the attached modal value to undefined from blank when the required is set to false, due to this ng-change is fired since the modal changes.
Check in the plunker i have console.log the value of input and you can see the change of modal clearly.
angular.
module('myApp', []).
component('greetUser', {
templateUrl: 'tmpl.html',
controller: function GreetUserController() {
this.output='';
this.isRequired = false;
console.log(this.val);
this.onChange = function() {
console.log(this.val);
this.output+='Changed\n';
}
}
});
plunker : https://plnkr.co/edit/6IeIjIDahcmBIU4KSASJ?p=preview
Now the question arises that why not the on load/ first time the change event is not firing up that is because we are using this object rather then $scope.
Here
'this' vs $scope in AngularJS controllers
is a very good example which explains why until we manually enter the value in the input at least once the change event is not firing up.
in short this is happening because ng-change/ng-model works with scope variables. Once you manually enter value in the input element, the model binding happens with the scope, and the ng-change event start firing up.
I think you misunderstanding.
ng-required and ng-change is different things. doing different purpose, and not calling each other.
ng-change is calling your function no matter what it's empty or not. It's call your method by it self, regarding to changes happening in the input.
ng-required is just checking value if it's empty or not, If it is empty, mark it as invalid.
In order to get what you want, you have to check the validity inside the onChange function.
this.onChange = function() {
if( !$scope.$ctrl.form.$valid ) return;
this.output+='Changed\n';
}
I think this is the reason. put an alert inside ng-change like this.
this.onChange = function() {
alert(this.val);
this.output+='Changed\n';
}
When you empty the box after completing it the value change between two values:
undefined when is required
'' when is not required
So By changing the radio box you call the ng-change,
At the beginning you have
However when you have not started to type in the text box , radio box does not change the input value , because ng-change is for input. In the beginning we have undefined --> undefined so nothing changed. then when you empty the input you have '' ---> undefined.
Actually if you put this in your controller you get call ng-change at the beginning too.
this.val ='';
So if you replace your controller with this , you see ng-change is called even at the beginning.
angular.
module('myApp', []).
component('greetUser', {
templateUrl: 'tmpl.html',
controller: function GreetUserController() {
this.output='';
this.isRequired = false;
this.val ='';
this.onChange = function() {
this.output+='Changed\n';
}
}
});

Watch ng-model whilst using minlength?

I'm trying to watch the value of ng-model whilst also using the minlength validation. The problem is the model value remains empty/undefined until the validation criteria is met.
HTML
<input ng-model="xyz" minlength="8" />
JS
$scope.$watch('xyz', function(val) {
// Will either be undefined or a
// string bigger than or equal
// to 8 characters.
console.log(val);
});
I know I could just substring the element's value, but this code is implemented in a directive which uses $compile, so ideally I'd prefer to watch the model value.
Any thoughts on how to resolve this?
https://docs.angularjs.org/api/ng/directive/ngModelOptions
allowInvalid: boolean value which indicates that the model can be set with values that did not validate correctly instead of the default behavior of setting the model to undefined.

Binding a Repeat Control to either a session or view scope variable

I have a repeat control which I have bound to a sessionScope variable and it works fine. I am trying to make the control a bit more extensible by dynamically binding it to either a viewScope or a sessionScope variable. I have added a Custom Property to the Control that holds the repeat and have called it scope. The problem is how to bind the repeat to the correct Scoped variable.
On the Repeat control I have selected JavaScript as the binding method and added this code:
switch (compositeData.scope){
case 'ss' :
return sessionScope.ssCat1
break;
case 'vs' :
return viewScope.vsCat1
break
}
I have checked and in my case compositeData.scope = 'ss' and the sessionScope.ssCat1 contains the correct data.
So am I missing something or is this even possible?
Is it not a better way to inject the repeat values in the custom control by a custom parameter
Name it repeat or something else with object as property type.
In your custom control you have
<xp:repeat id="repeat1" rows="30"
var="rowData" style="width:400px" value="#{compositeData.repeat}">
//do what ever you want with a rowData
</xp:repeat>

Categories