Amplify store and Knockout can be great partners. For example, you can use client storage to improve user experience, remembering user preferences or previously entered values such that the user doesn’t have to start all over.
There is a tutorial on Knockout that provides for client storage. In the tutorial, Knockout uses Amplify to restore user data when the user revisits the site.
You can augment Knockout observables with additional functionality, by using extenders.
You can use extender to automatically store and restore any observable property.For example, the following code:
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
var viewModel = { | |
stringValue: ko.observable("Hello") | |
.extend({ localStore: "myString" }) | |
} |
The extender can be implemented as follows:
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
ko.extenders.localStore = function (target, key) { | |
var value = amplify.store(key) || target(); | |
var result = ko.computed({ | |
read: target, | |
write: function(newValue) { | |
amplify.store(key, newValue); | |
target(newValue); | |
} | |
}); | |
result(value); | |
return result; | |
}; |
Sample Demonstrating Knockout Observable Stored Locally
The following sample shows Knockout updating several types of controls, the data storage happens automatically using the Knockout extension.
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Persisted Binding HTML Controls</title> </head> <body> <div class="readout"> <h3>What's in the model?</h3> <table> <tr> <td class="label">Text value:</td> <td data-bind="text: stringValue"></td> </tr> <tr> <td class="label">Password:</td> <td data-bind="text: passwordValue"></td> </tr> <tr> <td class="label">Bool value:</td> <td data-bind='text: booleanValue() ? "True" : "False"'></td> </tr> <tr> <td class="label">Selected option:</td> <td data-bind="text: selectedOptionValue"></td> </tr> <tr> <td class="label">Multi-selected options:</td> <td data-bind="text: multipleSelectedOptionValues"></td> </tr> <tr> <td class="label">Radio button selection:</td> <td data-bind="text: radioSelectedOptionValue"></td> </tr> </table> </div> <h3>HTML controls</h3> <table> <tr> <td class="label">Text value (updates on change):</td> <td><input data-bind="value: stringValue" /></td> </tr> <tr> <td class="label">Text value (updates on keystroke):</td> <td><input data-bind='value: stringValue, valueUpdate: "afterkeydown"' /></td> </tr> <tr> <td class="label">Text value (multi-line):</td> <td><textarea data-bind="value: stringValue"> </textarea></td> </tr> <tr> <td class="label">Password:</td> <td><input type="password" data-bind="value: passwordValue" /></td> </tr> <tr> <td class="label">Checkbox:</td> <td><input type="checkbox" data-bind="checked: booleanValue" /></td> </tr> <tr> <td class="label">Drop-down list:</td> <td><select data-bind="options: optionValues,
value: selectedOptionValue"></select></td> </tr> <tr> <td class="label">Multi-select drop-down list:</td> <td><select multiple="multiple" data-bind="options: optionValues,
selectedOptions: multipleSelectedOptionValues"> </select></td> </tr> <tr> <td class="label">Radio buttons:</td> <td> <label><input type="radio" value="Alpha" data-bind="checked: radioSelectedOptionValue" />Alpha</label> <label><input type="radio" value="Beta" data-bind="checked: radioSelectedOptionValue" />Beta</label> <label><input type="radio" value="Gamma" data-bind="checked: radioSelectedOptionValue" />Gamma</label> </td> </tr> </table> <script src="Scripts/knockout-2.2.1.js"></script> http://span <script> // Augment Knockout obsevables to enable a local store (function (ko) { ko.extenders.localStore = function (target, key) { // Store into local storage using Amplify's store var value = amplify.store(key) || target(); var result = ko.computed({ read: target, write: function (newValue) { amplify.store(key, newValue); target(newValue); } }); result(value); return result; }; })(ko); var viewModel = { stringValue: ko.observable("Hello") .extend({ localStore: "myString" }), passwordValue: ko.observable("mypass") .extend({ localStore: "myPassword" }), booleanValue: ko.observable(true) .extend({ localStore: "myBoolean" }), optionValues: ["Alpha", "Beta", "Gamma"], selectedOptionValue: ko.observable("Gamma") .extend({ localStore: "mySelected" }), multipleSelectedOptionValues: ko.observable(["Alpha"]) .extend({ localStore: "myMultipleSelect" }), radioSelectedOptionValue: ko.observable("Beta") .extend({ localStore: "myRadio" }) }; ko.applyBindings(viewModel); </script> </body> </html>
To test, refresh the page after changing the values.
Sample Code
Sample code for this post is available in the DevDays GitHub repository. See https://github.com/devdays/object-javascript/tree/master/KnockoutJSDemo/KnockoutJSDemo
The demo is in 09a-PersistedBindingToControls.html.
References
Simple client storage for view models with AmplifyJS and Knockout