Pregunta

I'm trying to learn Knockout and I'm following these two tutorials:
Tutorial 1
Better list example

But after half a day of trying (and failing), I'm not able to add or remove an item.
Here is my fiddle.

Any help would be greatly appreciated!

To clarify
Why doesn't self.items.push() add the new item to my list? Is it because of the self.item_id property?

My HTML is like this:

<input type="hidden" value="" data-bind="value: item_id" />
<input class="form-control" type="search" data-bind="value: item_name" />     
<button class="btn" data-bind="click: addItem, enable: item_name().length > 2">Add</button>

<ul data-bind="foreach: items">     
  // This list is retrieved from database on page load
  <li class="list-group-item">
    <span id="123" data-bind="text: item_name, attr: {'id': item_id}">Americanino</span>
    <span data-bind="click: $parent.removeItem"></span>
  </li>      

  <li class="list-group-item">
    <span id="842" data-bind="text: item_name, attr: {'id': item_id}">Diesel</span>
    <span data-bind="click: $parent.removeItem"></span>
  </li>          
</ul> 

And my JS looks like this:

function item(id, name){
    var self = this;
    self.item_id = ko.observable(id);
    self.item_name = ko.observable(name);    
    //test
    alert(self.item_id() + ' - ' + self.item_name());    
}

var manageListModel = function() {
    var self = this;
    self.items = ko.observableArray();
    self.item_id = ko.observable('345');
    self.item_name = ko.observable();

    self.addItem = function() {     
        if (self.item_name() != "") {      
            self.items.push(new item(self.item_id(),self.item_name()));
            self.item_name(""); 
        }
    }.bind(self); 

    // Remove item
    self.removeItem = function(item) {
        alert('tert');
        self.items.remove(item);
    }
};

ko.applyBindings(new manageListModel());

UPDATE

So after banging my head in the wall for a few hours, I finally found a solution that works pretty well. I hope this code will help other with similar problems :)

So what did I do?

  • I created a template for the list item. If you have 10 items in your list and use ko.observableArray(), the template will consists of 10 items. So for every item you add, you will get 10 in the list.

  • I cloned the existing list since ko.observableArray() removes any static content in the list

  • Looped through my cloned list and put the value-data into an array which I passed into ko.observableArray

Here is my final code:
(You can see the fiddle here)

My HTML:

<input type="hidden" value="" data-bind="value: item_id" />
<input class="form-control" type="search" data-bind="value: item_name" />     
<button class="btn" data-bind="click: addItem, enable: function(){item_name().length > 2}">Add</button>

<ul data-bind="template: { name: 'item-item-template', data: $root.items}">     
  // This list is retrieved from database on page load
  <li class="list-group-item">
    <span id="123" data-bind="text: item_name, attr: {'id': item_id}">Americanino</span>
    <span data-bind="click: $parent.removeItem"></span>
  </li>      

  <li class="list-group-item">
    <span id="842" data-bind="text: item_name, attr: {'id': item_id}">Diesel</span>
    <span data-bind="click: $parent.removeItem"></span>
  </li>          
</ul> 

<script type="text/html" id="item-item-template">
<!--ko foreach: $data-->
    <li>
      <span id="" data-bind="text: item_name, attr: {'id': item_id}"></span>
      <a href="#" class="remove el-icon-remove-circle" data-bind="click: $root.removeitem"></a>
    </li>
<!-- /ko -->
</script>

The JS code

// List item
function brand(name, id) {
    var self = this;
    self.item_name = ko.observable(name);
    self.item_id = ko.observable(id);
}


// Create array of existing list
function create_list(exiting_list){
    var arr_list = [];

    $(exiting_list).find('li').each(function(e,li){
        var id = $(li).find('span').prop('id');
        var name = $(li).find('span').html();
        arr_list.push({
                 item_id: id,
                 item_name: name
             });
     });
    return arr_list;
}

// Manage list
// Fiddle: http://jsfiddle.net/spstieng/THJE3/46/
var manageitemListModel = function() {
    var self = this;

    // Data
    var exiting_list = $('.item-list ul').clone();
    self.items = ko.observableArray(create_list(exiting_list));
    self.item_name = ko.observable('');
    self.item_id = ko.observable('');

    // Operations
    self.additem = function() {
        if (self.item_name() != "") {
            self.items.push(new item(self.item_name(),self.item_id()));
            self.item_name("");
        }
    }.bind(self);

    // Remove item
    self.removeitem = function(item) {
        self.items.remove(item);
    }
};

ko.applyBindings(new manageitemListModel());
¿Fue útil?

Solución

Take a look on the following version

Don't forget press F12 in your favorite browser, your binding was incorrect item_name().length > 2

  • You can initialise self.item_name = ko.observable("");
  • create additional computed property (as for me this version is better)

For instance

self.enableToAdd = ko.computed(function(){
    return self.item_name().length >2;
});

<button class="btn" data-bind="click: addItem, enable:enableToAdd">Add</button>

Here's more about Computed Observables

Please note: if a binding has any error, all the binding doesn't work

Otros consejos

Your "enable" function is requiring an object that is not defined by the time it's accessed. You need to verify if it exists before using its value. Here's a working version of the HTML

<div class="wrapper">

  <div class="input-group">
    <input type="hidden" value="" data-bind="value: item_id" />
    <input class="form-control" type="search" data-bind="value: item_name" />     

    <span class="input-group-btn">
      <button class="btn" data-bind="click: addItem, enable: item_name() ? item_name().length > 2 : false">Add</button>
    </span>
  </div>

  <ul class="list-group" data-bind="foreach: items">

    <li class="list-group-item">
      <span data-bind="text: item_name, attr: {'id': item_id}"></span>
      <span class="glyphicon glyphicon-remove-circle" data-bind="click: $parent.removeItem"></span>
    </li>      

  </ul>    

</div>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top