2013-05-16 1 views
2

У меня есть наблюдаемый массив с элементами, привязанными к ul. Мне нужно рассчитать ширину всего набора из li предметов, как только предметы были добавлены, поэтому я могу установить ширину элемента ul на общую ширину ребенка. li элементов.Пользовательская привязка, когда foreach закончил рендеринг

Как я могу это сделать с привязкой foreach?

<div> 
    <h2 data-bind="visible: items().length">Test</h2> 
    <ul data-bind="foreach: { data: items, afterAdd: $root.myAfterAdd }"> 
     <li data-bind="text: name"></li> 
    </ul> 
</div> 

И JavaScript:

var viewModel = { 
    items: ko.observableArray([ 
     { name: "one" }, 
     { name: "two" }, 
     { name: "three" } 
     ]), 
    myAfterAdd: function(element) { 
     if (element.nodeType === 1) { 
      alert("myAfterAdd"); 
     } 
    }, 
    addItem: function() { 
     this.items.push({ name: 'new' }); 
    } 
}; 


ko.applyBindings(viewModel); 

// once foreach has finished rendering I have to set the width 
// of the ul to be the total width of the children! 

Я попытался с помощью afterAdd так что я могу «обновление» ширина ul после добавления каждого элемента, к сожалению, я обнаружил, что afterAdd не срабатывает для начальные предметы! Он будет срабатывать только при нажатии дополнительные детали ...

Обратите внимание, что содержимое внутри li элементов будет неизвестной ширины :)

См this fiddle для примера сценария.

+0

'afterRender' делает то, что вы хотите, и запускается после первоначального рендера. – Tyrsius

+0

Проблема с 'afterRender' заключается в том, что она вызывается до того, как элемент dom полностью инициализирован (например, элемент li), это дает мне неправильную ширину. – Umair

+0

На стороне записки, я могу очень любопытно: * why * вы бы хотели сделать это? – Jeroen

ответ

1

После долгих 'прибегая к помощи' У меня есть нашел решение. Путь для достижения этой цели с Еогеаспом слишком зарегистрировать обработчик события:

ko.bindingHandlers.runafter = { 
    update: function (element, valueAccessor, allBindingsAccessor) { 
     var value = valueAccessor(); 
     setTimeout(function (element, value) { 
      if (typeof value != 'function' || ko.isObservable(value)) 
       throw 'run must be used with a function'; 

      value(element); 
     }, 0, element, value); 
    } 
}; 

Этого обработчик будет срабатывать каждый раз, когда происходит обновление (или только один раз, если значение не является наблюдаемым).

Что еще более важно, он срабатывает после завершения цикла foreach!

См. here.

0

Вы могли бы сделать что-то вроде этого:

var viewModel = { 
    items: ko.observableArray([ 
     { name: "one" }, 
     { name: "two" }, 
     { name: "three" } 
     ]), 
    myAfterAdd: function(event) { 
     if (event.originalEvent.srcElement.nodeType === 1) { 
      alert("myAfterAdd"); 
     } 
    }, 
    addItem: function() { 
     this.items.push({ name: 'new' }); 
    } 
}; 


$('#bucket').bind('DOMNodeInserted DOMNodeRemoved', viewModel.myAfterAdd); 

ko.applyBindings(viewModel); 

Какого ведра Л элемента

See fiddle

Я надеюсь, что это помогает

2

У меня была аналогичная проблема. Вы можете использовать это, чтобы сделать этот первоначальный расчет:

ko.bindingHandlers.mybinding { 
     init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
      var foreach = allBindings().foreach, 
       subscription = foreach.subscribe(function (newValue) { 
       // do something here (e.g. calculate width/height, use element and its children) 
       subscription.dispose(); // dispose it if you want this to happen exactly once and never again. 
      }); 
     } 
}