James Lin
James Lin

Reputation: 26528

knockoutjs data bind of something doesn't exist yet

Lets say I have a list of items, and the data is created by mapping plugin, and it's normal that the list is empty. I would like to have a form that performs edit and create actions, which should bind to a single selected item.

<div id="appointment-modal" class="reveal-modal" data-bind="with: appointment">
    <form id="appointment-form" data-bind="submit: submit_appointment">
        <input type="text" name="name" data-bind="value: name"/>
        <textarea name="description" data-bind="value: description"></textarea>
        <input type="text" name="time" data-bind="value time" />
        <input type="text" name="address1" data-bind="value: address1"/>
        <input type="text" name="address2" data-bind="value: address2"/>
        <input type="text" name="phone" data-bind="value: phone"/>
        <input type="text" name="email" data-bind="value: email"/>
    </form>
</div>

The problem is, I get errors complaining about appointment is not defined:

Uncaught ReferenceError: Unable to parse bindings.
Bindings value: with: appointment
Message: appointment is not defined

I might be able to use the "if" binding to check if appointment exists, but I don't know how to cater for creation, ideally I would like to reuse the same form.

Came across this link, I can just create a dummy obervable "appointment", but since appointment is dummy, the div renders nothing, this is not good particularly for creating new appointment item.

Upvotes: 0

Views: 1892

Answers (2)

James Lin
James Lin

Reputation: 26528

I have come up a workaround, which instead of defining an observable, define an observableArray()

In the template, instead of bind the observable like this:

<div data-bind="with: listing">
  <div data-bind="text: title">
</div>

You bind like looping through an array:

<div data-bind="foreach: listing">
  <div data-bind="text: title">
</div>

So the template won't complain about fields are not yet mapped.

To populate the listing into the template, just like this using mapping plugin:

vmodel.listing.removeAll();
vmodel.listing.push(ko.mapping.fromJS(listing_json));

Upvotes: 3

nwayve
nwayve

Reputation: 2331

Right, you need to define the property, even if it's null (fiddle - this is what you're experiencing). You just need to, as you've discovered, define the property with a null value (the default value for observables) and just add the appointment object to it later (fiddle). This is perfectly acceptable.

function ViewModel() {
    var self = this;
    self.appointment = ko.observable();// Don't set any value
}

This is one way to do a "late binding". If later, you add some new html to the DOM, you can call ko.applyBindingsToDescendants(myViewModel, $("#parentDiv")[0]). But in your particular case, I'd probably have the self.appointment property defined ahead of time.

Update:

Create an object with all the properties defined and bind it like this: fiddle. Then when it's submitted, clear all the fields.

Upvotes: 0

Related Questions