It’s quite simple example I have experienced recently. But I think to share notes on blog, so that I remember this case and maybe someone else might use it in future.
Task is about Multipart form with dropdown which defines (control) which part of UI to render (show/hide) and depend on file field values submit button should be enabled/disabled. Key point, that the logic of interaction should be one single function/formula.
Main part of HTML:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form id="myForm" name="myForm" enctype="multipart/form-data" method="POST" | |
data-bind="submit: pseudoSubmitForm"> | |
<select data-bind="options: uiTypes, | |
optionsText: 'typeLabel', | |
optionsValue: 'typeValue', | |
value: selectedType, | |
optionsCaption: 'Choose…'"> | |
</select> | |
<div id="file1" data-bind="visible: showField1"> | |
<p>Select file 1</p> | |
<input type="file" name="files" data-bind="value: valueField1"/> | |
</div> | |
<div id="file2" data-bind="visible: showField2"> | |
<p>Select file 2</p> | |
<input type="file" name="files" data-bind="value: valueField2"/> | |
</div> | |
<input type="submit" value="Execute" data-bind="enable: enableExecutionButton() "/> | |
</form> |
Main part of JavaScript:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (){ | |
var node = document.getElementById('myForm'); | |
function AppViewModel() { | |
this.uiTypes = ko.observableArray([ | |
{"typeValue":"A", "typeLabel": "1 field type"}, | |
{"typeValue":"B", "typeLabel": "2 fields type"} | |
]); | |
this.selectedType = ko.observable(); | |
this.showField1 = ko.observable(false); | |
this.showField2 = ko.observable(false); | |
this.valueField1 = ko.observable(''); | |
this.valueField2= ko.observable(''); | |
this.selectedType.subscribe(function (type) { | |
if (!type){ | |
this.showField1(false); | |
this.showField2(false); | |
} else { | |
this.showField1(true); | |
this.showField2(type === 'B'); | |
} | |
this.valueField1(''); | |
this.valueField2(''); | |
}, this); | |
this.enableExecutionButton = ko.computed(function(){ | |
var a = this.showField1(); | |
var b = this.showField2(); | |
var x = this.valueField1(); | |
var y = this.valueField2(); | |
function AndAndXnor(a,b,x,y) { | |
//return (a&&b && x&&y || a&&!b && x&&!y); // WORKS | |
return (a&&x) && (b&&y || !b&&!y); // WORKS | |
//return (a&&x) && !(b^y); // "(b&&y || !b&&!y)" // DOES NOT WORK | |
} | |
return AndAndXnor(a,b,x,y); | |
}, this); | |
this.pseudoSubmitForm = function(formElement){ | |
this.selectedType(''); | |
} | |
} | |
var model = new AppViewModel(); | |
ko.applyBindings(model, node); | |
})() |
Task implementation notes
- By default there should be only 1 dropdown – set of types how UI will behave when u select dropdown.
- Used Knockout
data-bind="options ..."
- Used Knockout
this.uiTypes = ko.observableArray()
for array/set of types. - Used Knockout
this.selectedType = ko.observable()
for selected value in dropdown - Used Knockout
this.selectedType.subscribe()
approach to receive always new value to bind.
- Used Knockout
- When you select “1 field type” it should show one upload filed, but “Execute” button should be disabled, until u provide real file name.
- Used
this.showField1 = ko.observable(false);
- Used
- When you select “2 fields type” – almost the same, but both field’s values should be provided. And if you select 1st then 2nd or 2nd then 1st, logic must be the same.
- Used
this.showField2 = ko.observable(false);
- And very special piece of code, where Boolean logic and my experience with microelectronics helped me
function AndAndXnor()
- Used
- Data binding between dropdown, UI containers for upload fields, uplod file values, execution button and reset values on submit should be implemented with Knockout.JS.
- Main “watcher/observation” function but in Knockout terms it’s “computed” –
this.enableExecutionButton = ko.computed(function(){...})
- Interesting Knockout feature –
data-bind="submit: pseudoSubmitForm"
for FORM tag
- Main “watcher/observation” function but in Knockout terms it’s “computed” –
Special note about Boolean Logic as help and fun 🙂
Initially I had issue – as soon as I selected 1 upload field, button “Execute” was enabled, which is not ok.
So I started to think on more complex logic:
enableExecutionButton if ( (showField1 AND showField2 AND field1Value AND fieldValue2) // This is case for 2 fields OR (showField1 AND NOT-showField2 AND field1Value AND NOT-fieldValue2) // This is case of 1 field only )
So far so good – logic works fine. But it’s complicated a bit. So I simplified using Boolean rules:
(A*B*X*Y) + (A*!B*X*!Y)
=> (A*X) * (B*Y + !B*!Y);
And again, so far all works fine.
But have a look, last part of formula is in fact inverted XOR operation – XNOR.
Unfortunately in JavaScript we have only bitwise XOR. So my try to simplify more – failed:
(a&&x) && !(b^y);
Because ^
is bitwise but not logical operation. So if we have Logical XNOR in JavaScript, then formula would look like this:
Having this logical formula in mind, I gave a name to the function – “AndAndXnor“, used it but I returned to previous simplified formula (without XNOR inside).
You can try/see how it works here
Resources
- Nice chart/cheatsheet about Boolean Algebra.
- One more short and good explainable Boolean table.
- George Boole the Master.
- Great table of truthy/falsy comparison of all data types in JavaScript.