2

I want to bind Angular model to a Knockout component. It's working - when the Angular model changes the Knockout binding applies. But the problem is in the opposite direction - I want the Angular model to update when the Knockout component changes, and do so without touching the component (it mustn't know about the Angular wrapper). The target - using Angular to build a rapid prototyping framework around KO components, which are being used in other environments too. The KO components can and should share models (which come from the Angular wrapper) therefore this question.

EDIT: Here is a jsFiddle example to showcase what I'm trying to achieve. Its a simplification because in real-world KO components will use internal VM's which will be difficult to $watch. But even here I don't get why the $watch doesn't work.

var sharedData = {
    personName: "alex",
    personAge: "32"
};


function WrapperCtrl($scope){
    $.each(sharedData, function(key, value) {
        sharedData[key] = (typeof value !== "function") ? ko.observable(value) : value;
    });

    $scope.wrapData = sharedData;

    ko.applyBindings(sharedData, document.getElementById("ko_1"));  
    ko.applyBindings(sharedData, document.getElementById("ko_2"));  

    $scope.$watch(
        function () {
            return sharedData.personName();
        }, 
        function(newValue, oldValue) {
            console.log("change");
        }
    );

    $scope.doSomething = function(){
        console.log("before angular function: ", $scope.wrapData.personName(),    $scope.wrapData.personAge())
        sharedData.personName('Bob').personAge(41);
         console.log("after angular function: ", $scope.wrapData.personName(), $scope.wrapData.personAge())
    };
}


function doSomething() {

    console.log("before knockout function", sharedData.personName(), sharedData.personAge())
    sharedData.personName('Mary').personAge(25);
    console.log("after knockout function", sharedData.personName(), sharedData.personAge())
}
krulik
  • 976
  • 1
  • 10
  • 31

1 Answers1

0

If you aren't allowed to directly insert angular bindings into your components, then you won't be able to fire off your digest cycle, and so angular will have no idea your data source has updated. One way of tackling this is to periodically call $apply, which will update your model with any external changes, like this:

    setInterval($scope.$apply, 500);

http://jsfiddle.net/HmcnB/11/

B Cotter
  • 951
  • 5
  • 7
  • What about variable scope? How can I $watch the data inside the KO component? – krulik Oct 15 '13 at 17:37
  • Ah sorry, my mistake - as your knockout components have to be completely separate from your angular - there's no way for angular to know it needs to perform a digest cycle, so it won't fire your watchers. One (slightly crude) way of handling this, is to call a periodic $apply, which will update your scope model with changes. http://jsfiddle.net/HmcnB/11/ – B Cotter Oct 16 '13 at 13:47
  • Thanks, it really sounds impossible otherwise. Isn't $watch itself performs some kind of periodic check though? Maybe I can somehow live with global data, but even then my $watch doesn't fire in the fiddle when the sharedData changes. – krulik Oct 16 '13 at 14:03
  • $watch only updates when angular performs one of its own internal digest cycles - this only occurs when something actually happens within angular. As your changes are completely external to angular (i.e. what you're watching isn't on the $scope), it can't fire. – B Cotter Oct 16 '13 at 14:07
  • So its a deadlock - I'm waiting for a digest cycle to know when to execute a digest cycle. I'm thinking now about attaching some custom binding to the KO components which will plug them to Angular but will be unobtrusive to their internals (there is perhaps a possibility to extend the KO components to support this if generic enough). Do you want to update your answer so I will accept it? Thanks again. – krulik Oct 16 '13 at 15:02