Single Page Apps – Store Your Data Locally in Knockout Using AmplifyJS

image8[3]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:

The extender can be implemented as follows:

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> <script src="Scripts/amplify.js"></script> <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

Advertisements