Omar
Omar

Reputation: 31732

Retrieve dynamically added element

The example is taken from knockout tutorials - working with lists and collections.

<table>
  <thead>
    <tr>
        <th>Passenger name</th>
        <th>Meal</th>
        <th>Surcharge</th>
        <th></th>
    </tr>
  </thead>
  <!-- Todo: Generate table body -->
  <tbody data-bind="foreach: seats">
    <tr>
        <td>
            <input data-bind="value: name" />
        </td>
        <td>
            <select data-bind="options: $root.availableMeals, value: meal, optionsText: 'mealName'"></select>
        </td>
        <td data-bind="text: formattedPrice"></td>
        </td>
    </tr>
  </tbody>
</table>

<button data-bind="click: addSeat">Reserve another seat</button>

ReservationsViewModel() adds a row of elements dynamically to a static table on button (Reserve another seat) click that is bound to addSeat with click event listener.

function ReservationsViewModel() {
   var self = this;
   ...        
   self.addSeat = function () {
       self.seats.push(new SeatReservation("", self.availableMeals[0]));
   }
   ...
}

by calling SeatReservation()

function SeatReservation(name, initialMeal) {
    var self = this;
    self.name = name;
    self.meal = ko.observable(initialMeal);
}

How can I retrieve the added elements from DOM (not the clicked element) from within SeatReservation().

Edit: As far as I understand knockout's foreach binding, it repeats child elements. What I'm trying to achieve is, have access to those elements once they are added into DOM.

fiddle

Upvotes: 0

Views: 131

Answers (2)

G&#244;T&#244;
G&#244;T&#244;

Reputation: 8053

With this binding handler:

ko.bindingHandlers.selectmenu = {
    init: function (element, valueAccessor, allBindings, viewModel, context) {
        $(element).parent().trigger("create");
        //you can customize the code depending on valueAccessor and allBindings
        //here
    }    
}

Used like this:

<select data-bind="selectmenu: {option1: optFromModel1, option2: optFromModel2}, 
                   options: $root.availableMeals, 
                   value: meal, 
                   optionsText: 'mealName'"></select>

It works (and you can replace the pagebeforecreate with ready)

Source for the select menu update: https://forum.jquery.com/topic/no-style-to-dynamic-select-element

Upvotes: 1

Joe Daley
Joe Daley

Reputation: 46476

You can use a post-processing property such as afterRender in the foreach binding.

See Note 7 on http://knockoutjs.com/documentation/foreach-binding.html

The afterRender callback will need to be defined on your main view model, but since it is passed the current data item as a parameter, you can use this to call a function on SeatReservation and pass the DOM elements in.

<tbody data-bind="foreach: { data: seats, afterRender: afterSeatRender }">

function SeatReservation(name, initialMeal) {
    // ...
    self.afterRender = function (elements) {
        $(elements).css('background-color', 'green');
    };
}

function ReservationsViewModel() {
    // ...
    self.afterSeatRender = function (elements, seat) {
        seat.afterRender(elements);
    };
}

Here is an updated fiddle: http://jsfiddle.net/4J3Pf/2/

Having said that, the whole point of Knockout is to use data binding instead of manipulating the DOM directly. The Knockout documentation makes it clear that this is not a normal use case:

These callbacks are only intended for triggering animations related to changes in a list.

In particular, manipulating the DOM within your SeatReservation view model is the wrong design. Try to keep your view models and your view rendering code separate.

Upvotes: 2

Related Questions