Something like a manual refresh is needed angularjs, and a $digest() iterations error

Posted by Tony Ennis on Stack Overflow See other posts from Stack Overflow or by Tony Ennis
Published on 2014-06-09T23:06:40Z Indexed on 2014/06/13 15:25 UTC
Read the original article Hit count: 253

Filed under:

(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;
    };
}

© Stack Overflow or respective owner

Related posts about angularjs