Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding empty elements on page load, but not afterwards using knockout.js

I have a form with a lot of inputs.

I am using the following syntax within my form: <!-- ko if: PropertyName -->. I am using this statement in the form for specific fields. This allows me to hide values that are not defined (actually not hide, but remove from DOM).

However, I do not need to hide them on the fly. I mean, when value was not empty and was loaded, then user can edit it, and user can empty it. In this case input disappears, I do not need this.

Can you suggest me – how to change my markup and what binding to use?

<!-- ko if: IsEmptyOnLoad(Property1) -->.
<input type="text" data-bind="value: Property1" />
<!-- /ko -->

<!-- ko if: IsEmptyOnLoad (Property2) -->.
<input type="text" data-bind="value: Property2" />
<!-- /ko -->

var myModel = function() {
    var self = this;

    self.Property1= ko.observable("non-empty");
    self.Property2= ko.observable();

    //self.IsEmptyOnLoad  is not implemented, how to implement?
};

var m = new myModel();
ko.applyBindings(m);

You can try playing with the corresponding JSFiddle.

It could be strange, but I really have business scenario:

  • onload do not show any empty variables, so after page is loaded there would be only non-empty variables loaded into the page
  • after page is loaded, user can edit variables, some of those variables can be removed (become empty), but in this case I do not need to hide empty variables
like image 959
renathy Avatar asked Dec 06 '13 13:12

renathy


2 Answers

Just create custom binding with empty update method. You can do whatever you want at binding init on page load (at knockout binding apply to be more specific).

ko.bindingHandlers.ifOnce = {
    init: function(element, valueAccessor) {
        var observable = valueAccessor(); // get observable
        var value = observable(); // get value of observable
        var isEmpty = !value; // do whatever check you want
        // and remove element from dom if empty
        if (isEmpty) {
            element.parentNode.removeChild(element);
        }
    },
    update: function(element, valueAccessor) {
        // do nothing on update
    }
};

Working example: http://jsfiddle.net/2FTEM/6/

Lets go one step further. You asked how to create IsEmptyOnLoad. You can do this by using Knockout virtual elements and some useful methods they have. I.e.: ko.virtualElements.emptyNode will remove everything between Knockout <!-- ko --> tags http://knockoutjs.com/documentation/custom-bindings-for-virtual-elements.html

ko.bindingHandlers.IsEmptyOnLoad = {
    init: function(element, valueAccessor) {
        var observable = valueAccessor(); // get observable
        var value = observable(); // get value of observable
        var isEmpty = !value; // do whatever check you want
        // and remove element from dom if empty
        if (isEmpty) {
            ko.virtualElements.emptyNode(element);
        }
    },
    update: function(element, valueAccessor) {
        // do nothing on update
    }
};
ko.virtualElements.allowedBindings.IsEmptyOnLoad = true;

Working example: http://jsfiddle.net/2FTEM/7/

like image 162
Wojciech Czerniak Avatar answered Oct 26 '22 08:10

Wojciech Czerniak


An interesting problem that has so far generated some inventive answers. It's a common trend that people seem to have a phobia of putting logic into the view-model. As the name suggests, it should be designed as an interface between the view and the model. Firstly, I would re-factor the view-model, something like this;

var myModel = function() {
    var self = this;
    var property = function (content, availibility) {
        return { 
            content: ko.observable(content),
            availibility: availibility
       };
    }
    self.Property1 = property('non-empty', true);
    self.Property2 = property();
};

Now your HTML can be simple;

<input type="text" data-bind="visible: Property1.availibility, value: Property1.content" />
<input type="text" data-bind="visible: Property2.availibility, value: Property2.content" />

I've also provided a fork of the JSFiddle for your reference

UPDATE: Re-factored for simplicity and minimal repetition.

like image 45
Michael Papworth Avatar answered Oct 26 '22 08:10

Michael Papworth