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 (bee empty), but in this case I do not need to hide empty variables
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 (bee empty), but in this case I do not need to hide empty variables
- I usually simply hide the div containing the bound UI items and showing some kind of "is loading..." instead. – Rob Commented Dec 6, 2013 at 13:26
- Please create a fiddle. As it stands this is extremely easy to acplish multiple ways and should be trivial to do... – PW Kad Commented Dec 6, 2013 at 13:30
- jsfiddle/2FTEM – renathy Commented Dec 6, 2013 at 13:36
- It is not related to "is loading". – renathy Commented Dec 6, 2013 at 14:19
- 2 Awwh, you've let the bounty slide. Though I was under the impression that you have some good answers here. What do you miss? – flup Commented Dec 17, 2013 at 14:28
5 Answers
Reset to default 1Just 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/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./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/2FTEM/7/
An interesting problem that has so far generated some inventive answers. It's a mon 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.
Do you simply want to disable the additional field if the FirstName
is empty? I can't see how hiding the first input field, as you are currently doing, will work as it will not allow anyone to enter a name once it disappears.
If you want to disable additional fields and leave the first filed you can use data-bind
with disable
to do the following and remove your <!-- ko if: .... -->
statements:
<input type="text" data-bind="value: FirstName" />
<input type="text" data-bind="disable: FirstName().length === 0" />
See updated fiddle: http://jsfiddle/2FTEM/1/
UPDATE
You can set up an observable that updates once your view model is loaded: IsPageLoaded
that you set to true. I've added a delay in the JS so you should see the control appear after the final lines update the observable: m.IsPageLoaded(true);
. You may have to check this with your code to see if it works (without the setTimeout
).
HTML:
<!-- ko if: IsNotEmpty -->.
<input type="text" data-bind="value: IsNotEmpty" />
<!-- /ko -->
<!-- ko if: IsEmpty && IsPageLoaded -->.
<input type="text" data-bind="value: ''" />
<!-- /ko -->
JS:
var myModel = function() {
var self = this;
self.IsPageLoaded = ko.observable(false);
self.IsNotEmpty = ko.observable("non-empty");
self.IsEmpty = ko.observable(true);
};
var m = new myModel();
ko.applyBindings(m);
// remove this timeout to test in your enviroment - just introduces a delay
setTimeout(function() {
m.IsPageLoaded(true);
}, 1000);
// simply use
// m.IsPageLoaded(true);
Updated Fiddle: http://jsfiddle/2FTEM/4/
Keep two copies of your model around: the version as it was on load and the version that's currently being edited.
var m = { onload : new myModel(),
current : new myModel() };
ko.applyBindings(m);
When binding, bind the visibility to the state on load and the editable value to the current state:
<!-- ko if: onload.Property1 -->.
<input type="text"
data-bind="value: current.Property1" />
<!-- /ko -->
http://jsfiddle/92GzK/
You can create normal variables (instead of observables) that are only used in the "if" statements. This way, the condition within the "if" won't get updated if you update the field :
self.Property1 = ko.observable("non-empty");
self.Property2 = ko.observable();
self.initialValue1 = self.Property1();
self.initialValue2 = self.Property2();
and the bindings are :
<!-- ko if: initialValue1 -->.
<input type="text" data-bind="value: Property1" />
<!-- /ko -->
<!-- ko if: initialValue2 -->.
<input type="text" data-bind="value: Property2" />
<!-- /ko -->
fiddle here : http://jsfiddle/2FTEM/13/ Hope that helps