javascript - Knockout calculate container height from custom element after rendering items -
i writing custom binding amongst other things, needs know height before running display logic. contents rendered in foreach
binding.
normally, ko call binding init
before of these items rendered, meaning height incorrect.
i used { controlsdescendantbindings: true }
, manually bound child items before using height; works fine in init
.
the problem is, i'd have binding fire update
, recalculate height when items updated, , above pattern gives me you cannot apply bindings multiple times same element
if change init
update
, understandably enough.
here cut down code:
html:
<a href="#" data-bind="click: rerun">re-run</a> <hr/> <div class="container" data-bind="test: true"> <!-- ko foreach: items --> <div class="item" data-bind="text: id"></div> <!-- /ko --> </div> <hr/> <b id="res"></b>
css:
hr { clear: both; } .item { width: 50px; height: 50px; background: red; margin: 5px; display: inline-block; }
code:
ko.bindinghandlers.test = { init: function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext) { var val = ko.unwrap(f_valueaccessor()); if(!!val) { ko.applybindingstodescendants(bindingcontext, el); $('#res').html($(el).height()); return { controlsdescendantbindings: true }; } } }; function model(data, mapping) { var _this = this; var _mapping = $.extend({}, {}, mapping); this.items = ko.observablearray(); if(data) ko.mapping.fromjs(data, _mapping, _this); this.rerun = function(model, e) { _this.items(_this.genitems()); } this.genitems = function() { var = []; for(var = 0; < 10 + (math.random() * 20); i++) a.push({id:i}); return a; } this.rerun(); } var model = new model(); ko.applybindings(model);
and fiddle: https://jsfiddle.net/whelkaholism/tzrxbojj/
i can see 2 ways solve this:
pass object binding contains function reference can called model when items changed force recalculate (this works fine pretty clunky)
use
afterrender
onforeach
(this works fine also, adds code model)
the afterrender
solution works fine , may turn out recommended method; mean have add model needs similar functionality, , custom binding seems nice abstraction.
is there (i.e. better #1) way of getting work using single custom binding on container?
the main thing missing in solution custom binding needs know items model in order subscribe changes , recalculate height of element. made height observable solution more knockout intended used.
my solution not require controlsdescendantbindings: true
.
html:
<a href="#" data-bind="click: rerun">re-run</a> <hr/> <div class="container" data-bind="test: {heightvalue: containerheight, itemsmodel: items}"> <!-- ko foreach: items --> <div class="item" data-bind="text: id"></div> <!-- /ko --> </div> <hr/> <b id="res" data-bind="text: containerheight"></b>
binding:
ko.bindinghandlers.test = { init: function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext) { var data = f_valueaccessor(); var recalculate = function () { settimeout(function () { data.heightvalue($(el).height()); }, 0); } data.itemsmodel.subscribe(function () { recalculate(); }); recalculate(); } };
the settimeout 0 helps fire height recalculation after rendering complete.
and view model same exception of added observable container height:
this.containerheight = ko.observable();
Comments
Post a Comment