Pro Yearly is on sale from $80 to $50! »

End to End with Polymer

95c3a3b33ea51545229c625bef42e343?s=47 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.

95c3a3b33ea51545229c625bef42e343?s=128

Rob Dodson

September 29, 2015
Tweet

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