Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoffeeScript,Knockout & observable

I have the following CoffeeScript to generate Javascript for Knockoutjs

class NewsItem
    content: ko.observable("")
    title: ko.observable("")

    constructor: (data,dispForm) ->
        @content data.get_item("content")
        @title data.get_item("title")
        @id = data.get_id()

class NewsItemViewModel
    collection: ko.observableArray()

    loadAll: =>
            listEnumerator = items.getEnumerator()          
            while listEnumerator.moveNext()
                 currentItem = listEnumerator.get_current()
                 @collection.push new NewsItem currentItem, @list.get_defaultDisplayFormUrl()
            return

$ -> 
    viewModel = new NewsItemViewModel
    ko.applyBindings viewModel
    return

To render HTML I use this code

<ul id="results" data-bind="template: {name: 'item_template', foreach: collection}">
</ul>
<script id="item_template" type="text/x-jquery-tmpl"> 
<li>
    <h3><a href="/" data-bind="text: title"></a></h3>
    <p>
        <textarea data-bind="value: content"></textarea>
        <input type="button" value="save" data-bind="enable: content().length > 0">
    </p>
</li>   
</script>

However, in the HTML all items show the values of the last NewsItem added to the collection.

Any hints?

like image 543
nyn3x Avatar asked Jun 27 '12 21:06

nyn3x


1 Answers

Okay, this may be one of the pitfalls that CoffeeScript has:

class NewsItem
    content: ko.observable("")

Here, you're creating a new class with a property "content" that is an observable object. This compiles into the following JavaScript:

var NewsItem = (function() {
    function NewsItem() {}
    NewsItem.prototype.content = ko.observable("");
    return NewsItem;
})();

As you can see now, the property "content" is attached to the prototype. That means: there is only one observable created, not one per instance. So whenever you do new NewsItem, the constructor updates this single observable in the prototype, hence the same value for all instances.

To solve this, simply create the observable in the constructor. That way, it get's attached to the instance, not the prototype:

class NewsItem
    constructor: (data,dispForm) ->
        @content = ko.observable data.get_item("content")

Compiles into (relevant part):

this.content = ko.observable(data.get_item("content"));
like image 50
Niko Avatar answered Oct 23 '22 18:10

Niko