Reputation: 5403
I've created a custom binding handler to render an html select component.
EG:
<select data-bind="dynamicSelect: { src: 'Category', label: 'Category'} "></select>
After a user selects a category, the category fields array gets populated, which i have bound to a div that'll render a certain template
<div data-bind="template: { name: displayMode, foreach: categoryFields }"></div>
My templates
<script type="text/html" id="inputTemplate">
<label data-bind="text: FieldName, attr: { for: FieldName }"></label>
<input data-bind="attr: { name: FieldName, type: $parent.fieldType($data) }" />
</script>
<script type="text/html" id="lookupTemplate">
<label data-bind="text: FieldName, attr: { for: FieldName }"></label>
<select data-bind="dynamicSelect: { src: FieldName, label: FieldName}"></select>
</script>
The problem is, that the dynamicSelect inside the template does not seem to be binding? How would i go about reusing a binding handler inside a template?
Binding handler
define(['durandal/composition', 'plugins/http'], function (composition, http) {
composition.addBindingHandler('dynamicSelect', {
init: function (element, valueAccessor) {
console.log(element);
console.log(valueAccessor());
var elem = $(element);
elem.addClass('hidden');
elem.before('<label>' + valueAccessor().label + '</label>');
elem.after('<div><br/><label><i class="icon-spinner icon-spin active"></i> Loading...</label></div>');
console.log('/api/lookup?type=' + valueAccessor().src);
return http.get('/api/lookup?type=' + valueAccessor().src).then(function (res) {
var items = res.LookupItems;
$.each(items, function (idx) {
elem.append('<option value=' + items[idx].Id + '>' + items[idx].Name + '</option>');
});
elem.removeClass('hidden');
elem.next().addClass('hidden');
});
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
}
});
});
Upvotes: 4
Views: 1604
Reputation: 17564
This does not answer your durandal releated question, but it addresses your lack of MVVM mindset in your code. :D
I would do it has a ViewModel
MyApp.LookupViewModel = function(label, src) {
this.label = label;
this.src = src;
this.items = ko.observableArray();
this.selectedItem = ko.observable();
this.loading = ko.observable(true);
this.loaded = ko.computed(function() {
return !this.loading();
}, this);
//simulate ajax
setTimeout(function() {
this.items([{
name: "Foo1",
id: 1
},{
name: "Foo2",
id: 2
}
]);
this.loading(false);
}.bind(this), 1000);
};
When you want to use it you just declare an instance of the VM
MyApp.ViewModel = function() {
this.lookup = new MyApp.LookupViewModel("Foo", "http://foo");
}
My above fiddle uses a little lib of mine that takes care of finding the view names so that you do not need to explicit declare template bindings in your view.
Upvotes: 1
Reputation: 5403
Found my problem.
I registered the binding handler using Durandals, composition helper. Thus (and its my best guess), it only got called during the page's creation. Explains why my first select got populated.
I changed my binding handler to
define(['plugins/http'], function (http) {
ko.bindingHandlers.dynamicSelect = {
init: function (element, valueAccessor) {
var elem = $(element);
elem.addClass('hidden');
elem.before('<label>' + valueAccessor().label + '</label>');
elem.after('<div><br/><label><i class="icon-spinner icon-spin active"></i> Loading...</label></div>');
console.log('/api/lookup?type=' + valueAccessor().src);
return http.get('/api/lookup?type=' + valueAccessor().src).then(function (res) {
var items = res.LookupItems;
$.each(items, function (idx) {
elem.append('<option value=' + items[idx].Id + '>' + items[idx].Name + '</option>');
});
elem.removeClass('hidden');
elem.next().addClass('hidden');
});
}
};
});
Upvotes: 1