0

I'm having this odd issue when I update my viewmodel...basically with every update, there appears to be a random chance that each observable will contain this data:

function observable() {    
        if (arguments.length > 0) {    
            // Write               

            // Ignore writes if the value hasn't changed    
            if ((!observable['equalityComparer']) || !observable['equalityComparer'](_latestValue, arguments[0])) {    
                observable.valueWillMutate();    
                _latestValue = arguments[0];    
                observable.valueHasMutated();    
            }

            return this; // Permits chained assignments    
        } else {    
            // Read    
            ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation

            return _latestValue;
        }    
    }

I've been using KnockoutJS for a while, and I've never seen anything like this. My guess is that it has something to do with my template binding, but I'm really not sure. I'm going to dig into it, but I figured I'd post it here in case anyone else is having this issue, or has a solution. Like I said, it doesn't happen consistently, only on occasion.

//// More Information ////

So Matt below referenced this (http://stackoverflow.com/questions/9763211/option-text-becomes-a-function-string-after-updated-with-fromjs), which is roughly the same issue. The only difference is that I'm using the native template binding in a style like this:

<div data-bind="template: {name: 'issueTemplate', data: incidents}"></div>

<script id="dashboardIssueTemplate" type="text/html">       
    <!--ko foreach: $data-->    
    <div data-bind="text: title"></div>
</script>

It was my assumption that KnockoutJS handled the unwrapping by itself when you pass the observableArray into the template binder. I know I can't say "title()" in this example, because that doesn't exist. Am I supposed to be binding with a command like $root.title()?

//// Even More Information ////

It appears that this problem occurs as a result of having two "applyBindings" on one page. My application contains an external widget which adds it's DOM to the host page DOM at runtime. That widget is using the ko.applyBindings(vm, ROOTNODE) syntax which should allow for the host page to run it's own ko.applyBindings(hostVm).

In fact, it does, and it works correctly every refresh. The problem however is when the host page does a viewModel update with no refresh. Somehow, the UI rendering spits out this internal function on EVERY data-bound node. I've debugged through KnockoutJS and actually confirmed that the viewModel and rootNode are correct...something outside of the actual binding is taking over.

farina
  • 3,486
  • 6
  • 32
  • 44
  • 1
    if it has to do with your binding, you may want to post an example of how you're binding (preferably code where you've seen the error happen) – deltree Mar 19 '12 at 17:36
  • Agreed. A jsfiddle (http://jsfiddle.net/) is a good way to go. – James Sulak Mar 19 '12 at 17:45
  • Ya, I normally would, but I'm deep in this project and I can't really share the code per company policy :(. I'll see if I can't get it reproduced in jsfiddle. – farina Mar 19 '12 at 17:59
  • Actually, I'm thinking this may have more to do with my other question [http://stackoverflow.com/questions/9775073/knockoutjs-proper-way-to-update-observablearray-ajax] It seems to happen the instant the new data comes into the observable array after I've cleared it out. I think I'm going to play with the mapping plugin and see how to do this properly. – farina Mar 19 '12 at 18:00
  • Nevermind...doesn't look like the mapping plugin does what I need because it actually replaces the entire viewmodel. I thought it just created an observableArray :(. – farina Mar 19 '12 at 18:06
  • Is this http://stackoverflow.com/questions/9763211/option-text-becomes-a-function-string-after-updated-with-fromjs similar? – Matt Burland Mar 19 '12 at 18:40
  • Good find...that's basically the same thing except I'm having the issue right inside the general template binding. So I'm not breaking it out into javascript, I'm just depending on KnockoutJS to pull the value from the viewmodel. I'm going to update the question. – farina Mar 19 '12 at 20:20
  • I've tried to reproduce this in jsfiddle, and I cannot get it to fail...even using the same code :(. I'll keep working. I really think it has something more to do with how I'm updating the data. I'll post back if I figure it out. Thanks for all of the help! – farina Mar 19 '12 at 21:11
  • OK, I figured out this is actually tied to a different thing I've got going on on this page. Basically, this page loads in a "widget" which also uses KnockoutJS. That widget binds using a style like I described here (http://stackoverflow.com/questions/9729718/knockoutjs-double-binding-widget-application) that casts the binding down from a root node. It turns out that when I remove the widget, my problem is gone. I can't go around having this consumable widget break everyone else's KnockoutJS, so I'm going to have to come up with some kind of solution. – farina Mar 19 '12 at 21:51

1 Answers1

1

This has something to do with the "()" appended onto the data object in the template. What I've found is that during the first render (page load) writing the template like this:

<div data-bind="template: {name: 'issueTemplate', data: incidents}"></div>

<script id="dashboardIssueTemplate" type="text/html">       
    <div data-bind="text: title"></div>
</script>

works just fine. However, once you run the update on the observableArray my "title" object becomes that function. If I write the template using this style:

<div data-bind="text: title()"></div>

It seems to work on every update.

I am not certain why this is the solution. From the looks of it, the data object being passed to the Knockout binder is the exact same on both page load and update. I'll post this as an answer, but I'm not marking it as an answer until I understand why this is happening.

farina
  • 3,486
  • 6
  • 32
  • 44