Value-binding without overwriting the element value property

414 views
Skip to first unread message

jun...@gmail.com

unread,
Jul 26, 2011, 3:25:14 PM7/26/11
to knock...@googlegroups.com
I followed the introductory tutorial (http://learn.knockoutjs.com/#/?tutorial=intro) and I have a question:

If I put my HTML view like this:

<p>First name: <input id="firstName" value="Reinaldo" data-bind="value: firstName"  /></p>
<p>Last name: <input id="lastName" value="Junior" data-bind="value: lastName"  /></p>

and my viewModel like this:

var viewModel = {
    firstName: ko.observable(""),
    lastName: ko.observable("") 
};


The bind will always replace the value of each input with the viewModel values? How can I make my viewModels to set observables and initialize their values with the default value in the input?

Will I have to make something like this?

var viewModel = {
    firstName: ko.observable($('#firstName').val()),
    lastName: ko.observable($('#lastName').val()) 
};

I think that is a bad idea because it makes the viewModel depending on the HTML view.


Thank you!

Arafat M

unread,
Jul 26, 2011, 3:34:35 PM7/26/11
to KnockoutJS
Why not just do

var viewModel = {
firstName: ko.observable("Reinaldo"),
lastName: ko.observable("Junior")
};

jun...@gmail.com

unread,
Jul 26, 2011, 3:36:39 PM7/26/11
to knock...@googlegroups.com
Because I'm trying to use it on a Rails app (for example), and the form HTML is generated value properties.

rpn

unread,
Jul 26, 2011, 3:38:25 PM7/26/11
to knock...@googlegroups.com
Hello-
I have suggested a custom binding for something like this before, if you are not able to put the values in yourself.  It would look something like:

//create an observable set to the current field's value.  Rewrite data-bind to use real value binding
ko.bindingHandlers.valueWithInit {
    initfunction(elementvalueAccessorallBindingsAccessorcontext{
        var property valueAccessor();
        var value $(element).val();
        
        //create the observable, if it doesn't exist
        if (!ko.isWriteableObservable(context[property]){
            context[propertyko.observable();   
        }
        context[property](value);
        
        //rewrite data-bind to use real value binding (would need to be more robust, if attribute contains other bindings).  Otherwise, we could certainly just call the value binding's init and update function with a new valueAccessor that returns the new observable.
        $(element).attr("data-bind""value: " property );
        ko.applyBindings(contextelement);
    }
};

So, the idea is that it creates an observable on your view model, if necessary, and then populates it with your value and then converts the field to just use the normal value binding.

Here is this working with your data: http://jsfiddle.net/rniemeyer/kjqfN/.  In this case, firstName exists on the viewModel and lastName does not, but both are popualted correctly.


jeroen...@gmail.com

unread,
Aug 1, 2011, 8:31:47 AM8/1/11
to KnockoutJS
Thank you for your solution. While working with it, I enhanced it to
the following:

ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindingsAccessor,
context) {
var property = valueAccessor();
var value = $(element).val();

//create the observable, if it doesn't exist
if (!ko.isWriteableObservable(context[property])) {
context[property] = ko.observable();
}
context[property](value);
ko.bindingHandlers.value.init(element, valueAccessor,
allBindingsAccessor, context);
},
update : ko.bindingHandlers.value.update
};

rpn

unread,
Aug 1, 2011, 9:31:18 AM8/1/11
to knock...@googlegroups.com
Hello-
You might want to be careful with how you pass the valueAccessor on to the real value binding in this case.  You would want to make sure that you pass the underlying observable and not the string value of the property name.

Something like this:

ko.bindingHandlers.valueWithInit {
    initfunction(elementvalueAccessorallBindingsAccessorcontext{
        var property valueAccessor();
        var value $(element).val();

        //create the observable, if it doesn't exist 
        if (!ko.isWriteableObservable(context[property]){
            context[propertyko.observable();
        }
        context[property](value);
        ko.bindingHandlers.value.init(elementfunction({
            return context[property];
        }allBindingsAccessorcontext);
    },
    updatefunction(elementvalueAccessorallBindingsAccessorcontext{
        var property valueAccessor();
        ko.bindingHandlers.value.update(elementfunction(return context[property]},allBindingsAccessor);
    }
};

janm...@gmail.com

unread,
Apr 3, 2012, 3:13:53 PM4/3/12
to knock...@googlegroups.com
Guys,

my biggest issue with all these approaches is that you need to declare the binding property with single quotes: <input id="firstName" value="Reinaldo" data-bind="valueWithInit: 'firstName'"  />

Is there any way to achieve the same effect without the single quotes? I understand that the issue lies within the scope of the context and its properties, but perhaps there's some magic that can be done to overcome this? Thanks!

rpn

unread,
Apr 3, 2012, 4:16:32 PM4/3/12
to knock...@googlegroups.com
It is kind of tough, because the binding will error out before getting into the actual binding handler if you try to bind against an undefined value.

You would need to write some code that parses the attributes and initializing any properties prior to calling applyBindings.  This might get difficult if you have nested structures.

Another thought would be to use a custom binding provider that wraps the real one.  You could parse the binding each time in the provider and see if you need to initialize any properties before actually returning the binding.

Here is a sample where it looks for a "data-initialize" attribute, initializes the property, and removes the attribute so it doesn't have to do as much work next time: http://jsfiddle.net/rniemeyer/5G8wE/

janm...@gmail.com

unread,
Apr 3, 2012, 5:15:08 PM4/3/12
to knock...@googlegroups.com
But basically the issue exists only when I try to bind a property that doesn't exist on the view model, right? If I don't want the auto-creation of properties, it should be ok to use valueAccess() directly, no?

rpn

unread,
Apr 3, 2012, 5:35:10 PM4/3/12
to knock...@googlegroups.com
Yes, if the observable exists already, then in "init" you could read the value and use it to populate it.  As simple as:  http://jsfiddle.net/rniemeyer/S2kJA/.  If you want to be able to bind/write to non-observables, then it would get more complicated.

janm...@gmail.com

unread,
Apr 3, 2012, 5:41:38 PM4/3/12
to knock...@googlegroups.com
Thank you!

I came to a similar solution, with the exception of wrapping "ko.bindingHandlers.value.update" in valueWithInit's update method. I think your solution is more elegant though, so I'll use that.

Андрей Клаус

unread,
Jun 7, 2014, 11:40:50 AM6/7/14
to knock...@googlegroups.com
Hello guys,

I know this post is little old, but anyway I think this is best place for question.

How to implement the same thing valueWithInit (I already have observable for sure) http://jsfiddle.net/rniemeyer/S2kJA/  but for "checked" binding?

Thank you,
Andrey.


Андрей Клаус

unread,
Jun 9, 2014, 8:29:42 AM6/9/14
to knock...@googlegroups.com
for now I have code like this

    ko.bindingHandlers.checkedWithInit = {
        init
: function(element, valueAccessor, allBindingsAccessor, vm, bindingContext) {
           
var observable = valueAccessor()
           
           
if(element.type == "checkbox")
                observable
(element.checked)                
           
else if(element.checked)
                observable
(element.value)

            ko
.applyBindingsToNode(element, { checked: observable }, allBindingsAccessor, vm, bindingContext)
       
}
   
}

looks like it works.

Reply all
Reply to author
Forward
0 new messages