Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knockout - execute code after the last item has been rendered

i'm using jquery quicksearch to search a table that is being populated by knockout foreach loop. the quicksearch element needs to be initiate after the foreach finishes. I've tried several approaches, but was unsuccessful so far.

I've tries using 'afterRender', but was unable to determine if the current item is the last item on the collection, i've also tried using a bindingHandlers, but then i got a collection of length 0 instead of length 2005.

so:

  1. what's the best approach to finding the last element in a foreach loop ?
  2. what's the best way to implement it in this specific scenario?

here's my view:

    <tbody data-bind="foreach: containers">
        <tr>
            <td><span data-bind="text: code"></span></td>
            <td><span data-bind="text: typeName"></span></td>
            <td><span data-bind="text: parentClient"></span></td>
            <td><span data-bind="text: client"></span></td>
            <td>
                <a data-bind="attr: { onclick: deleteUrl }">
                    <i class="icon-trash"></i>@delete </a>
                |
                <a data-bind="attr: { href: editUrl }">
                    <i class="icon-edit"></i>@edit</a>
                |
                <a data-bind="attr: { href: qrUrl }" target="blank">
                    <i class="icon-qrcode"></i>
                    @printQr
                </a>
            </td>

        </tr>
    </tbody>

and here's my knockout code:

function Container(data) {
        var self = this;
        self.id = ko.observable(data.Id);
        self.code = ko.observable(data.Code);
        self.typeName = ko.observable(data.TypeName);
        self.parentClient = ko.observable(data.ParentClient);
        self.client = ko.observable(data.Client);
        self.deleteUrl = ko.computed(function () {
            return "GetModal('/Containers/Delete/" + data.Id + "','containerModal');";
        });
        self.editUrl = ko.computed(function () {
            return '/Containers/Edit/' + data.Id;
        });

        self.qrUrl = ko.computed(function () {
            return '/Qr/Index/10?entity=' + data.Id;
        });
    }
 function ContainersViewModel() {
        var self = this;
        self.containers = ko.observableArray([]);
        self.counter = ko.computed(function () {
            return self.containers().length;
        });

        $.getJSON("/Containers/Json", function (data) {
            var containers = $.map(data, function (item) {
                return new Container(item);
            });

            self.containers(containers);
        });

    };
    ko.applyBindings(new ContainersViewModel());

Thanks a lot !

Nir

like image 719
nir weiner Avatar asked May 05 '13 14:05

nir weiner


3 Answers

The foreach binding afterRender option can do what you are looking for, the trick part is to be able to know that the element is the last one. That can be solved using the provided arguments like shown in http://jsfiddle.net/n54Xd/.

The function:

this.myPostProcessingLogic = function(elements, data) {
    if(this.foreach[this.foreach.length-1] === data)
        console.log("list is rendered");
};

Will be called for every element but the "if" will make sure the code only runs for the last element.

like image 121
Ricardo Medeiros Penna Avatar answered Nov 08 '22 15:11

Ricardo Medeiros Penna


Try to subscribe manually on the array changes. By using subscribe you will be notified on array modification.

If you subscribe after applyBindings call, you will be notified after the refresh process of the view.

See fiddle

var ViewModel = function () {
    var self = this;

    self.list = ko.observableArray();

    self.add = function () {
        self.list.push('Item');
    }
    self.list.subscribe(function () {
        alert('before');
    });
};
var vm = new ViewModel();
ko.applyBindings(vm);

vm.list.subscribe(function () {
    alert('after');
});

I hope it helps.

like image 34
Damien Avatar answered Nov 08 '22 16:11

Damien


I was successful using Ricardo Medeiros Penna's answer with just one little tweak.

    this.myPostProcessingLogic = function(elements, data) {
      if(this.foreach._latestValue[this.foreach._latestValue.length-1] === data)
        console.log("list is rendered");
    };

I had to add _latestValue to get the length value and now it is working as desired.

like image 44
JaxCoder Avatar answered Nov 08 '22 17:11

JaxCoder