Something like a manual refresh is needed angularjs, and a $digest() iterations error
- by Tony Ennis
(post edited again, new comments follow this line)
I'm changing the title of this posting since it was misleading - I was trying to fix a symptom.
I was unable to figure out why the code was breaking with a $digest() iterations error. A plunk of my code worked fine. I was totally stuck, so I decided to make my code a little more Angular-like. One anti-pattern I had implemented was to hide my model behind my controller by adding getters/setters to the controller. I tore all that out and instead put the model into the $scope since I had read that was proper Angular.
To my surprise, the $digest() iterations error went away. I do not exactly know why and I do not have the intestinal fortitude to put the old code back and figure it out. I surmise that by involving the controller in the get/put of the data I added a dependency under the hood. I do not understand it.
edit #2 ends here.
(post edited, see EDIT below)
I was working through my first Error: 10 $digest() iterations reached. Aborting! error today.
I solved it this way:
<div ng-init="lineItems = ctrl.getLineItems()">
<tr ng-repeat="r in lineItems">
<td>{{r.text}}</td>
<td>...</td>
<td>{{r.price | currency}}</td>
</tr
</div>
Now a new issue has arisen - the line items I'm producing can be modified by another control on the page. It's a text box for a promo code. The promo code adds a discount to the lineItem array. It would show up if I could ng-repeat over ctrl.getLineItems().
Since the ng-repeat is looking at a static variable, not the actual model, it doesn't see that the real line items have changed and thus the promotional discount doesn't get displayed until I refresh the browser.
Here's the HTML for the promo code:
<input type="text" name="promo" ng-model="ctrl.promoCode"/>
<button ng-click="ctrl.applyPromoCode()">apply promo code</button>
The input tag is writing the value to the model. The bg-click in the button is invoking a function that will apply the code. This could change the data behind the lineItems.
I have been advised to use $scope.apply(...). However, since this is applied as a matter of course by ng-click is isn't going to do anything. Indeed, if I add it to ctrl.applyPromoCode(), I get an error since an .apply() is already in progress.
I'm at a loss.
EDIT
The issue above is probably the result of me fixing of symptom, not a problem. Here is the original HTML that was dying with the 10 $digest() iterations error.
<table>
<tr ng-repeat="r in ctrl.getLineItems()">
<td>{{r.text}}</td>
<td>...</td>
<td>{{r.price | currency}}</td>
</tr>
</table>
The ctrl.getLineItems() function doesn't do much but invoke a model. I decided to keep the model out of the HTML as much as I could.
this.getLineItems = function() {
var total = 0;
this.lineItems = [];
this.lineItems.push({text:"Your quilt will be "+sizes[this.size].block_size+" squares", price:sizes[this.size].price});
total = sizes[this.size].price;
this.lineItems.push({text: threads[this.thread].narrative, price:threads[this.thread].price});
total = total + threads[this.thread].price;
if (this.sashing) {
this.lineItems.push({text:"Add sashing", price: this.getSashingPrice()});
total = total + sizes[this.size].sashing;
}
else {
this.lineItems.push({text:"No sashing", price:0});
}
if(isNaN(this.promo)) {
this.lineItems.push({text:"No promo code", price:0});
}
else {
this.lineItems.push({text:"Promo code", price: promos[this.promo].price});
total = total + promos[this.promo].price;
}
this.lineItems.push({text:"Shipping", price:this.shipping});
total = total + this.shipping;
this.lineItems.push({text:"Order Total", price:total});
return this.lineItems;
};
And the model code assembled an array of objects based upon the items selected. I'll abbreviate the class as it croaks as long as the array has a row.
function OrderModel() {
this.lineItems = []; // Result of the lineItems call
...
this.getLineItems = function() {
var total = 0;
this.lineItems = [];
...
this.lineItems.push({text:"Order Total", price:total});
return this.lineItems;
};
}