0

I discovered an issue using knockout bindings in HTML partials. I have a container region on a page. Its content is meant to be replaced dynamically dictated by my application's logic. I employ a detach/append technique to do it. However I noticed that updates are not propagated when element is detached. Not only that, in some cases bindings are lost altogether and when element is attached back, update stops working.
Updating data is important in my case because while an element may not be visible, it still could be updated asynchronously.
I found threads on the stackoverflow pointing to the same problem. However I couldn't find a definitive answer. There were various workarounds that didn't look appealing (e.g. using private knockout methods). The only way for me to make things work as intended is to show/hide partials instead of detach/append.
Is this behavior by design (I mean knockout library design)? If so, I wouldn't want to circumvent it by introducing various workarounds leading to potentially more problems down the road. If not, what is the recommended pattern of use for append/detach in knockout to make it work just like show/hide (I'd rather use detach/append because main application uses this policy for all of its containers).

Here's jsbin link to reproduce the problem. I also included show/ hide sample for comparison: http://jsbin.com/iTIrigAp/3/edit?html,js,output

<h3>Attach/Detach</h3>

<p>issue 1: Detach/Update/Attach/Update</p>
<p>issue 2: Run with JS/Update/Enter new val/Detach/Update/Attach</p>

<div id="attdet">
    <input type="text" data-bind="value: a" />
    <input type="text" data-bind="value: b" />
</div>

<button id="detach">Detach</button>
<button id="update">Update</button>
<button id="attach">Attach</button>


function ViewModel() {
        this.a = ko.observable("a");
        this.b = ko.observable("b");

        this.c = ko.observable("c");
        this.d = ko.observable("d");     
    }

    var vm = new ViewModel();

    ko.applyBindings(vm, document.getElementById("attdet"));


    var nodes = null;

    $("#detach").click(function () {
        nodes = $("#attdet").children().detach();
    });

    $("#attach").click(function () {
        $("#attdet").append(nodes);
    });

    $("#update").click(function () {
        vm.a("att-det");
        vm.b("att-det");
    });

1 Answers1

1

If you're just hiding and showing nodes, use the knockout visible binding:

The visible binding causes the associated DOM element to become hidden or visible according to the value you pass to the binding.

or the CSS binding to tie a true/false value to it:

The css binding adds or removes one or more named CSS classes to the associated DOM element. This is useful, for example, to highlight some value in red if it becomes negative.

or, probably the easiest way, use jQuery's show/hide events.

Your problem is that detach literally removes the items from the DOM. While it does store the events and values of the fields, it is outside Knockout's ability to recognize the parameters and it will re-evaluate when it comes back into the DOM.

Show/hide on the other hand, simply removes them from the display, which seems to be your actual intent.

Sources:

Community
  • 1
  • 1
Organiccat
  • 5,633
  • 17
  • 57
  • 104
  • If you really want to attach/detach (e.g. to avoid building a huge DOM) then you can use the `if`/`ifnot` bindings, which will allow Knockout to add/remove DOM sections while keeping track of the bound values. – ebohlman Feb 04 '14 at 10:34