$30 off During Our Annual Pro Sale. View Details »

End to End with Polymer

Rob Dodson
September 29, 2015

End to End with Polymer

Video: https://www.youtube.com/watch?v=1f_Tj_JnStA

Developers are really excited by Polymer and Web Components, but they're not always sure how to connect the dots to get a full blown, production-ready app. Where should you store your data? How do you handle user authentication? What are the patterns that make up a good Polymer app? In this session I'll answer these questions, and guide you through the process of building an awesome webapp using Polymer Starter Kit and Firebase.

Rob Dodson

September 29, 2015
Tweet

More Decks by Rob Dodson

Other Decks in Technology

Transcript

  1. None
  2. oh hai! @rob_dodson +RobDodson uber nerd

  3. end to end with Polymer

  4. polymer-todo.firebaseapp.com

  5. #wisdom Solutions to Problems

  6. Problem #1

  7. ( The Blank Page )

  8. Web Development is complicated

  9. Homescreen icons Responsive design Optimizing images Offline caching Concat &

    App manifest Grunt? Gulp?
  10. Polymer Starter Kit

  11. Responsive app layout & routing Components for nearly any app,

    out of the box. Unit test support with Web Component Tester Complete build chain for bringing your app to production.
  12. DOWNLOAD developers.google.com/web/tools/polymer-starter-kit

  13. None
  14. But RoB, I’m super lazy

  15. npm install generator-polymer -g oh snap!

  16. None
  17. Problem #1

  18. Problem #2

  19. the app Breaking up

  20. build your app out of small components

  21. None
  22. <todo-VIEW>

  23. <todo-list>

  24. <todo-ITEM>

  25. <todo-DATA>

  26. <todo-item> <todo-data> <todo-view> <todo-list>

  27. <todo-list> <todo-item> <todo-data> <todo-view>

  28. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true, value: function() { return [ { label: 'My first todo!', isComplete: false }, … ]; } } } }); </script> </dom-module>
  29. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true, value: function() { return [ { label: 'My first todo!', isComplete: false }, … ]; } } } }); </script> </dom-module> Todos are objects in an array
  30. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true, value: function() { return [ { label: 'My first todo!', isComplete: false }, … ]; } } } }); </script> </dom-module> Todos are bindable
  31. an element for data? that’s CRAZY TALK!

  32. <todo-data></todo-data> data provider

  33. <todo-data todos=“{{todos}}”></todo-data> data provider + bindings = sweet!

  34. index.html <body> <template is="dom-bind"> </template> </body>

  35. index.html <body> <template is="dom-bind"> <todo-data todos="{{todos}}"></todo-data> </template> </body>

  36. index.html <body> <template is="dom-bind"> <todo-data todos="{{todos}}"></todo-data> <todo-view todos=“{{todos}}”></todo-view> </template> </body>

  37. <todo-list> <todo-item> <todo-data> <todo-view>

  38. todo-view.html <dom-module id="todo-view"> <template> <todo-list todos="{{todos}}"></todo-list> </template> <script> Polymer({ is:

    'todo-view', properties: { todos: Array } }); </script> </dom-module>
  39. todo-view.html <dom-module id="todo-view"> <template> <todo-list todos="{{todos}}"></todo-list> </template> <script> Polymer({ is:

    'todo-view', properties: { todos: Array } }); </script> </dom-module> Bind data to reduce boilerplate
  40. None
  41. None
  42. None
  43. None
  44. communicate Need to

  45. todo-view.html <dom-module id="todo-view"> <template> <paper-button class=“clear-btn” on-tap=“clearTodos”> <todo-list todos=“{{todos}}" on-delete-todo=“deleteTodo”>

    </todo-list> <todo-input on-add-todo=“addTodo”></todo-input> </template> <script> Polymer({ is: 'todo-view', properties: { todos: Array }, clearTodos: function() { … }, deleteTodo: function() { … }, addTodo: function() { … } }); </script> </dom-module>
  46. todo-view.html <dom-module id="todo-view"> <template> <paper-button class=“clear-btn” on-tap=“clearTodos”> <todo-list todos=“{{todos}}" on-delete-todo=“deleteTodo”>

    </todo-list> <todo-input on-add-todo=“addTodo”></todo-input> </template> <script> Polymer({ is: 'todo-view', properties: { todos: Array }, clearTodos: function() { … }, deleteTodo: function() { … }, addTodo: function() { … } }); </script> </dom-module> Mediate events
  47. todo-view.html addTodo: function(e) { this.push('todos', { label: e.detail.value, isComplete: false

    }); }
  48. Kinda like a traffic cop

  49. <todo-list> <todo-item> <todo-data> <todo-view>

  50. <todo-list> <todo-item> <todo-data> <todo-view>

  51. You could type this yourself

  52. AWESOME Or you could be

  53. yo polymer:element

  54. yo polymer:element

  55. None
  56. todo-list.html <dom-module id="todo-list"> <template> <template is="dom-repeat" items="{{todos}}" as="todo"> <todo-item todo="{{todo}}"></todo-item>

    </template> </template> <script> Polymer({ is: 'todo-list', properties: { todos: Array } }); </script> </dom-module>
  57. todo-list.html <dom-module id="todo-list"> <template> <template is="dom-repeat" items="{{todos}}" as="todo"> <todo-item todo="{{todo}}"></todo-item>

    </template> </template> <script> Polymer({ is: 'todo-list', properties: { todos: Array } }); </script> </dom-module> Iterate over data with dom-repeat
  58. <todo-list> <todo-item> <todo-data> <todo-view>

  59. todo-item.html <dom-module id="todo-item"> <template> <paper-checkbox checked=“{{todo.isComplete}}"></paper-checkbox> <paper-input value=“{{todo.label}}"></paper-input> <paper-icon-button icon=“todo-icons:delete"

    on-tap="_onDelete"> </paper-icon-button> </template> <script> Polymer({ is: 'todo-item', properties: { todo: Object }, _onDelete: function() { this.fire(‘delete-todo’, {todo: this.todo}); } }); </script> </dom-module>
  60. todo-item.html <dom-module id="todo-item"> <template> <paper-checkbox checked=“{{todo.isComplete}}"></paper-checkbox> <paper-input value=“{{todo.label}}"></paper-input> <paper-icon-button icon=“todo-icons:delete"

    on-tap="_onDelete"> </paper-icon-button> </template> <script> Polymer({ is: 'todo-item', properties: { todo: Object }, _onDelete: function() { this.fire(‘delete-todo’, {todo: this.todo}); } }); </script> </dom-module> Bind data to reduce boilerplate
  61. todo-item.html <dom-module id="todo-item"> <template> <paper-checkbox checked=“{{todo.isComplete}}"></paper-checkbox> <paper-input value=“{{todo.label}}"></paper-input> <paper-icon-button icon=“todo-icons:delete"

    on-tap="_onDelete"> </paper-icon-button> </template> <script> Polymer({ is: 'todo-item', properties: { todo: Object }, _onDelete: function() { this.fire(‘delete-todo’, {todo: this.todo}); } }); </script> </dom-module> Mediate events
  62. None
  63. Problem #2

  64. None
  65. Problem #3

  66. production Getting to

  67. None
  68. REALTIME DATABASE AUTHENTICATION HOSTING

  69. REALTIME DATABASE

  70. // Write some data ref.set({ name: ‘Rob Dodson’ }); //

    Push Array-like data ref.push({ isComplete: false, label: ‘A new todo!’ }); // Create a connection to Firebase var ref = new Firebase(‘https://<YOUR-FIREBASE-APP>.firebaseio.com');
  71. None
  72. // Listen for changes from Firebase ref.on('value', function(snapshot) { console.log(snapshot.val());

    });
  73. None
  74. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true, value: function() { return [ { label: 'My first todo!', isComplete: false }, … ]; } }
  75. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true, value: function() { return [ { label: 'My first todo!', isComplete: false }, … ]; } } Just an array
  76. None
  77. None
  78. There’s an element for that

  79. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true, value: function() { return [ { label: 'My first todo!', isComplete: false }, … ]; } }
  80. todo-data.html <dom-module id="todo-data"> <script> Polymer({ is: 'todo-data', properties: { todos:

    { notify: true } } }); </script> </dom-module>
  81. todo-data.html <dom-module id="todo-data"> <template> <firebase-collection data=“{{todos}}" location="https://polymer-todo.firebaseio.com"> </firebase-collection> </template> <script>

    Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script> </dom-module>
  82. todo-data.html <dom-module id="todo-data"> <template> <firebase-collection data=“{{todos}}" location="https://polymer-todo.firebaseio.com"> </firebase-collection> </template> <script>

    Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script> </dom-module>
  83. todo-data.html <dom-module id="todo-data"> <template> <firebase-collection data=“{{todos}}" location="https://polymer-todo.firebaseio.com"> </firebase-collection> </template> <script>

    Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script> </dom-module>
  84. todo-data.html <dom-module id="todo-data"> <template> <firebase-collection data=“{{todos}}" location=“{{location}}“> </firebase-collection> </template> <script>

    Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script> </dom-module>
  85. index.html <body> <template is="dom-bind"> <todo-data todos=“{{todos}}"></todo-data> <todo-view todos=“{{todos}}”></todo-view> </template> </body>

  86. index.html <body> <template is="dom-bind"> <todo-data todos=“{{todos}}" location=“https://polymer-todo.firebaseio.com"> </todo-data> <todo-view todos=“{{todos}}”></todo-view>

    </template> </body>
  87. None
  88. None
  89. None
  90. None
  91. REALTIME DATABASE AUTHENTICATION HOSTING

  92. AUTHENTICATION

  93. None
  94. { "rules": { "users": { "$uid": { ".write": "auth.uid ===

    $uid", ".read": "auth.uid === $uid" } } } }
  95. { "rules": { "users": { "$uid": { ".write": "auth.uid ===

    $uid", ".read": "auth.uid === $uid" } } } } Path to a unique ID
  96. { "rules": { "users": { "$uid": { ".write": "auth.uid ===

    $uid", ".read": "auth.uid === $uid" } } } } The user’s unique ID
  97. { "rules": { "users": { "$uid": { ".write": "auth.uid ===

    $uid", ".read": "auth.uid === $uid" } } } } Authenticated user’s ID must match the path
  98. // Authenticate a user with Google Sign in ref.authWithOAuthPopup('google', function(error,

    authData) { // Get the user’s todo list this.userRef = this.ref.child(‘users/‘ + authData.uid); });
  99. There’s an element for that too

  100. todo-data.html <dom-module id="todo-data"> <template> <firebase-collection data=“{{todos}}" location="{{location}}"> </firebase-collection> </template> <script>

    Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script> </dom-module>
  101. todo-data.html <dom-module id="todo-data"> <template> <firebase-auth id="auth" location="{{location}}" provider="google" user="{{user}}"> </firebase-auth>

    <firebase-collection data=“{{todos}}" location="{{location}}"> </firebase-collection> </template> <script> Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script>
  102. todo-data.html <dom-module id="todo-data"> <template> <firebase-auth id="auth" location="{{location}}" provider="google" user="{{user}}"> </firebase-auth>

    <firebase-collection data=“{{todos}}" location="{{location}}"> </firebase-collection> </template> <script> Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script>
  103. todo-data.html <dom-module id="todo-data"> <template> <firebase-auth id="auth" location="{{location}}" provider="google" user="{{user}}"> </firebase-auth>

    <firebase-collection data=“{{todos}}" location="{{location}}"> </firebase-collection> </template> <script> Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script>
  104. todo-data.html <dom-module id="todo-data"> <template> <firebase-auth id="auth" location="{{location}}" provider="google" user="{{user}}"> </firebase-auth>

    <firebase-collection data=“{{todos}}" location="{{location}}"> </firebase-collection> </template> <script> Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script>
  105. todo-data.html <paper-dialog modal opened="{{!user}}"> … </paper-dialog> No user? Open the

    sign-in dialog
  106. todo-data.html _userChanged: function(user) { if (user) { this.userLocation = [

    this.location, 'users', this.user.uid ].join('/'); } } Find the user’s todos in Firebase
  107. todo-data.html <dom-module id="todo-data"> <template> <firebase-auth id="auth" location="{{location}}" provider="google" user="{{user}}"> </firebase-auth>

    <firebase-collection data=“{{todos}}" location="{{userLocation}}"> </firebase-collection> </template> <script> Polymer({ is: 'todo-data', properties: { todos: { notify: true } } }); </script>
  108. None
  109. REALTIME DATABASE AUTHENTICATION HOSTING

  110. HOSTING

  111. npm install -g firebase-tools

  112. None
  113. None
  114. Problem #3

  115. Polymer starter kit Get rolling with

  116. Small elements Break your app into

  117. get to production Use Firebase to

  118. bit.ly/polycasts

  119. thanks! @rob_dodson +RobDodson credits Images by Gregor Črešnar, Aenne Brielmann,

    Michal Beno, Golden Roof, Rohith M S, Till Teenck, Julien Deveaux, Garrett Knoll, Matt Brooks, Nick Kinling, Brad Ashburn, Juan Pablo Bravo, Nicolas Vicent, Aha-Soft, Nicky Knicky, Max Cougar Oswald & Nihir Shah