Update DOM only when $scope data changes - javascript

In my angular js app, I noticed that the DOM updates any time I call a function. This happens even when the function did not change any $scope variable.
I'm using an ng-repeat to create a set of checkboxes as shown below.
HTML
<a ng-click="someFunction()" >Some Function</a>
<form action="/settings/save">
<label>
<input ng-repeat="option in settings.active" name="options[]" value="{{option}}" checked="checked" />
{{option}}
</label>
<input type="submit" value="Save" />
</form>
JS
angular.module("myapp")
.controller("settingsCtrl", function($scope, $loadServ){
//$loadServ is a service for fetching data from the server
$loadServ("/settings/load")
.success(function(response){$scope.settings = response.data});
$scope.someVariable = "something";
$scope.someFunction = function(){
//There's nothing here yet
}
//More code follows
})
I noticed that all the unchecked checkboxes are checked when the "Some Function" button is clicked. I inspected the DOM and realised that all the checkboxes were re-rendered when the button was clicked.
Is there a way to update the DOM only when the $scope changes?
Note: I can't use one way binding because the $scope.settings.active can be changed by some other function

I've found the source of the error. It was caused by a filter that was applied far above the nodes

Related

Angular Reactive form: Unable to bind checkbox checked property on page load

In Angular Reactive forms i want Two-way data binding of Checkbox, after checking it i am dispatching check variable it to the store and after page load i am getting the new updated value also. But that checkbox isdateChkd variable even though its coming TRUE checkbox is not getting auto checked on page load.
Below are both HTML and ts files..
HTML File
<form [formGroup]="form">
<div class="abc" formControlName="inputDate">
<div class="xyz">
<p-calendar formControlName="inputDate"
[(ngModel)]="inputDateValue"
class="abc"/>
<input type="checkbox" id="checkbox-1" class="abc" name="dateChk"
name="dateIsChecked" [(ngModel)]="isdateChkd" (change)="chkFunction($event.target.value)" />
</div>
</div>
</form>
Component.ts file
public isdateChkd: boolean = false;
ngOnInit() {
....Other service calls
// Call to get isdateChkd value
this.store.select(getValues).takeUntil(this.ngUnsubscribe).subscribe((data) => {
this.isdateChkd = data.defaultUpdate // this value holds boolean to be used
});
}
chkFunction(valule) {
this.isdateChkd = value; // i get update when checkbox is checked
this.store.dispatch(new updateCheckboxCheckedService(value)); // after its checked dispatching it to the store
}
Thanks in advance for help...
You can use checked attribute along with NgModel.
It's working here with checked along with the value isdateChkd. You can also check here in this stackBlitz
Please try to make changes like below code
...
<input type="checkbox" id="checkbox-1" class="abc" name="dateIsChecked"
[(ngModel)]="isdateChkd" (change)="chkFunction($event)" checked="isdateChkd" />
...
Happy Coding.. :)

Fire ng-change when ng-model updated programmatically inside ng-repeat

