javascript - binding multi dropdown list in knockout.js -
i have multi dropdown list , need following:
1. make sure when selecting value in 1 dropdown list won't appear in others (couldn't find proper solution here).
2. when selecting value "text" text field (<input>
) apear instead of yes/no dropdown.
3. "choose option" appear first row (still working on it).
4. make sure if "text" selected, on top (still working on it).
html:
<div class='liveexample'> <table width='100%'> <tbody data-bind='foreach: lines'> <tr> <td> choose option: </td> <td> <select data-bind='options: filters, optionstext: "name", value: filtervalue'> </select> </td> <td data-bind="with: filtervalue"> <select data-bind='options: filtervalues, optionstext: "name", value: "name"'> </select> </td> <td> <button href='#' data-bind='click: $parent.removefilter'>remove</button> </td> </tr> </tbody> </table> <button data-bind='click: addfilter'>add choice</button>
javascript:
var cartline = function() { var self = this; self.filter = ko.observable(); self.filtervalue = ko.observable(); // whenever filter changes, reset value selection self.filter.subscribe(function() { self.filtervalue(undefined); }); }; var cart = function() { // stores array of filters var self = this; self.lines = ko.observablearray([new cartline()]); // put 1 line in default // operations self.addfilter = function() { self.lines.push(new cartline()) }; self.removefilter = function(line) { self.lines.remove(line) }; }; ko.applybindings(new cart());
i appeaciate assist here! first problem.
thanks!
mike
if want limit options based on options selected in ui, you'll need make sure every cartline
gets own array of filters. let's pass in constructor so:
var cartline = function(availablefilters) { var self = this; self.availablefilters = availablefilters; // other code // ... };
you'll have use new viewmodel property instead of global filters
array:
<td> <select data-bind='options: availablefilters, optionstext: "name", value: filtervalue'> </select> </td>
now, we'll have find out filters still available when creating new cartline
instance. cart
manages lines
, , has addfilter
function.
self.addfilter = function() { var availablefilters = filters.filter(function(filter) { return !self.lines().some(function(cartline) { var currentfiltervalue = cartline.filtervalue(); return currentfiltervalue && currentfiltervalue.name === filter.name; }); }); self.lines.push(new cartline(availablefilters)) };
the new cartline
instance gets filter aren't yet used in other line. (note: if want use array.prototype.some
in older browsers, might need polyfill)
the thing remains more of ux decision "coding decision": want users able change previous "choices" after having added new one? if case, you'll need create computed availablefilters
arrays rather ordinary ones.
here's forked fiddle contains code posted above: http://jsfiddle.net/ztwcql69/ note can create doubled choices, because choices remain editable after adding new ones. if comment desired behavior be, can figure out how so. might require more drastic changes... solution provided more of pointer in right direction.
edit: felt bad not offering final solution, here's approach:
if want update availablefilters
retrospectively, can this:
cartline
s reference siblings
(the other cart lines) , create subscription changes via ko.computed
uses siblings
and filtervalue
:
var cartline = function(siblings) { var self = this; self.availablefilters = ko.computed(function() { return filters.filter(function(filter) { return !siblings() .filter(function(cartline) { return cartline !== self }) .some(function(cartline) { var currentfiltervalue = cartline.filtervalue(); return currentfiltervalue && currentfiltervalue.name === filter.name; }); }); }); // other code... };
create new cart lines so: self.lines.push(new cartline(self.lines))
. initiate empty array , push first cartline
afterwards using addfilter
.
concerning point 2: can create computed observable sorts based on filtervalue:
self.sortedlines = ko.computed(function() { return self.lines().sort(function(linea, lineb) { if (linea.filtervalue() && linea.filtervalue().name === "text") return -1; if (lineb.filtervalue() && lineb.filtervalue().name === "text") return 1; return 0; }); });
point 3: move outside foreach
.
point 4: use if
binding:
<td data-bind="with: filtervalue"> <!-- ko if: name === "text" --> <input type="text"> <!-- /ko --> <!-- ko ifnot: name === "text" --> <select data-bind='options: filtervalues, optionstext: "name", value: "name"'> </select> <!-- /ko --> <td>
updated fiddle contains code: http://jsfiddle.net/z22m1798/
Comments
Post a Comment