Reputation: 71
I have been given some KO code to maintain:
<div class="formField" id="formFieldWorkflowStatus">
<div id="comboWorkflowStatus" data-bind="combobox: workflowStatusModel"></div>
</div>
<div class="formField" id="formFieldSalesStatus">
<div id="comboSalesStatus" data-bind="combobox: salesStatusModel"></div>
</div>
It appears to use this mapping JS file behind it:
(function (app, ko) {
app.namespace("mappers");
function test()
{
convertedObject.salesStatuses
}
var
config = {
"consignments": {
create: mapInlineNotes
},
"charities": {
create: mapInlineNotes
},
"gifts": {
create: mapInlineNotes
},
ignore: ["saleStatus", "saleStatuses"]
},
map = function (json) {
var convertedObject = ko.utils.parseJson(json);
var model = ko.mapping.fromJS(convertedObject, config);
model.salesStatusModel = new ListModel({
isRemoteSource: false,
currentValue: convertedObject.selectedSalesStatus,
data: convertedObject.salesStatuses,
onlyPreparedValues: true,
allowNull: false,
readonly: true
});
model.workflowStatusModel = new ListModel({
isRemoteSource: false,
currentValue: convertedObject.selectedWorkflowStatus,
data: convertedObject.workflowStatuses,
onlyPreparedValues: true,
allowNull: false,
readonly: true
});
return model;
},
toJS = function (model) {
return ko.mapping.toJS(model);
},
unmapConsignment = function (consignment) {
return ko.mapping.toJS(consignment);
},
mapConsignment = function (data, consignment) {
if (!consignment) {
return mapInlineNotes({ data: data });
}
return ko.mapping.fromJS(data, consignment);
};
function mapInlineNotes(options) {
return app.mappers.inlineNoteContainer.map(options.data);
}
app.mappers.consignment = {
map: map,
toJS: toJS,
unmapConsignment: unmapConsignment,
mapConsignment: mapConsignment
};
})(app, ko);
All the examples I find are nothing like this and use things like:
<select data-bind="options: workflowStatuses, value: selectedWorkflowStatus, optionsText: 'name', optionsCaption: 'Choose a make'"></select>
But this didn't work for me. The stuff at the top works in that they are two dropdowns populated from the DB, but they are not linked together in anyway. I need the 2nd one to be able to filter itself depending on what is selected in the first.
What events can I hook into to capture the top dropdown changing?
This is all so easy in normal MVC, but with KO I have no idea how to do it and it is really frustrating!
So any help would be really appreciated before I am forced to throw KO away altogether and just re-write it in normal MVC.
UPDATE:
I found this file (knockout.combobox.js):
/// <reference path="jquery-1.7.js" />
/// <reference path="knockout-2.2.1.debug.js" />
/// <reference path="jquery-ui-1.8.16.js" />
// Alex B
(function ($, ko) {
var defaultKeyDownTemplateName = "defaultTemplate";
var defaultChangeComboboxTemplateName = "specialTemplate";
var defaultKeyDownTemplate = "<input type=\"text\" class='ignoreReadonly' data-bind=\"readonly: $data.readonly, enable: $data.editable, autocomplete: $data, value: $data.selected.value, valueUpdate: 'afterkeydown'\"/><button style='opacity: 1' tabindex=\"-1\" title=\"Show All Items\" type=\"button\" data-bind=\"enable: $data.editable, visible: !($data.nobutton), click: $data.showAll, jqueryui: {widget:'button', options:{icons:{primary:'ui-icon-triangle-1-s'}, text: false}}\"> </button>";
var defaultFocusChangeTemplate = "<input type=\"text\" class='ignoreReadonly' data-bind=\"readonly: $data.readonly, enable: $data.editable, autocomplete: $data, value: $data.selected.value\"/><button style='opacity: 1' tabindex=\"-1\" title=\"Show All Items\" type=\"button\" data-bind=\"enable: $data.editable, visible: !($data.nobutton), click: $data.showAll, jqueryui: {widget:'button', options:{icons:{primary:'ui-icon-triangle-1-s'}, text: false}}\"> </button>";
var textareaTemplateName = "textAreaTemplate";
var textareaTemplate = "<textarea class=\"elastic\" type=\"text\" data-bind=\"readonly: $data.readonly, enable: $data.editable, autocomplete: $data, value: $data.selected.value, valueUpdate: 'afterkeydown'\"/>";
var templateEngine = new ko.nativeTemplateEngine();
ko.bindingHandlers.combobox = {
init: function (element, valueAccessor) {
templateEngine.addTemplate(defaultKeyDownTemplateName, defaultKeyDownTemplate, element);
templateEngine.addTemplate(textareaTemplateName, textareaTemplate, element);
templateEngine.addTemplate(defaultChangeComboboxTemplateName, defaultFocusChangeTemplate, element);
$("textarea.elastic").elastic();
return { 'controlsDescendantBindings': true };
},
update: function (element, valueAccessor, allBindingsAccessor) {
var model = ko.utils.unwrapObservable(valueAccessor());
if (allBindingsAccessor().enable) {
var enabled = ko.utils.unwrapObservable(allBindingsAccessor().enable);
model.editable = enabled;
}
if (model.templateName) {
ko.renderTemplate(model.templateName, model, { templateEngine: templateEngine }, element, "replaceNode");
} else {
ko.renderTemplate(defaultKeyDownTemplateName, model, { templateEngine: templateEngine }, element, "replaceNode");
}
}
};
})(jQuery, ko);
And also found:
ko.bindingHandlers.comboboxSelectedValue = {
init: function (element, valueAccessor) {
$(element).bind("change", function () {
var accessor = valueAccessor();
if (ko.isObservable(accessor)) {
accessor(element.selectedIndex != -1 ? $(element).val() : null);
} else {
valueAccessor(element.selectedIndex != -1 ? $(element).val() : null);
}
});
},
update: function (element, valueAccessor) {
if (!$(element).data("combobox")) {
$(element).combobox(); //it is here (not in init method) to provide sorting, because there are no select options rendered by ko by the time init method is called
}
var value = ko.utils.unwrapObservable(valueAccessor());
if (value != null) {
$(element).combobox("setSelectedValue", value);
} else {
$(element).combobox("clearSelected");
}
}
};
in a knockout.extentions.js file.
Hopefully that helps?
Someone on another forum posted this:
//Creates an observable array which changes its contents automatically, based on another value
var filteredWorkflowStatuses=ko.computed(function(){
//Some kind of filtering, e.g
return ko.utils.arrayFilter(convertedObject.workflowStatuses(), function(item){
return convertedObject.selectedSalesStatus() && convertedObject.selectedSalesStatus().someProperty()==item.someProperty();
});
});
Which appears to be what I need to do but cant figgure out how I get this to filter my salesstatus dropdown depending on values selected from WorkFlowStatus dropdown. Currently SalesStstus dropdown shows ALL the values, I need this to be filtered depending on what is selected in WorkFlowStatus dropdown.
Basically when the 1st dropdown (workflow status is changed I need the 2nd dropdown (sales status) to filter its results. So for example when Workflow status is Reserved, Sales Status will show Comission, Reserved, Gift, Charity. When Workflow status is On Offer, sales status will show On Consignment, On offer and On Loan as options. Plus there are more...
Hope that makes sense?
Really appreciate the help thanks! :)
Upvotes: 1
Views: 568
Reputation: 43899
You don't hook into events to observe values changing in Knockout. Widgets are bound to observables, so that changes in values show up in your viewmodel. You subscribe to those observables to take action when they change, or you write computed observables that implicitly subscribe.
If you're new to Knockout and this code has just been thrown into your lap, take a little time to go through the Knockout tutorial. That will give you a better feel for the Knockout way of approaching things. You will need to understand Knockout pretty well, at least up to the point of custom binding handlers. It looks like what you have been given is going to require some rewriting to get the functionality you want.
Because it's a custom widget, it's probably not going to have any standard events fire. The best suggestion I can give you (not having all of the code to go through, nor the time to go through all of it if it were here) is to add this bit of code just after the definition of workflowStatusModel
:
model.workflowStatusModel.subscribe(function (newValue) {
console.debug("Hey, it changed!", newValue);
});
If you get console output from it, that's where you can deal with changes to it. Otherwise, look inside the definition of ListModel
for observable members that seem likely to hold the value you're looking for, and try something like
model.workflowStatusModel.someMember.subscribe(function (newValue) {
console.debug("Hey, it changed!", newValue);
});
Upvotes: 2