Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Google Stack: Polymer and Firebase

Google Stack: Polymer and Firebase

Recently, there has been a move by the major browser vendors to simplify the development of software through the new Web Components standard. Polymer, a free open-source library by Google, makes use of this new standard to make web software reusable (see YouTube, newly built using Polymer). Firebase, a collection of cloud services, combined with Polymer, make for a compelling development platform for web and mobile apps and the new push for mobile web apps. In this talk we will cover Polymer, Firebase and the major services of Google Functions, Hosting, RealTime and Firestore Databases. Examples will be shown of a production app in progress.

Moncton Developer User Group

September 18, 2018
Tweet

More Decks by Moncton Developer User Group

Other Decks in Programming

Transcript

  1. We are Looking for developers... We work on contracts building

    large scale web apps... Startups need to build apps quickly, and scale from the start Looking for contractors to help front-end (Polymer) and back-end (Firebase) Do contacts? Email me: [email protected]
  2. Going to the Web Browser API and DOM structures full

    of legacy code Many parts missing: 1. No general loading Mechanism for code 2. Managing CSS-JS-HTML together 3. HTML limitations, everything is a <div>, slow parsing 4. CSS namespace leaking into other rules, kill entire app layout 5. No OOP or MVC patterns for creating apps at scale
  3. A scalable dev platform provides solutions. Managing Code/Implementation/Deployment/Versioning Managing OS/Services/Libs/VMs

    Managing Networking/Security Managing Performance Managing Memory/Disk/Network Improve focus on code, release faster, release better code.
  4. Polymer Designed on top of Webcomponents.org specification for custom HTML

    Client library for building <custom-elements> Fixes the CSS/JS/HTML mess with integrated <template> model Fixes CSS leakage with Shadow DOM Fixes code loading with HTML imports Fixes OOP/MVC with Components and Dataflow 3 releases 1.0 (2014), 2.0 (2017), 3.0 (2018) https://www.polymer-project.org/
  5. Polymer 2.0 <link rel="import" href="../polymer/polymer-element.html"> <dom-module id="x-custom"> <!-- Optional shadow

    DOM template --> <template> <style> /* CSS rules for your element */ </style> <!-- shadow DOM for your element --> <div>{{greeting}}</div> <!-- data bindings in local DOM --> </template> <script> // Define the element's API using an ES2015 class class XCustom extends Polymer.Element { static get is() { return 'x-custom'; } // Declare properties for the element's public API static get properties() { return { greeting: { type: String, value: "Hello!" } } } // Add methods to the element's public API greetMe() { console.log(this.greeting); } } // Register the x-custom element with the browser customElements.define(XCustom.is, XCustom); </script> </dom-module> Custom Elements ES5 Classes Shadow DOM Events Dataflow System Utility Functions HTML Imports Bower package manager
  6. Polymer 3.0 import {PolymerElement, html} from '@polymer/polymer/polymer-element.js'; // Define the

    element's API using an ES2015 class class XCustom extends PolymerElement { // Define optional shadow DOM template static get template() { return html` <style> /* CSS rules for your element */ </style> <!-- shadow DOM for your element --> <div>[[greeting]]</div> <!-- data bindings in shadow DOM --> `; } // Declare properties for the element's public API static get properties() { return { greeting: { type: String } } } constructor() { super(); this.greeting = 'Hello!'; } // Add methods to the element's public API greetMe() { console.log(this.greeting); } } // Register the x-custom element with the browser customElements.define('x-custom', XCustom); Custom Elements ES5 Classes Shadow DOM Events Dataflow System React-style UX template ES6 Modules - import Npm package manager
  7. Custom Element You can define your own HTML tags <link

    rel="import" href="../bower_components/polymer/polymer-element.html"> <dom-module id="new-component"> <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } } window.customElements.define(NewComponent.is, NewComponent); </script> </dom-module> // Then use it… <new-component></new-component>
  8. Custom Element You can define your own HTML tags Use

    these new tags in other HTML Use the new tags in new tags Elements can extend other elements to use inheritance. Elements can be mixed with other elements (mixins). <link rel="import" href="../bower_components/polymer/polymer-element.html"> <dom-module id="new-component"> <template> <style> :host { display: block } /* add your custom CSS here... */ </style> <!-- UX HTML goes here... --> </template> <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } static get properties() { return { // Add new properties here... name: { type: String, value: "" } }; } // Use: initializing state set up event listeners, create shadow dom. constructor() { super(); } // Use for one-time configuration of your component after local DOM is initialized. ready() { super.ready(); } // add your own methods here... } window.customElements.define(NewComponent.is, NewComponent); </script> </dom-module>
  9. Add Properties Properties exist with the instance of component and

    are encapsulated. Can be set to any JS type. Can have default values. HTML to camelCase conversion. <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } static get properties() { return { // Add new properties here... userName: { type: String, value: "" }, manager: { type: Boolean }; } </script> </dom-module> // Use the component and pass in values to properties <new-component user-name=”My Name” manager></new-component>
  10. Add Objects/Arrays Objects and Arrays can be set to defaults

    with use of function. Properties accessed in methods using this.propertyName notation. <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } static get properties() { return { // Add new properties here... user: { type: Object, value: function() { return {}; } }, list: { type: Array, value: function() { return []; } }; } </script> </dom-module> // Use the component and pass in values to properties <new-component user=’{name:”bob”}’ list=’[1,2,3,4]’></new-component>
  11. Observed Properties Properties can trigger code when value is changed.

    <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } static get properties() { return { userName: { type: String, value: "", observer: ‘_nameChanged’ }, }; } _nameChanged(value) { // the new value of name console.log(“my name changed: “, value); } </script> </dom-module>
  12. Computed Properties Properties can be computed based on some code,

    and depend on other properties. <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } static get properties() { return { count: { type: Number, computed: ‘_computeCount(total)’ }, total: { type: number }; } _computeCount(value) { return value * 2; } </script> </dom-module>
  13. Data Flow Data can flow into, or out of a

    component. Values of properties are read in the DOM with double square brackets [[x]] for 1-way data flow. Values of properties are read in the DOM with double curly brackets {{x}} for 2-way data flow. 1. Refer to external JS object 2. Refer to local <div> 3. Data binding to set value <dom-module id="name-card"> <template> <div>[[name.first]] [[name.last]]</div> </template> <script> class NameCard extends Polymer.Element { static get is() { return "name-card"; } constructor() { super(); this.name = {first: 'Kai', last: 'Li'}; } } customElements.define(NameCard.is, NameCard); </script> </dom-module>
  14. Add methods Components, because they are a class, can have

    its own methods. Methods can be called from outside the component using the id and $ operator. The component must be in the calling components <template>. Methods can have any number of parameters. <script> class NewComponent extends Polymer.Element { static get is() { return 'new-component'; } static get properties() { return { user: { type: Object, value: function() { return {}; } }, list: { type: Array, value: function() { return []; } }; // Add new methods here... getUserName() { return this.user.name; } } </script> </dom-module> // call method on a component using id <new-component id=”comp” user=’{name:”bob”}’ list=’[1,2,3,4]’> </new-component> <script> var name = this.$.comp.getUserName(); </script>
  15. Handle Events Components can handle events on their own sub-components

    and link these to their methods. <dom-module id="x-custom"> <template> <button on-click="handleClick">Kick Me</button> </template> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} handleClick() { console.log('Ow!'); } } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  16. Add Components Components can be added to the template section

    of a component. <dom-module id="x-custom"> <template> <div> <button >Kick Me</button> </div> </template> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  17. Import Components Any HTML component, Polymer component, or custom component

    can be added. For custom and polymer components you need to import the html file that defines it. <link rel="import" href="../paper-button/paper-button.html"> <dom-module id="x-custom"> <template> <div> <paper-button raised>Kick Me</paper-button> </div> </template> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  18. Add CSS Components can be styled using local CSS. <dom-module

    id="x-custom"> <style> :host { padding: 4px; background-color: gray; } .container { color: var(--my-toolbar-title-color); } </style> <template> <div class=”container”> <button >Kick Me</button> </div> </template> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  19. CSS Components The name of a component can be used

    to customize its look. You can also use custom CSS properties to change the inside of the component. <link rel="import" href="../paper-button/paper-button.html"> <dom-module id="x-custom"> <style> paper-button { color: var(--title-color); --paper-button-style: { Font-size: 12px; } } </style> <template> <div> <paper-button raised>Kick Me</paper-button> </div> </template> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  20. Dispatch Events Components can also communicate up the DOM by

    using Event system in browser. <dom-module id="x-custom"> <template> <button on-click="handleClick">Kick Me</button> </template> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} handleClick() { this.dispatchEvent( new CustomEvent('sign-out', {detail:{count:2}}, { bubbles: true, composed: true })); } } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  21. Listen for Events Components can also listen to Events dispatched

    by other components. <dom-module id="x-custom"> <script> class XCustom extends Polymer.Element { static get is() {return 'x-custom'} constructor() { super(); this.addEventListener('sign-out', function(event) { console.log(“event received”); }); } } customElements.define(XCustom.is, XCustom); </script> </dom-module>
  22. Building an App... Create Firebase account Create firebase project, cloud,

    then shell Create polymer project Connect polymer to firebase through polymerfire $firebase serve $firebase deploy https://firebase.google.com
  23. Polymer Tools $nvm install node $npm install -g polymer-cli $polymer

    init $bower install --save PolymerElements/<element-name> #^<version> $polymer serve $polymer build
  24. App Structure index.html <chat-app> <my-login> <my-app> <app-route> <app-drawer> <app-toolbar> <app-header>

    <iron-pages> <view-1> <view-2> <view-3> <firebase-app> <firebase-auth> <firebase-query>
  25. Add App Component Create a new component to interface between

    Polymer and index file. venn-chat-app.html <dom-module id="venn-chat-app"> <template> </template> <script> class VennChatApp extends Polymer.Element { static get is() {return 'venn-chat-app'} } customElements.define(VennChatApp.is, VennChatApp); </script> </dom-module> index.html <html> <!-- Load your application shell --> <link rel="import" href="src/venn-chat-app.html"> </head> <body> <venn-chat-app></venn-chat-app> </body> </html>
  26. Authenticate Transfer the credentials into your app using polymerfire firebase-app

    component. <link rel="import" href="../polymerfire/firebase-app.html"> <dom-module id="venn-chat-app"> <template> <firebase-app project-id="vennchat" api-key="AIzaSyDXLvla2maDj1BOJvvwLWvHWqaL4650CPM" auth-domain="vennchat.firebaseapp.com" database-url="https://vennchat.firebaseio.com" storage-bucket="vennchat.appspot.com" messaging-sender-id="1064751089005"> </firebase-app> <my-login></my-login> </template> <script> class VennChatApp extends Polymer.Element { static get is() {return 'x-custom'} } customElements.define(VennChatApp.is, VennChatApp); </script> </dom-module> bower install --save FirebaseExtended/polymerfire
  27. Create Login Form Login will use the <firebase-auth> to validate

    your Google account. Add login() and logout() methods to call auth object API. “dom-if” template used to show/hide components based on existence of user. <link rel="import" href="../polymerfire/firebase-auth.html"> <dom-module id="my-login"> <template> <firebase-auth id="auth" user="{{user}}" provider="google"> </firebase-auth> <template is="dom-if" if="{{user}}"> <my-app user=[[user]]></my-app> </template> <template is="dom-if" if="{{!user}}"> <h1>Venn Chat App</h1> <paper-button raised on-click="login">Login with Google</paper-button> </template> </template> <script> class MyLogin extends Polymer.Element { static get is() {return 'my-login'} login() { return this.$.auth.signInWithPopup(); } logout() { return this.$.auth.signOut(); } } customElements.define(MyLogin.is, MyLogin); </script> </dom-module> bower install --save PolymerElements/paper-button#^2
  28. Add sign-out listener <my-login> handles signing out, so we send

    an event from <my-app> to trigger API call. <link rel="import" href="../polymerfire/firebase-auth.html"> <dom-module id="my-login"> <template> ... </template> <script> class MyLogin extends Polymer.Element { static get is() {return 'my-login'} ready() { super.ready(); this.addEventListener('sign-out', function () { this.logout(); }.bind(this)); } } customElements.define(MyLogin.is, MyLogin); </script> </dom-module> <dom-module id="my-app"> <template> ... </template> <script> class MyApp extends Polymer.Element { static get is() {return 'my-app'} _logout() { this.dispatchEvent( new CustomEvent('sign-out', { bubbles: true, composed: true })); } } window.customElements.define(MyApp.is, MyApp); </script> </dom-module>
  29. Firebase RTDB Firebase is a realtime NoSQL DB with listeners.

    Can be accessed directly from app, no need to have App-tier. Can be accessed from Functions too. Updates all client data when changes are made, no polling. Modify <view-1> for <firebase-auth> and <firebase-query>. <link rel="import" href="../bower_components/polymer/polymer-element.html"> <link rel="import" href="../bower_components/polymerfire/firebase-query.html"> <link rel="import" href="../bower_components/polymerfire/firebase-auth.html"> <link rel="import" href="shared-styles.html"> <dom-module id="my-view1"> <template> <style include="shared-styles"> :host { display: block; } </style> <firebase-auth user="{{user}}"></firebase-auth> <firebase-query id="query" path="posts" data="{{posts}}"> </firebase-query> </template> <script> class MyView1 extends Polymer.Element { static get is() { return 'my-view1'; } static get properties() { return { user: Object, posts: { type: Object } }; } } window.customElements.define(MyView1.is, MyView1); </script> </dom-module>
  30. Firebase RTDB Add sub-components for list and input post. Add

    CSS to style the components. <link rel="import" href="../bower_components/paper-input/paper-input.html"> <dom-module id="my-view1"> <template> <style include="shared-styles"> :host { display: block; } // Add CSS here </style> <div id="messages" class="card message-pane"> <template is="dom-repeat" items="{{posts}}" as="post"> <div class="post flex-row-left"> <img class="face" src="[[post.photo]]"/> <div class="flex-col-left"> <b>[[post.name]]</b> <span>[[post.text]]</span> </div> </div> </template> </div> <div class="message"> <img class="face" src="[[user.photoURL]]"/> <paper-input id="input" label="message"></paper-input> <paper-button raised on-click="_post">Post</paper-button> </div> </template> <script> ... bower install --save PolymerElements/paper-input#^2
  31. Firebase RTDB Add post() method to write to database. Used

    id of <firebase-query> to get reference to collection and adds a new child with push() method to auto-generate key. Use the Key to set() the value of the document to the object we create from user and text. <dom-module id="my-view1"> <template> <style include="shared-styles"> :host { display: block; } </style> </template> <script> class MyView1 extends Polymer.Element { static get is() { return 'my-view1'; } static get properties() { return { user: Object, posts: { type: Object } }; } _post() { var msg = this.$.input.value; if (msg) { var key = this.$.query.ref.push().key; this.$.query.ref.child(key).set({ photo: this.user.photoURL, name: this.user.displayName, text: msg, timestamp: -Date.now() }); this.$.input.value = ""; } } } window.customElements.define(MyView1.is, MyView1); </script> </dom-module>
  32. Deploy App to Hosting... $firestore deploy Use your phone to

    get venchat.firebaseapp.com For production you would perform: $polymer build <es5/es6> to bundle and obfuscate JS code Create all the security rules on database, firestore, storage Create your own ico/images for mobile app homepage Add analytics to your app to watch use/crashes
  33. Firebase Functions // Listens for new messages added to /posts/:pushId/text

    and creates an // uppercase version of the message to /posts/:pushId/text exports.makeUppercase = functions.database.ref('/messages/{pushId}/text') .onCreate((snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const text = snapshot.val(); const uppercase = text.toUpperCase(); return snapshot.ref.parent.child('text').set(uppercase); });
  34. Firebase Functions - CRUD const express = require('express'); const cors

    = require('cors'); const app = express(); // Automatically allow cross-origin requests app.use(cors({ origin: true })); // Add middleware to authenticate requests app.use(myMiddleware); // build multiple CRUD interfaces: app.get('/:id', (req, res) => res.send(Widgets.getById(req.params.id))); app.post('/', (req, res) => res.send(Widgets.create())); app.put('/:id', (req, res) => res.send(Widgets.update(req.params.id, req.body))); app.delete('/:id', (req, res) => res.send(Widgets.delete(req.params.id))); app.get('/', (req, res) => res.send(Widgets.list())); // Expose Express API as a single Cloud Function: exports.widgets = functions.https.onRequest(app);
  35. Firebase RTDB Make changes after deployment that does not interrupt

    your users. $firebase deploy <dom-module id="my-view1"> <template> <style include="shared-styles"> :host { display: block; } </style> </template> <script> class MyView1 extends Polymer.Element { static get is() { return 'my-view1'; } static get properties() { return { user: Object, posts: { type: Object } }; } ready() { super.ready(); setTimeout(() => this._scrollToBottom(), 500); } // at end of post()... setTimeout(() => this._scrollToBottom(), 500); _scrollToBottom(){ this.$.messages.scrollTop = this.$.messages.scrollHeight; } } window.customElements.define(MyView1.is, MyView1); </script> </dom-module>
  36. Other Topics… Firebase Storage (for media) SMS Messaging/Email Push Notification

    Firestore Beta (nosql that scales) NoSQL Normalization & Performance patterns Integration with Google Cloud Platform...
  37. Thank You Many thanks to all developers who came to

    talk! Special thanks to MUG and Doreen for opportunity. Extra special thanks to Marc Albert for pizza and support! Simon Gauvin: [email protected]