View is not updating in AngularJS - javascript

In my app, i am calculating total bill and displaying on my view. First time its work fine. But when I increment the $scope.bill_total it is not updating the view. But in console it is changing. I have tried $scope.$apply(). But it is throwing error.what are the reasons of view not get updating in general case Can anyone explain it?
HTML
<div id="total" class="col col-30 cart-item text-right text-black">
Bill Total<br/> ${{ bill_total }}
</div>
<button class="button button-small button-light" ng-click="increment()"> +</button>
JavaScript:
$scope.calculate = function () {
$scope.bill_total = parseFloat($scope.gross_bill) + parseFloat($scope.taxes) + parseFloat($scope.tips);
}
$scope.calculate();
$scope.increment = function () {
$scope.gross_bill++;
$scope.calculate();
console.log($scope.bill_total )
}

Need to see more of your code, but why are you updating gross_bill when you are expecting bill_total to change?
If you aren't using gross_bill in your template, it won't be watched and hence changing it's value won't redraw the view.
So, modify things that are bound in your template. If there's some reason I'm wrong and you need to do scope.apply, and maybe that's the case, try wrapping your code in a $timeout which will trigger a digest, is the 'recommended' solution preferred to calling apply directly.
notes on apply vs timeout

as we can only see part of your source code, it looks all good.
to test if everyting is in the same digest scope, you can manually do an async apply:
$scope.increment = function () {
setTimeout(function () {
$scope.gross_bill++;
$scope.calculate();
$scope.$apply();
console.log($scope.bill_total );
});
}
and pls also double check below points:
if bill_total is one-time binding {{ ::bill_total }}
if the directive scope is isolated with one-way binding bill_total

The problem is probably that $scope.bill_total is not an object and therefore cannot be watched. You need to change it to something like $scope.bill_total = {value: 3}. Then when you update the value it should be updated correctly in your view. There should be no reason to call $scope.apply since you are within a digest cycle already.
Make sure to change your html to :
<div id="total" class="col col-30 cart-item text-right text-black">
Bill Total<br/> ${{ bill_total.value }}
</div>

Related

AngularJS loads constructor multiple times

