dynamic row creation knockout js

2019-08-15 12:04发布

问题:

Using knockout js am trying to create a table which will have 4rows and 4coulmns. Below is my array which will have 16 elements which will be needed to be put inside the table.

    /*----------------------------------------------------------------------*/
/* View Model                                                           */
/*----------------------------------------------------------------------*/
function ViewModel() {
    var self = this;
    self.items = ko.observableArray(["1.jpg", 
                                      "2.jpg", 
                                      "3.jpg", 
                                      "4.jpg",
                                      "5.jpg",
                                      "6.jpg"
                                      ]);
    self.itemRows = ko.computed(function () { //computes the rows dynamically based on items
        var rows = [];
        var count = 0;
        var items = self.items(); //get the item array
        var current = [];
        for(var i in items)
        {
            var item = items[i];
            current.push(item);
            count++;

            if (count == 4)
            {
                count = 0;
                rows.push(current);
                current = []; //next array
            }
        }

        if (current.length > 0)
        {
            rows.push(current);
        }

        return rows;
    });

    self.bindSort = function() {

        var startIndex = -1;         
        var sortableSetup = {        
         start: function (event, ui)
         {         
          startIndex = ui.item.index();
         },


         stop: function (event, ui) 
         {      
          var newIndex = ui.item.index();        
          if (startIndex > -1) 
          {
              self.from = ko.observable(startIndex);
              self.to = ko.observable(newIndex);
              var iTo = parseInt(self.to());
          var iFrom = parseInt(self.from());
          var from = self.items()[iFrom];
              var to = self.items()[iTo];
              self.items()[iTo] = from;
                  self.items()[iFrom] = to;
                  //alert('before');
                  // alert(from);
                  // alert(to);
                  var fromA = self.items()[iFrom];
              var toA = self.items()[iTo]; 
              //alert('after');
          //alert(fromA);
                  // alert(toA);
              self.items.remove(from);               
              self.items.splice(newIndex, 0, toA);
               self.items.remove(to); 
                self.items.splice(startIndex, 0, fromA);
                          //ui.item.remove();
               self.items.valueHasMutated();

          }

         }
        };


        $(".fruitList").sortable( sortableSetup );                           

    };

}   

/*----------------------------------------------------------------------*/
/* KO HTML Binding                                                      */
/*----------------------------------------------------------------------*/   
$(document).ready(function() {

    // create the view model
    var model = new ViewModel();

    // call the bind method for jquery UI setup
    model.bindSort();

    // binds ko attributes with the html
    ko.applyBindings(model);

});

and trying to do this in html,

<table data-bind="foreach: itemRows">
              <tr class="fruitList" data-bind="foreach: $data">
                  <td><img data-bind="attr: { src: $data }" /></td>
              </tr>
        </table>

I am not able to get the length of the array and also how can i break the loop once it creates 4 tds in the first and then create the second row.. Any suggestion ???

Update:

Below code doesnt seem to work when i use sortable,

    start: function (event, ui)
     {         
      startIndex = ui.item.index();
     },


     stop: function (event, ui) 
     {      
      var newIndex = ui.item.index();  

回答1:

UPDATE: Move to the bottom for the sortable version

Non-sortable version

I have created a jsfiddle which seems to meet your requirements before you edited and mentioned sorting: http://jsfiddle.net/fENSD/4/

What I did was create a computed observable that observes your items observable and returns a nested array in the shape that you want for your table rendering. This isn't the most efficient function, but with some work you could probably cut down on the number of arrays that get allocated (at the moment, 5 or so will be created for a 4x4 grid every time it is updated):

self.items = ko.observableArray(["images/1.jpg", "images/2.jpg", "images/3.jpg", "images/4.jpg", "images/5.jpg", "images/6.jpg"]);

self.itemRows = ko.computed(function () { //computes the rows dynamically based on items
    var rows = [];
    var count = 0;
    var items = self.items(); //get the item array
    var current = [];
    for(var i in items)
    {
        var item = items[i];
        current.push(item);
        count++;

        if (count == 4)
        {
            count = 0;
            rows.push(current);
            current = []; //next array
        }
    }

    if (current.length > 0)
    {
        rows.push(current);
    }

    return rows;
});

The table is then rendered like so:

<table data-bind="foreach: itemRows">
    <tr data-bind="foreach: $data">
        <td><img data-bind="attr: { src: $data }" /></td>
    </tr>
</table>

The JSFiddle includes an example of adding an item to the items observable and the table being updated to reflect that as well if you wanted to see that in action. There is also in the JSFiddle an example of one way you can get the length of the items array (data-bind="text: items().length").

Sortable version

It was a quite a change, but I managed to get the sorting working as well. Taking the example from the jquery ui website I got the following:

http://jsfiddle.net/fENSD/11/

It is very difficult to sort on tables, and so the jquery ui example actually used a list that had a defined width with each element taking up a certain width. This effectively creates a table. I assume that since you are using images that you can make them all exactly the same size.

To do this, I have some CSS like so:

#items { list-style-type: none; margin: 0; padding: 0; width: 200px; }
#items li { width: 48px; margin: 0px; padding: 0px; float: left; text-align: center; height: 48px; }

I then created a proper binding handler like so:

ko.bindingHandlers['sortable'] = {
    init: function (element, valueAccessor) {
        var $element = $(element);
        var observable = valueAccessor();
        var value = ko.utils.unwrapObservable(observable);
        $element.sortable({
            stop: function(event, ui) {
                var prev = ui.item.prev();
                var data = ko.dataFor(ui.item[0]);
                var index = 0;
                if (prev.length > 0) {
                    //not the first item
                    var prevData = ko.dataFor(prev[0]);
                    var index = value.indexOf(prevData) + 1;
                }
                var oldIndex = value.indexOf(data);
                value.splice(oldIndex, 1);
                value.splice(index, 0, data);
                observable.valueHasMutated();
            }
        });
    }
};

Whenever the sortable is updated, it modifies the observable array passed into the binding. Note: This is incredibly unrobust and needs to check to make sure that the passed value is actually an observable before telling it that its value has mutated.

The viewmodel looks like so:

function ViewModel() {
    var self = this;

    self.items = ko.observableArray(["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"]);

    self.items.subscribe(function (value) {
        console.log(value); //this is so you can see that the array is indeed being re-sorted
    });

    self.add = function () {
        self.items.push("another thing");
    };
}

And to render:

<ul id="items" data-bind="sortable: items, foreach: items">
    <li><img data-bind="attr: { src: $data }" /></li>
</ul>

The limitations of this method are that if your items are different sizes, the order of the sortable will look a little different than what is actually represented in memory. However, providing that all of your elements are the same size, it should work just fine. The other issue is that this doesn't work well with duplicate items. However, to solve this it should be a simple matter of making each element an object containing a value (maybe even an observable value) instead of a plain old string.