blob: bc5b434293058da5597282c91fe1c399e963a288 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Defines StoreClient, a Polymer behavior to tie a front-end
* element to back-end data from the store.
*/
cr.define('bookmarks', function() {
/**
* @polymerBehavior
* @implements {StoreObserver}
*/
const StoreClient = {
created: function() {
/**
* @type {!Array<{
* localProperty: string,
* valueGetter: function(!BookmarksPageState)
* }>}
*/
this.watches_ = [];
},
attached: function() {
bookmarks.Store.getInstance().addObserver(this);
},
detached: function() {
bookmarks.Store.getInstance().removeObserver(this);
},
/**
* Watches a particular part of the state tree, updating |localProperty|
* to the return value of |valueGetter| whenever the state changes. Eg, to
* keep |this.item| updated with the value of a node:
* watch('item', (state) => state.nodes[this.itemId]);
*
* Note that object identity is used to determine if the value has changed
* before updating the UI, rather than Polymer-style deep equality. If the
* getter function returns |undefined|, no changes will propagate to the UI.
*
* Typechecking is supressed because this conflicts with
* Object.prototype.watch, which is a Gecko-only method that is recognized
* by Closure.
* @suppress {checkTypes}
* @param {string} localProperty
* @param {function(!BookmarksPageState)} valueGetter
*/
watch: function(localProperty, valueGetter) {
if (!this.getPropertyInfo(localProperty).defined) {
console.error(
'No property ' + localProperty + ' defined on ' + this.is);
}
this.watches_.push({
localProperty: localProperty,
valueGetter: valueGetter,
});
},
/**
* Helper to dispatch an action to the store, which will update the store
* data and then (possibly) flow through to the UI.
* @param {?Action} action
*/
dispatch: function(action) {
bookmarks.Store.getInstance().dispatch(action);
},
/**
* Helper to dispatch a DeferredAction to the store, which will
* asynchronously perform updates to the store data and UI.
* @param {DeferredAction} action
*/
dispatchAsync: function(action) {
bookmarks.Store.getInstance().dispatchAsync(action);
},
/** @param {string} newState */
onStateChanged: function(newState) {
this.watches_.forEach((watch) => {
const oldValue = this[watch.localProperty];
const newValue = watch.valueGetter(newState);
// Avoid poking Polymer unless something has actually changed. Reducers
// must return new objects rather than mutating existing objects, so
// any real changes will pass through correctly.
if (oldValue === newValue || newValue === undefined)
return;
this[watch.localProperty] = newValue;
});
},
updateFromStore: function() {
if (bookmarks.Store.getInstance().isInitialized())
this.onStateChanged(bookmarks.Store.getInstance().data);
},
/** @return {!BookmarksPageState} */
getState: function() {
return bookmarks.Store.getInstance().data;
},
};
return {
StoreClient: StoreClient,
};
});