0

The goal

Read content from DOM element with KnockoutJS.

The problem

I have a list of products in my HTML. The code is something like this:

<li>
    <div class="introduction">
        <h3>@Model["ProductName"]</h3>
    </div>
    <form data-bind="submit: addToSummary">
        <input type="number" 
               placeholder="How much @Model["ProductName"] do you want?" />
        <button>Add product</button>
    </form>
</li>

When I click on <button>Add Product</button>, I want to send to KnockoutJS the text inside <h3></h3> of the element that was submitted.

The file to work with KnockoutJS is external and independent of HTML. It name is summary.js and the code is below:

function ProductReservation(id, name, unity, quantity) {
    var self = this;

    self.id = id;
    self.name = name;
    self.unity = unity;
    self.quantity = quantity;
}

function SummaryViewModel() {
    var self = this;

    self.products = ko.observableArray([
        new ProductReservation(1, "Testing", "kgs", 1)
    ]);

    self.addToSummary = function () {
        // Do Something
    }
}

What I'm thinking about

HTML:

<li>
    <div class="introduction">
        <h3 data-bind="text: productName">@Model["ProductName"]</h3>
    </div>
    [...]
</li>

JS:

productName = ko.observable("text: productName");

But, of course, no success — this is not the correct context or syntax, was just to illustrate.

So I ask: what I need to do?

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
Guilherme Oderdenge
  • 4,935
  • 6
  • 61
  • 96

2 Answers2

1

One strategy that has worked well in my experience is to implement a toJSON extension method that serializes your model (if you use a library like JSON.NET you can have a lot of control over what gets serialized and what does not).

Inside of the view where you initialize KnockoutJS, you could serialize your model and pass it into the KnockoutJS ViewModel you're creating (assuming :

Main view:

<script type="text/javascript">
    var viewModel = new MyViewModel(@Model.ToJSON());
</script>

ViewModel:

function MyViewModel(options) {
    this.productName = ko.observable(options.ProductName);
}
Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • Hi, @AndrewWhitaker. Thanks about your answer. I have implemented the `toJSON` extension method but in my view (Razor) the `.ToJSON()` method doesn't appear. I already have compiled the code. Any idea? – Guilherme Oderdenge Jun 24 '13 at 12:33
  • You might have to add a reference to your namespace in your web.config. See [this question](http://stackoverflow.com/q/5052752/497356) for more information. Does that help? – Andrew Whitaker Jun 24 '13 at 13:23
1

You're binding addToSummary via the submit binding. By default KO sends the form element to submit-bound functions.

So addToSummary should have a parameter -

self.addToSummary = function (formElement) {
    // Do Something
}

You can pass additional parameters to this function (as described in KO's click binding documentation under 'Note 2'), or you could just add a hidden field to your form element and pull it from there.

<li>
    <div class="introduction">
        <h3>@Model["ProductName"]</h3>
    </div>
    <form data-bind="submit: addToSummary">
        <input type="number" name="quantity" 
               placeholder="How much @Model["ProductName"] do you want?" />
        <input type="hidden" name="productName" value="@Model["ProductName"]" />
        <button>Add product</button>
    </form>
</li>

Then in your knockout code you could use jQuery to process the form element to pull out the data -

self.addToSummary = function (formElement) {
    var productName = $(formElement).children('[name="productName"]')[0].val();
    var quantity= $(formElement).children('[name="quantity"]')[0].val();
    // ...
    self.products.push(new ProductReservation(?, productName, ?, quantity));
}
Nate
  • 16,748
  • 5
  • 45
  • 59