4

In my web app i'm reciving data every 3-4 seconds from an AJAX call to API like this:

$http.get('api/invoice/collecting').success(function(data) {
    $scope.invoices = data
}

Then displaying the data, like this: http://jsfiddle.net/geUe2/1/

The problem is that every time i do $scope.invoices = data ng-repeat rebuilds the DOM area which is presented in the jsfiddle, and i lose all <input> values.

I've tried to do:

  • angular.extend()
  • deep version of jQuery.extend
  • some other merging\extending\deep copying functions

but they can't handle the situation like this:

On my client a have [invoice1, invoice2, invoice3] and server sends me [invoice1, invoice3]. So i need invoice2 to be deleted from the view.

What are the ways to solve this problem?

1
  • which version of angular you are using Commented Aug 30, 2013 at 13:34

3 Answers 3

1

Check the ng-repeat docs Angular.js - Data from AJAX request as a ng-repeat collection You could use track by option:

variable in expression track by tracking_expression – You can also provide an optional tracking function which can be used to associate the objects in the collection with the DOM elements. If no tracking function is specified the ng-repeat associates elements by identity in the collection. It is an error to have more than one tracking function to resolve to the same key. (This would mean that two distinct objects are mapped to the same DOM element, which is not possible.) Filters should be applied to the expression, before specifying a tracking expression.

For example: item in items track by item.id is a typical pattern when the items come from the database. In this case the object identity does not matter. Two objects are considered equivalent as long as their id property is same.

Sign up to request clarification or add additional context in comments.

1 Comment

Thats defenitly what i needed! Solves my problem. I've used 1.0.8 angular.js version, so i switched it to 1.1.5 unstable branch. Please update your answer, this feature was introduced in 1.1.x versions.
0

You need to collect data from DOM when an update from the server arrives. Save whatever data is relevant (it could be only the input values) and don't forget to include the identifier for the data object, such as data._id. All of this should be saved in a temporary object such as $scope.oldInvoices.

Then after collecting it from DOM, re-update the DOM with the new data (the way you are doing right now) $scope.invoices = data. Now, use underscore.js _.findWhere to locate if your data._id is present in the new data update, and if so - re-assign (you can use Angular.extend here) the data-value that you saved to the relevant invoice.

Comments

0

Came out, that @luacassus 's answer about track by option of ng-repeat directive was very helpful but didn't solve my problem. track by function was adding new invoices coming from server, but some problem with clearing inactive invoices occured. So, this my solution of the problem:

function change(scope, newData) {
    if (!scope.invoices) {
        scope.invoices = [];
        jQuery.extend(true, scope.invoices, newData)
    }
    // Search and update from server invoices that are presented in scope.invoices
    for( var i = 0; i < scope.invoices.length; i++){
        var isInvoiceFound = false;
        for( var j = 0; j < newData.length; j++) {
            if( scope.invoices[i] && scope.invoices[i].id && scope.invoices[i].id == newData[j].id ) {
                isInvoiceFound = true;
                jQuery.extend(true, scope.invoices[i], newData[j])
            }
        }
        if( !isInvoiceFound ) scope.invoices.splice(i, 1);
    }
    // Search and add invoices that came form server, but are nor presented in scope.invoices
    for( var j = 0; j <  newData.length; j++){
        var isInvoiceFound = false;
        for( var i = 0; i < scope.invoices.length; i++) {
            if( scope.invoices[i] && scope.invoices[i].id && scope.invoices[i].id == newData[j].id ) {
                isInvoiceFound = true;
            }
        }
        if( !isInvoiceFound ) scope.invoices.push(newData[j]);
    }

}

In my web app i'm using jQuery's .extend() . There's some good alternative in lo-dash library.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.