UNPKG

product-admin

Version:

EA admin screens

206 lines (174 loc) 8.96 kB
# Routing For Google's `app-route` component docs, see (Google app-route)[https://github.com/PolymerElements/app-route] Handle app routing using Polymer's `app-route` and `app-location` components. ## Getting Started `bower install polymer PolymerElements/app-route PolymerElements/iron-pages --save` ## Simple Router Example A barebones routing example. When you change the URL to `[protocol][host]/#/[page]` it will select the corresponding`iron-pages` element automatically, and show it, also invoking that element's `attach` method. For example, `http://localhost/#/p1` ```html <!-- src/bm-my-router.html --> <link rel="import" href="../../bower_components/polymer/polymer.html" /> <link rel="import" href="../../bower_components/app-route/app-location.html" /> <link rel="import" href="../../bower_components/app-route/app-route.html" /> <link rel="import" href="../../bower_components/iron-pages/iron-pages.html" /> <dom-module is="bm-my-router"> <template> <app-location use-hash-as-path route="{{route}}"></app-location> <app-route route="{{route}}" pattern="/:page" data="{{data}}" ></app-route> <iron-pages id="manWithTheIronPages" selected="{{data.page}}" attr-for-selected="id"> <h3 id="p1">Page 1</h3> <h3 id="p2">Page 2</h3> <h3 id="p3">Page 3</h3> </iron-pages> </template> </dom-module> <script> ``` ```Javascript // src/bm-my-router.html (function (Polymer, undefined) { "use strict"; Polymer({ is: "bm-my-router", _routeDataChanged: _routeDataChanged, properties: { data: { type: Object, observer: "_routeDataChanged" } } }); function _routeDataChanged (newData, oldData) { const SELECTED_PAGE_ID = "#".concat(newData.page); const PAGE_ELEMENT = this.$$(SELECTED_PAGE_ID); // Ensure your ID selector is a valid one (doesn't start with a number), otherwise your code will complain this.$.manWithTheIronPages.appendChild(PAGE_ELEMENT); } })(Polymer); ``` ```html <!-- src/bm-my-router.html --> </script> ``` ## Example App ### index.html This page is in charge of loading all of your app's components which are required at this level. Unless this page itself is the router, that should be the only component added to the DOM at this time. ```html <!DOCTYPE html> <html> <head> <link rel="import" href="bower_components/polymer/polymer.html" /> <link rel="import" href="components/bm-my-router.html" /> <link rel="import" href="components/your-component-1/your-component-1.html" /> <link rel="import" href="components/your-component-2/your-component-2.html" /> <link rel="import" href="components/your-component-3/your-component-3.html" /> </head> <body> <bm-my-router></bm-my-router> </body> </html> ``` ### Main Router Component This component is responsible for routing to the various pages of your application. Each of those pages loads its own components and behaviors as normal. If using the provided route observer, each time the `window.location` is changed, the `attach` method of the routed component will be called. Generally it's best to design each component, and the application, in such a way that each of these main components can operate independently of the others. This is in order to avoid over-burdening the router with app/component-specific logic. Effectively these main components become much more deliberate and clean. A particular component can have its own subrouting, which allows encapsulation of data to a particular area of interest. ```html <!-- src/bm-my-router.html --> <link rel="import" href="bower_components/app-route/app-location.html" /> <link rel="import" href="bower_components/app-route/app-route.html" /> <link rel="import" href="bower_components/iron-pages/iron-pages.html" /> <dom-module is="bm-my-router"> <template> <app-location use-hash-as-path route="{{route}}"></app-location> <app-route route="{{route}}" pattern="/:page" data="{{data}}" ></app-route> <app-route route="{{route}}" pattern="/page2/:parm1/:parm2/:parm3/:parm4" data="{{subpageData}}" ></app-route> <app-route route="{{route}}" pattern="/page3/:subPage" tail="{{subroute}}" ></app-route> <iron-pages id="manWithTheIronPages" selected="{{data.page}}" attr-for-selected="id" > <your-component-1 id="page1" route="{{route}}" ></your-component-1> <your-component-2 id="page2" parm1="{{subpageData.parm1}}" parm2="{{subpageData.parm2}}" parm3="{{subpageData.parm3}}" parm4="{{subpageData.parm4}}" route="{{route}}" ></your-component-2> <your-component-3 id="page3" route="{{subroute}}" ></your-component-3> </iron-pages> </template> </dom-module> <script> ``` ```Javascript (function (Polymer, undefined) { "use strict"; // This const is referenced in _routeDataChanged const PAGE = Object.freeze({ page1: "your-component-1", page2: "your-component-2", page3: "your-component-3" }); Polymer({ is: "bm-my-router", _routeDataChanged: _routeDataChanged, properties: { data: { type: Object, observer: "_routeDataChanged" } } }); /** * @description This method is invoked on change of {{data}} which means that the main route has changed. It finds the mapped page as indicated by the main path. If the page is the same as the old page or doesn't exist, it does nothing. If the page is in fact different, it appends the page to the Iron Pages element—of which it is already a child—which invokes that component's #attach method and allows it to handle the event internally. This is advantageous to ivoking the #ready method again as ready should only happen once upon component load and subsequent #ready invocation per app session, while still allowing certain initialization routines to run such as querying an API for fresh data, since normally we don't wish for the component to continue execution despite being hidden, we could also take advantage of #detach per component. * @param {object} newData - {{data}} updated to * @param {object} oldData - {{data}} updated from * @return void */ function _routeDataChanged (newData, oldData) { oldData = oldData || {}; newData = newData || {}; // Get dynamically bound node (because we're potentially adding / removing) var pageElement = this.$$(PAGE[newData.page]); if (!pageElement || newData.page === oldData.page) return; this.$.manWithTheIronPages.appendChild(pageElement); } })(Polymer); ``` ```html </script> ``` #### app-route All routes are defined as `app-route` components. The main component should have a data property which will be used to determine what page for the `iron-pages` component to show. It has the following properties which can be of use: * `route` - Main route binding used by the `app-location` component. Can be used by individual page components if they have individual subrouting schemes. * `pattern` - URL to match for a given route. This determines the `{{data}}` and `{{tail}}` bindings. * `data` - Route parameters as determined by their interpretation in a given URL. * `tail` - Additional route beyond the main pattern. Routes 2 and 3 are examples to demonstrate usage. In the simplest routing scheme, only the main route is required as it will interpret the current page and select an `iron-page` accordingly. #### app-location Keeps track of the current URL and any navigation changes which take place. Using `use-hash-as-path` allows us to use `window.location.hash` as the primary means of navigation, similar to `ngRoute` or `uiRouter` in Angular-world. For the most part, this component just needs to be used once and bound to `{{route}}` in order to reliably handle URL updates. #### iron-pages Show / hide pages depending on the current `{{route}}` interpreted as an ID of a particular page element. For instance, navigating to #/page1 will trigger the route for `your-component-1`, navigating to #/page2/helloWorld/2017-01-01/apples/oranges will navigate to `your-component-2` passing in the additional path sections as parameters of that route's data property `subpageData`; navigating to #/page3/subpage1/profile0 will trigger the `your-component-3` route and will pass in the `{{tail}}` (additional pattern sections) as that page's route. #### script Maps each page to a particular ID (arguably unnecessary as there must be a programmatic approach to getting the same data. See the simple routing example above), and upon observing the main route has changed, it will determine whether to reattach a relevant component, thus invoking its `attach` method. Additional properties can be observed here, but as always it's best to keep it simple and only add such imperitive logic as necessary.