Search code examples
javascriptarrayswebknockout.jsobserver-pattern

Knockout : context changed before the instruction in observable.subscribe


I'm trying to separate my Model from my ViewModel since I need the Model in two different ViewModel. So i've tried to this :

var ConversationManager = {
    conversations: ko.observableArray([
        {id: 3, receiver:'Toto'}
    ])
};

function ConversationDialogViewModel(){
    var self = this;

    ConversationManager.conversations.subscribe(function(context){
        console.log('Before :', context); // context.test == true ???

        for(var i = 0; i < context.length; i++){
            if(context[i].id % 2 == 0 && !context[i].test){
                console.log('Insertion Called');
                context[i].test = true;
            }
        }
        console.log('After :', context);
    });

    ConversationManager.conversations.push({id: 4, receiver:'Jean'});
    ConversationManager.conversations.push({id: 5, receiver:'Paul'});
    ConversationManager.conversations.push({id: 6, receiver:'Bleu'});
}

Somehow, in the first console.log, when i push "Jean", in the browser, the variable "test" exists and is correctly set to true even before i actually tell JS to do it. How is that possible ? And, am I correctly separating my concerns ?


Solution

  • When the console references an object (where you can click an arrow to open it and look at its contents), it is still a reference, not a snapshot in time. As you modify context, it doesn't matter which reference to it in the debugger you look at, they will all be the same. You need to convert it to something that is static (or put in a breakpoint) if you want to see it at a moment in time.

    var ConversationManager = {
        conversations: ko.observableArray([
            {id: 3, receiver:'Toto'}
        ])
    };
    
    function ConversationDialogViewModel(){
        var self = this;
    
        ConversationManager.conversations.subscribe(function(context){
            console.log('Before :', JSON.stringify(context)); // context.test == true ???
    
            for(var i = 0; i < context.length; i++){
                if(context[i].id % 2 == 0 && !context[i].test){
                    console.log('Insertion Called');
                    context[i].test = true;
                }
            }
            console.log('After :', JSON.stringify(context));
        });
    
        ConversationManager.conversations.push({id: 4, receiver:'Jean'});
        ConversationManager.conversations.push({id: 5, receiver:'Paul'});
        ConversationManager.conversations.push({id: 6, receiver:'Bleu'});
    }
    
    new ConversationDialogViewModel();
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>