I read topics related to this and found following, but still I am stuck.
How to $watch changes on models created by ng-repeat?
I am trying to implement plus, minus counter with following code. Now whenever ng-model updates either by clicking +/- button I want to fire ng-change. I understand that ng-change doesn't fire when the ng-model updated programatically. But the refrenced Stackoverflow has solution for that to add $watch on the ng-model. But its working if I create separate controller, not working inside current controller. ng-change is calling functions inside current controller. How to fix it??
<div class="input-group" ng-repeat="product in oc.itemdetail.lines">
<input type="button" value="-" class="minus"
ng-click="product.line.total = product.line.total - product.stepCount">
<input type="number" ng-if="condition 1" ng-model="product.line.total" min="" max=""
step="product.stepCount" oninput="some condition"
ng-change="oc.function1()" />
<input type="number" ng-if="condition 2" ng-model="product.line.total" min="" max=""
step="product.stepCount" oninput="some condition"
ng-change="oc.function2()" />
<input type="button" value="+" class="plus"
ng-click="product.line.total = product.line.total + product.stepCount">
</div>
I have two input box above, based on ng-if condition either one will be shown at a time. Each ng-change is calling different function inside current controller. If I put separate controller then on click of +/- button following function is firing. But I want to call function inside ng-change
$scope.$watch('Count', function (oldValue, newValue) {
if (newValue != oldValue)
console.log(oldValue, newValue);
});
The code should avoid using the onchange attribute with the ng-model directive. The ng-if directive uses alot of resources. If the only reason it is being used is to switch the ng-change function, it should be avoided. The choice of ng-change function can be made in the controller.
<div class="input-group" ng-repeat="product in oc.itemdetail.lines">
<input type="button" value="-" class="minus"
ng-click="oc.decrement(product)">
<input type="number" ng-model="product.line.total" min="" max=""
step="product.stepCount" ng-change="oc.update(product)" />
<input type="button" value="+" class="plus"
ng-click="oc.increment(product)">
</div>
Avoid complex formulations in templates. Instead put them in the controller:
oc.update = function(product) {
if (oc.<condition 1>) {
oc.function1();
};
if (oc.<condition 2>) {
oc.function2();
};
};
For performance reasons, avoid using watchers. Instead use the functions that change the model to invoke update operations:
oc.decrement = function(product) {
product.line.total = product.line.total - product.stepCount;
oc.update(product);
};
oc.increment = function(product) {
product.line.total = product.line.total + product.stepCount;
oc.update(product);
};
Putting complex expressions in the controller makes the code easier to understand, debug, test, and maintain.
You can try by moving this 'Count = Count + s.increment' code on the ng-click event into a function in the JS file and in that function you can call ct.function2().
eg:-
$ct.Counter = function(){
Count = Count + s.increment;
ct.function2();
}
and call this method on ng-click
ng-click="ct.Counter()"
Similarly you can implement the minus scenario.

ng-change not fired from checkbox inside label

