Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knockout.js - deferred databinding for modal?

I am using knockout.js to display a list of employees. I have a single hidden modal markup on the page. When the "details" button for a single employees is clicked, I want to data-bind that employee to the modal popup. I am using the ko.applyBindings(employee, element) but the problem is when the page loads, it is expecting the modal to start off as bound to something.

So I'm wondering, is there a trick/strategy to do a late/deferred databinding? I looked into virtual bindings but the documentation was not helpful enough.

Thanks!

like image 952
Adam Levitt Avatar asked May 16 '12 21:05

Adam Levitt


People also ask

What is deferred in knockout JS?

Deferred – Notifications happen asynchronously, immediately after the current task and generally before any UI redraws. Rate-limited – Notifications happen after the specified period of time (a minimum of 2-10 ms depending on the browser).

How do I assign a value to Knockout observable?

To create an observable, assign the ko. observable function to the variable. A default value can be specified in the constructor of the call. Knockout then converts your variable into a function and tracks when the value changes, in order to notify the UI elements associated with the variable.

What is knockout js used for?

Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model.


1 Answers

I would like to propose a different way to work with modals in MVVVM. In MVVM, the ViewModel is data for the View, and the View is responsible for the UI. If we examine this proposal:

this.detailedEmployee = ko.observable({}),

var self = this;
this.showDetails = function(employee){
    self.detailedEmployee(employee);
    $("#dialog").dialog("show"); //or however your dialog works
}

I strongly agree with this.detailedEmployee = ko.observable({}), but I am in strong disagreement with this line: $("#dialog").dialog("show");. This code is placed in the ViewModel and shows the modal window, wherein fact it is View's responsibility, so we screw-up the MVVM approach. I would say this piece of code will solve your current task but it could cause lots of problems in future.

  • When closing the popup, you should set detailedEmployee to undefined to have your main ViewModel in a consistent state.
  • When closing the popup, you might want to have validation and the possibility to discard the close operation when you want to use another modal's component in the application

As for me, these points are very critical, so I would like to propose a different way. If we "forget" that you need to display data in popup, binding with could solve your issue.

this.detailedEmployee = ko.observable(undefined);
var self = this;
this.showDetails = function(employee){
    self.detailedEmployee(employee);
}

<div data-bind="with: detailedEmployee">
Data to show
</div>

As you can see, your ViewModel don't know anything about how data should be shown. It knows only about data that should be shown. The with binding will display content only when detailedEmployee is defined. Next, we should find a binding similar to with but one that will display content in the popup. Let's give it the name modal. Its code is like this:

ko.bindingHandlers['modal'] = {
    init: function(element) {
        $(element).modal('init');
        return ko.bindingHandlers['with'].init.apply(this, arguments);
    },
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var returnValue = ko.bindingHandlers['with'].update.apply(this, arguments);

        if (value) {
            $(element).modal('show');
        } else {
            $(element).modal('hide');
        }

        return returnValue;
    }
};

As you can see, it uses the with plugin internally, and shows or hide a popup depending on value passed to binding. If it is defined - 'show'. If not - 'hide'. Its usage will be the as with:

<div data-bind="modal: detailedEmployee">
    Data to show
</div>

The only thing you need to do is to use your favorite modals plugin. I prepared an example with the Twitter Bootstrap popup component: http://jsfiddle.net/euvNr/embedded/result/

In this example, custom binding is a bit more powerful; you could subscribe the onBeforeClose event and cancel this event if needed. Hope this helps.

like image 113
Romanych Avatar answered Oct 20 '22 13:10

Romanych