my controller is loaded multiple times in my AngularJS 1.5 code:
<div ng-repeat="conditionForMultipleRows">
<div data-ng-if="$first">
<div co-my-component></div>
</div>
</div>
export function coMyComponent(): ng.IDirective {
return {
template: coMyComponentTemplateHtml,
controller: 'MyComponentController',
controllerAs:'$ctrl'
}
}
export class MyComponentController{
state: MyStateClass;
static $inject = [someServices]
constructor(someServices) {
document.getElementById("myComponent").addEventListener("myEvent", (ev: CustomEvent) => {
doStuff()
}
The HTML Part is only called once, so there should be no issue. Only my controller is loaded multiple times.
The angular.module only loads the controller once and the directive only once, so there is no issue. Also there is no other place where the controller or webcomponent is called in the code.
I'm not very familiar with AngularJS so you can also point out other parts if you see something wrong here. Please refer to a source if it was resolved there. I didnt find any helpful answer
Thanks in advance guys
Each time your directive is instantiated, it will received a brand new controller.
The ng-repeat directive instantiates a template once per item from a collection.
In your case, if conditionForMultipleRows is an array having four items inside, you will instantiate four times the template
<div data-ng-if="$first">
<div co-my-component></div>
</div>
Each template instance gets its own scope and own controller, thus calling the constructor four times.
The answer for my issue here is:
<div ng-repeat="conditionForMultipleRows track by $index ">
<div data-ng-if="$first">
<div co-my-component></div>
</div>
</div>
With adding track by $index, the controller of my component only loaded once.
Also learned that in a ng-repeat it should always be added a "track by". There are only a few egde cases where this isnt required. Correct me if I'm wrong

Assign a value to a variable in the template - Angular7

There are toggle two button (edit and submit), which button should work like toggle show/hide style on click
<button (click)="showEditBtn = false;" *ngIf="showEditBtn;"> Edit</button>
<button (click)="showEditBtn = true;" *ngIf="!showEditBtn;">Submit</button>
I need showEditBtn variable should be true in default without touching script file
Is it possible to assign a value to a variable in the template, like below example?
<div> {{ let showEditBtn = true }} </div>
stackblitz example
Figured out. It is a bit of a hack. But works perfectly
<div *ngIf="true; let showEditBtn">
<div> {{ showEditBtn }} </div>
<button (click)="showEditBtn = false" *ngIf="showEditBtn"> Edit</button>
<button (click)="showEditBtn = true" *ngIf="!showEditBtn">Submit</button>
</div>
You cannot create or set value in a variable inside interpolation {{ }}, interpolation is only used to print the output (value of variable).
Angular Interpolation is a way of data binding in Angular. And it will allow user to communicate between component and it's template (view).
String Interpolation is a one way data binding. In one-way data binding, the value of the Model is inserted into an HTML (DOM) element and there is no way to update the Model from the View.
Hope given link may help to understand well.
I highly recommend to not set or update variables in your template. All of your logic should be in the controller.
here is a simple example of how you can do it
app.component.ts:
public isEditMode: boolean;
public toggleEditMode(): void {
this.isEditMode = !this.isEditMode;
}
app.component.html
<button (click)="toggleEditMode()" *ngIf="isEditMode;"> Edit</button>
<button (click)="toggleEditMode()" *ngIf="!isEditMode;">Submit</button>
There is no way in Angular2+ to initialize the variable value in the template. You must look into the ngOnInit() function.

Using the Battery Manager in angular js

im trying to hook the battery manager object to a angular controller however the controller object doesnt seem to be updating when the promise by navigator.getBattery() complete. here is what i came up with
(function(){
var app=angular.module('appBattery',[]);
app.controller('batteryController',['$window',function($window){
this.bat={};
this.level=this.bat.level;
$window.navigator.getBattery().then(function(battery){
setBattery(battery);
});
function setBattery(battery){
this.bat=battery;
console.log(this.bat);
}
console.log(this.bat);
}]);
})();
with this html
<div ng-app='appBattery'>
<div id="battery-status-bar" ng-controller='batteryController as battery'>
<div class="battery">
<div class="power">
{{battery}}
<div class="level"></div>
</div>
</div>
<div class="percentage">{{battery.bat.level}}</div>
<div class="time">{{battery.bat.chargeTime +':'+battery.bat.dischargeTime}}</div>
</div>
</div>
it can also be found on jsfiddle here
You can keep your controller as syntax. Check out my updated fiddle at https://jsfiddle.net/fnnruzjw/1/. There were a couple of issues at hand:
You needed to use $scope.$apply.
$window.navigator.getBattery().then(function(battery){
$scope.$apply(function() {
setBattery(battery);
});
});
Your this was referring to the wrong this (I changed it to be vm as is a popular convention).
You were losing the reference to your battery object when you were reassigning it. I used angular.copy to keep the reference.
function setBattery(battery){
angular.copy(battery, vm.bat);
console.log(vm.bat);
}
Assign battery to $scope.bat (inject $scope in your controller also). And you will probably need to do $scope.$apply() too. Pretty much replace this with $scope.

ng-click requires two clicks for view to update

I called ng-click like this:
<div ng-repeat="(key,value) in filterdata">
<div ng-click="filter(key,value)">
{{key}} ::::::: {{value}}
</div>
</div>
and my Controller function looks like this:
$scope.filter = function(key,value){
$location.search(key, value);
var filter = $location.url();
service.get(filter).success(function(data) {
$scope.applicationdata = data.data;
console.log($scope.applicationdata);
});
}
and my HTML file has ng-repeat like this:
<div class="resultsa" ng-repeat="data in applicationdata">
{{data.name}}<hr>
<div ng-repeat="metadata in data.metadata">
{{metadata.name}} , {{metadata.pivot.value}}
<hr>
</div>
</div>
The first time I click, my service function is called and my model is updated but my view doesn't update. When I click a second time, my view properly updates. Can anyone tell me why this happening?
This is because you are changing the value of the model after the digest cicle was triggered, and in a different context. For solving this you need to call $scope.$apply() after setting the application data, for triggering another digest cycle.

ng-include is being called far too often

I've discovered that when I use ng-include it gets called far too often.
Every time you click one of the Nothing buttons or change Views, or type something in the input box, getView gets run several times. Up to 6 times when changing views. Basically doing anything that alters the $scope will generate a call to getView.
I've created a plunker to show the behavior I'm describing: plnkr.co
Is this normal behavior and is there any way to make it only run once? I'm worried I may be losing performance due to this.
MY CODE:
index.html
<body ng-app="includeExample">
<div ng-controller="ExampleController">
<div class="row">
<input type="text" ng-model="unrelated">
</div>
<div class="row">
<tabset>
<tab heading="View 1">
<ng-include src="getView('template1')"></ng-include>
</tab>
<tab heading="View 2">
<ng-include src="getView('template2')"></ng-include>
</tab>
</tabset>
</div>
</div>
</body>
Script.js
angular.module('includeExample', ['ngAnimate', 'ui.bootstrap'])
.controller('ExampleController', ['$scope',
function($scope) {
$scope.getView = function(filename) {
console.log("getView " + new Date());
return filename + ".html";
};
}
]);
Template1.html
Content of template1.html
<button type="button" class="btn btn-primary" ng-click="">Nothing</button>
Angular is calling your getView method each time a digest runs to make sure the value hasn't changed. This is part of the "magic" for how angular binds the model and the view together. If you look at the network tab for your development tools you will see that the template is not actually being retrieved by the browser each time the digest runs. Knowing that this is how the digest actually works - you should develop code accordingly running intensive operations in methods that are not directly bound to the view!
For more information on the digest itself see:
http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/
https://docs.angularjs.org/guide/scope (search for "Scope Life Cycle")
https://www.ng-book.com/p/The-Digest-Loop-and-apply/
Hope that helps!
Yeah, angularJS will go through getView() every time you do anything. A good first iteration would be to assign it to a json object instead of using a get method. Actually, it's better if you don't use any get type methods in the html. Then if you really want it to not change, you can remove the 2 way binding (Render value without data-binding).

Categories