I have some HTML like this:
<input ng-controller="cboxCtrl" type="checkbox"
ng-model="hideCompleted" ng-change="hideChanged()"/>
and a controller like this:
angular.module('simple-todos').controller('cboxCtrl', ['$scope',
function ($scope) {
console.log("starting");
$scope.hideChanged = function () {
console.log("in hideChanged() ");
};
}]); // end controller
It works fine and I see the message on the console when I click the checkbox. However, if I add a label around the checkbox:
<label>
<input ng-controller="cboxCtrl" type="checkbox"
ng-model="hideCompleted" ng-change="hideChanged()"/>
Some text to explain the checkbox
</label>
The ng-change function does not run when I click the checkbox. I expect it has something to do with scoping but I cannot figure out what. If I replace labels with divs (which of course does not give a "nice" laĆ½out), the ng-change function is again executed as expected.
I just created a jsfiddle with your code and it works for me.
https://jsfiddle.net/jfplataroti/hphb8c4v/5/
angular.module('simple-todos', []).controller('cboxCtrl', ['$scope', cboxCtrl]);
function cboxCtrl($scope) {
console.log("starting");
$scope.hideChanged = function() {
console.log("in hideChanged() ");
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simple-todos">
<label>
<input ng-controller="cboxCtrl" type="checkbox" ng-model="hideCompleted" ng-change="hideChanged()" />some text for the label
</label>
</div>
would you mind sharing your complete code?
clicking on label also triggers ng-change, you can click the label of a checkbox/radio to check/uncheck It should trigger the scoped function.
Please check for the browser you are trying to run this code. Also do check it on other browsers you have installed. AngularJS no longer supports IE8 or earlier.
link here: https://docs.angularjs.org/guide/ie
You need to move your ng-change on the checkbox to the surrounding label's ng-click
<label ng-click="hideChanged()">
<input ng-controller="cboxCtrl" type="checkbox" ng-model="hideCompleted" />
Some text to explain the checkbox
</label>

JQuery selecting radio button using a variable

I'm trying to select a radio button using a variable but, although I can print the selected value the button does not stay selected.
This button is defined in an angular template as follows:
<a ng-click="selectNode($event, node)">
<input type="radio" name="library" style="float:left;" id="id-{{n}}" />
</a>
Where id is a GUID so is definitely unique.
Then, because it is the angular click event that is called when a button is selected I call this code in selectNode:
context.selectedNodes = [node];
$scope.selectedNode = node.$model.name;
var id = '#id-' + node.$model.id;
//console.log(typeof id);
//console.log(id);
$(id).prop("checked", true);
alert($("[name=library]:checked").val())
Which should theoretically check the radio button. However, it checks for a moment (it is checked when I call the alert) and then seems to disappear. I am however able to hardcode in an id and it stays checked.
I recommend diving more into the tools angular gives you to handle this things simply. If you need to track the value on this input, tie it to a model. If you want to process something once the checkbox has changed, use ng-change and tie it to a function.
<input type="radio" name="library" style="float:left;" ng-model=libraryModel ng-change="doSomething()" />
Keep in mind that doSomething() needs to be in scope, so in your controller, you need to define the function as $scope.doSomething(). Your model will also be attached to the $scope object, so you'd reference it as $scope.libraryModel. You can initialize the model as true or false as needed for a default value in the controller, then any user changes will auto update this value.
You're mixing jQuery and Angularjs, and it is not really worth. You can do the following just by using AngularJS ngModel, if you use AngularJs in your project.
Controller
function Controller($scope) {
//Init data input to value 1
$scope.data = ['1'];
}
HTML
<input type="radio" name="name" value="1" ng-model="data">
<input type="radio" name="name" value="2" ng-model="data">
Here, our $scope.data is set to 1 in our Controller, so it will match with the first input.

Hidden fields in AngularJs

How do I access hidden fields in angular? I have an app, where I want to submit a form for each of items in the list. The form is simple - it has submit button and a hidden field holding the ID value. But it does not work. The value is empty.
I updated the default angular example to display the situation - the todo text is in hidden field.
http://jsfiddle.net/tomasfejfar/yFrze/
If you don't want to hardcode anything in your javascript file, you can either load it via AJAX, or do:
<input type="hidden" name="value" ng-init="model.value=1" value="1">
this way, you can keep the form functionality with JS off, and still use the hidden field in AngularJS
If you want to pass the ID from the ng-repeat to your code, you don't have to use a hidden field. Here's what I did:
For example, let's say I'm looping through a collection of movies, and when you click the "read more" link it will pass your ID to your JS code:
<ul>
<li ng-repeat="movie in movies">
{{movie.id}} {{movie.title}} read more
</li>
</ul>
Then in your JS code, you can get the ID like this:
$scope.movieDetails = function (movie) {
var movieID = movie.id;
}
In your simpler fiddle, the problem can be fixed by using ng-init or setting an initial value in the controller. The value attribute won't effect the ng-model.
http://jsfiddle.net/andytjoslin/DkMyP/2/
Also, your initial example (http://jsfiddle.net/tomasfejfar/yFrze/) works for me in its current state on Chrome 15/Windows 7.
You can do something like this.
It is a dirty trick, but it works (like most dirty tricks ;-)
You just use the form name as Your hidden field
and always give the form the id "form"
<!doctype html><html ng-app><head>
<script src="angular-1.0.1.min.js"></script>
<script>
function FormController($scope) {
$scope.processForm = function() {alert("processForm() called.");
$scope.formData.bar = "";
try {$scope.formData.bar = document.getElementById("form").name;}
catch(e) {alert(e.message);}
alert("foo="+$scope.formData.foo+ " bar="+$scope.formData.bar);
};
}
</script></head><body>
<div ng-controller="FormController">
<form name="YourHiddenValueHere" id="form">
<input type="text" ng-model="formData.foo" />
<button ng-click="processForm()"> SUBMIT </button>
</form>
</div></body></html>
This allows You to use ONE Controller for ALL forms and send
them to ONE server script.
The script than distinguishes by the
form name (formData.foo) and knows what to do.
The hidden field names the operation in this scenario.
Voila - You have a complete application with as
many forms You want and one server script
and one FormController for all of them.
Simpler:
<input type="hidden" name="livraisonID" value="{{livraison.id}}"/>
It works!
Use ng-binding="{{employee.data}}". It will work properly.
I have to correct (improve) myself:
You can do it more elegantly:
<form>
<input type="text" ng-model="formData.foo" />
<input type="hidden" id="bar" value="YourHiddenValue" />
<button ng-click="processForm()"> SUBMIT </button>
</form>
and then in the JavaScript controller:
$scope.formData.bar = "";
try {$scope.formData.bar = document.getElementById("bar").value;}
catch(e) {alert(e.message);}
alert("foo="+$scope.formData.foo+ " bar="+$scope.formData.bar);
So you can have as many hidden fields as you like.

Categories