Kai
Kai

Reputation: 2073

Two-way binding is not woking

I have a problem binding data with angular.
My model:

<div id="ng-app-MyApp">
    <ng-view></ng-view>
</div> 

controller:

angular.module("MyApp.controllers", []).
controller("myController", function($scope, service) {
    $scope.someprop = "12";
    $scope.nameFilter = "text here";
    $scope.val = "";

    setTimeout(function() {
        $scope.val = "321";
    }, 1000);

    $.when(service.getItems()).done(function (items) {
        console.log(items.length);
        $scope.someprop = "hello";
    });
}); 

index.html:

<div>
    <input type="text" ng-model="nameFilter" />
    <div>{{nameFilter}}</div>
    <table>
        <tr>
            <td>{{someprop}}</td>
            <td>{{val}}</td>
        </tr>
    </table>
</div> 

I use manual binding (angular.bootstrap($("#ng-app-MyApp")[0], ["MyApp"]);). service.getItems() returns jQuery deferred object. When page loads, I expect that value of someprop become "hello" and val will be equals to "321". But actually nothing happens. When I start typing into textbox, then data binding is performed and I can see that someprop="hello" and val="321" on UI.

I can't figure out, why this properties are not updated automatically, but only when I start typing into textbox?

Upvotes: 1

Views: 84

Answers (3)

Brian Noah
Brian Noah

Reputation: 2972

Both items aren't being set because they have missed the digest. Everything you want to use databinding with must be done with a service.

To override this here's what you need to do:

setTimeout(function() {
    $scope.$apply(function () {
        $scope.val = "321";
    });
}, 1000);

$.when(service.getItems()).done(function (items) {
    console.log(items.length);
    $scope.$apply(function () {
        $scope.someprop = "hello";
    });
});

Upvotes: 0

Jossef Harush Kadouri
Jossef Harush Kadouri

Reputation: 34207

add $scope.$apply()

  $.when(service.getItems()).done(function (items) {
        console.log(items.length);
        $scope.someprop = "hello";
        $scope.$apply();
    });

You need to call it because you are handling the callback outside of angular's framework (jquery) and the event of refreshing the dependencies does not get raised.

$apply is the function that 'reloads/refreshes' the bindings


if possible, i recommend you to try avoiding jQuery in your angular code

if service.getItems() is a simple ajax request, use $http angular's service instead of jQuery and edit your custom service like this:

service:

...
service.getItems = function()
{
   return $http.get('<url>');
};
...

controller:

service.getItems()
   .success(function(data)
         {
            console.log(items.length);
            $scope.someprop = "hello";
         });

note that using pure angular, you don't need to add $apply()

Upvotes: 1

maurycy
maurycy

Reputation: 8465

You miss the $digest process which is responsible for two-way data binding in angularjs (although why do you bother to use jQuery for promises when you can achieve exactly the same with pure angularjs)

to avoid that you can do it several ways

$.when(service.getItems()).done(function (items) {
  console.log(items.length);
  $scope.apply(function(){
    $scope.someprop = "hello";
  })
});

$.when(service.getItems()).done(function (items) {
  console.log(items.length);
  $timeout(function(){
    $scope.someprop = "hello";
  })
});

$.when(service.getItems()).done(function (items) {
  console.log(items.length);
  $scope.someprop = "hello";
  $scope.$digest()
});

although the last one is a bit tricky, if the app is big and digest happens more often it might throw an error digest already in progress

Upvotes: 1

Related Questions