mjuniper.github.io/presentations/ds2018/ambitious-apps-architecture.html

github.com/mjuniper/presentations

Services

A Service is an Ember object that lives for the duration of the application, and can be made available in different parts of your application.

Services

Services are useful for features that require shared state or persistent connections.

Creating a service


$ ember g service session
          

Service definition


            import Service from '@ember/service';
            import { computed } from '@ember/object';

            export default Service.extend({
              fetchStuff () {
                // this could be async
                return [];
              }
            });
          

Injecting a service


            import { inject as service } from '@ember/service';
          

Ad hoc injection:


            session: service()

            // available in this class as this.get('session');
          
Or

            userSession: service('session')

            // now available in this class as this.get('userSession');
          

Injecting a service

Injecting into all instances of a type:


            // app/initializers/session.js

            export function initialize(application) {
              application.inject('route', 'session', 'service:session');
            }

            export default {
              initialize
            };

            // now available in all routes as this.get('session');
          

Components

  • Composed of a js file and/or a template
  • Isolated - do not have access to their outer context
  • Get data & actions via attributes (data binding)

Creating a component


            $ ember g component user-profile
          

Component definition


            // app/components/user-profile/component.js
            import Component from '@ember/component';
            import { computed } from '@ember/object';

            export default Component.extend({

              tagName: 'h2',

              classNames: [ 'user-profile-component' ],

              greeting: computed('firstName', 'lastName', function () {
                return `Hello, ${this.get('firstName')} ${this.get('lastName')}!`;
              })

            });
          

            {{!-- app/components/user-profile/template.hbs --}}
            {{greeting}}
          

Component usage


            {{!-- some template somewhere --}}
            {{user-profile firstName="Mike" lastName="Juniper"}}
          

            <!-- renders -->
            <h2 class="user-profile-component">Hello, Mike Juniper!</h2>
          

Component lifecycle hooks

  • init
  • didInsertElement
  • didReceiveAttrs
  • didUpdateAttrs
  • willDestroyElement
  • many more


            this._super(...arguments);
          


            this.$(...);
          

DDAU, baby

A best practice

Child components do not mutate data they do not own.

Instead they:

  • Send actions up to the "owner"
  • Who mutates the data
  • Which flows back down to the child component via bound attributes