Building Ember Apps with Duplos®
Background: wallpaperscraft
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/5683665/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6481348/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6481354/GitHub-Mark-Light-120px-plus.png)
jonkilroy jkusa
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6476856/pasted-from-clipboard.png)
Photos: AIatariel, polarstein, Lego
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6476867/pasted-from-clipboard.png)
Web Apps
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6511254/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/5683665/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/5683728/Yahoo_Logo_White.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/5683702/2000px-Verizon_2015_logo_-vector.svg.png)
Web Services
Data Pipelines
2007
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6465649/pasted-from-clipboard.png)
Photo: Lego Ideas
Data Systems
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6670414/Screenshot_2019-10-18_at_3.48.05_AM.png)
How We Build Apps
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6465649/pasted-from-clipboard.png)
Photo: Lego Ideas
Data Systems
Data Systems 101
-
Table: Collection of related data
-
📐 Metric: Measurement/Fact
-
Dimension: Categorizes Metric
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6595562/pasted-from-clipboard.png)
Part Num | Brick Type | Brick Color | Brick Count |
---|---|---|---|
3003 | 2x2 | Red | 13 |
3003 | 2x2 | Blue | 21 |
3001 | 4x2 | Red | 34 |
Dimension
Metric
Dimension
Dimension
{ "date":"2019-10-17", "event": brickPurchase ... },
{ "date":"2019-10-17", "event": brickPurchase ... }
{ "date":"2019-10-17", "brickCount": 2 ... }
{ "brickId": 3003, "type": "2x2" ... }
{ "brickId": 3003, "brickCount": 2 ... }
1x4 ▉▉▉▉▉▉▉▉▉▉ 2x2 ▉▉▉▉▉▉ 4x2 ▉▉▉▉ 1x2 ▉▉
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6656747/Copy_of_Blank_Diagram__1_.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
Photo: Jon Kilroy
Ember Has Been There
For Us
Photo: Lego
ember
1.2.0
ember
3.
12.0
2015
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6617857/digits-blank.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6581611/campaign-blank-mock.png)
When You Build
A Lot of Apps
Photo: Tom Nagy
You See The Patterns
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6588075/pasted-from-clipboard.png)
Credit: Lego
Credit: Lego Snazzy
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600420/Screen_Shot_2019-09-30_at_8.26.34_PM.png)
Credit: Richard Süselbeck
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6588146/pasted-from-clipboard.png)
Credit: Sarah von Innerebner
Photo: Lego
Large Patterns
Photo: https://i.ebayimg.com/images/g/~b4AAOSwcBJdJkkp/s-l1600.png
Photo: Lego
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6507522/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466729/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466729/pasted-from-clipboard.png)
!==
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6577571/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466729/pasted-from-clipboard.png)
!==
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6577571/pasted-from-clipboard.png)
-
This Talk Is Not Endorsed By Lego®
-
This Talk Is Not Sponsored By Lego®
-
I Am Not An Employee of Lego®
-
I Have No Connections With Lego®
-
All Lego Images Are Owned By Lego®
-
I Am Not Being Paid In Legos®
Photo: Lego
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466729/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466731/pasted-from-clipboard.png)
Lego === 'play well'
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6493316/pasted-from-clipboard.png)
Is Like Building With Legos
Just Like Real Legos
Ember Addons
Work Well Together
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466729/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466731/pasted-from-clipboard.png)
Legos + Denmark + EmberFest === 'It All Fits Together'
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6667567/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6540505/pasted-from-clipboard.png)
ember-power-select
ember-data
ember-cp-validations
ember-cli-clipboard
ember-ember-modal-dialog
ember-composable-helpers
ember-cli-flash
ember-concurrency
ember-tooltips
ember-c3
ember-font-awesome
ember-animated
ember-tooltips
vertical-collection
ember-gridstack
Photo: CC0 1.0
Using Small Composable Addons To Build Features Provides A Lift
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6533884/pasted-from-clipboard.png)
Development Is Costly
Photo: Lego
-
🧮 UX/Usability
-
🕹 Interactions/Wiring
-
🖼 Styles
-
🧪 Testing
-
♿︎ Accessibility
-
🇩🇰 l10n & i18n
-
🎧 Customer Feedback
Extract Large Patterns Into Larger Pieces
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6466729/pasted-from-clipboard.png)
Duplos®
Photo: Maxx 3001
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6507568/pasted-from-clipboard.png)
With Ember We Can Build
Large Composable Addons
That Encapsulate Large
Amounts Functionality
And Prevent Choking Hazards
models
routes
components
services
controllers
templates
Addon
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6593165/gif__1_.gif)
Encapsulating Large Swaths Of Functionality Can
-
💰 Save On Development Costs
-
👨🏻💻 Collaborative Development
-
🏋️♀️ Reduce Repetitive Work
Is this Ember Engines?
Photo: Jon Kilroy
Photo: Lego
The Short Answer Is
Not Really
Ember App
Photo: Jon Kilroy
Engine A
Engine B
Engine C
Engine D
Team 1
Team 2
Photo: Jon Kilroy
Engine A
Engine B
Engine C
Engine D
Ember App
Photo: Jon Kilroy
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6540664/Screen_Shot_2019-09-14_at_4.30.33_PM.png)
Duplo A
Duplo B
Duplo C
Photo: Jon Kilroy
Duplo A
Duplo B
Duplo C
Duplo A
Duplo B
Duplo D
App One
Experiences
App Two
Experiences
Photo: Jon Kilroy
App One
App Two
Customizations
Duplo A
Duplo B
Duplo C
Duplo A
Duplo B
Duplo D
Photo: Jon Kilroy
Photo: Lego
Each App Is A Snowflake
Each With Its Own
-
📐 Requirements
-
🥩 Stakeholders
-
🍶 Special sauce
What Does This Look Like?
Photo: letsbuilditagain.com
One Pattern We've Observed
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6581602/digits-blank.png)
Photo: Loozrboy
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600531/pasted-from-clipboard.png)
Photo: Lego
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6581602/digits-blank.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600571/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600574/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600616/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600616/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600571/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600574/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6578779/pasted-from-clipboard.png)
Made with: mecabricks.com
Self Serve
Photo: The Brick Zombie
Our Duplos
Core Addons
Reports
Dashboards
Directory
Alerts
Admin
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
navi
Custom Data App
Navi Reports
Navi Dashboards
Navi Core
Custom App Views & Experiences
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
Custom Experience A
Custom Experience B
Custom Data App
Navi Reports
Navi Dashboards
Navi Core
Custom App Views & Experiences
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
Custom Experience A
Custom Experience B
Navi Features
Scheduled Reports
CSV Export
Dashboard Filters
PDF Export
Threshold Alerting
Report Visualization
Save Reports
Ad-hoc Reports
Share URL
API Query URL
Clone Report
Dashboards
Asset Organization
Scheduled Dashboards
Bulk Import
Report Templates
Print Views
Cardinality Aware Lookups
Metric Descriptions
Admin Controls
Report Conversion
-
⏰ < Week To Get To Prod
-
🍶 Focus on Special Sauce
-
👨👧👦 Collaborative Development
-
🧮 Used In 8 Product Areas
Photo: CC0 1.0
How Do We Build Reusable Duplos®?
Photo: Jon Kilroy
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6608434/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6608443/pasted-from-clipboard.png)
Abstraction
Flexibility
size
<NaviReportBuilder
@subComponent1="customSubComponent1"
@subComponent2="customSubComponent2"
@subComponent3="customSubComponent3"
@subComponent4="customSubComponent4"
@subComponent5="customSubComponent5"
@subComponent6="customSubComponent6"
@subComponent7="customSubComponent7"
@subComponent8="customSubComponent8"
@subComponent9="customSubComponent9"
@subComponent10="customSubComponent10"
@subComponent11="customSubComponent11"
@subComponent12="customSubComponent12"
@subComponent13="customSubComponent13"
@subComponent14="customSubComponent14"
@subComponent15="customSubComponent15"
...
onAction1={{action 'action1'}}
onAction2={{action 'action2'}}
onAction3={{action 'action3'}}
onAction4={{action 'action4'}}
onAction5={{action 'action5'}}
onAction6={{action 'action6'}}
onAction7={{action 'action7'}}
onAction8={{action 'action8'}}
onAction9={{action 'action9'}}
onAction10={{action 'action10'}}
...
as | yield1 yield2 yield3 yield4 yield5 yield6 yield7 yield8 |
>
...
</NaviReportBuilder>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6578779/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6613092/pasted-from-clipboard.png)
Photo: Jon Kilroy
Photo: Michael Brennand-Wood
How Ember Wires Your App
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6551871/Screen_Shot_2019-09-17_at_9.38.01_PM.png)
Build
Config
Route
Component
Services
Made with: mecabricks.com
bricks.io/bricks
bricks
route
bricks
controller
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6555575/Screen_Shot_2019-09-18_at_1.12.54_PM.png)
bricks
template
component tree
Made with: mecabricks.com
How Does Ember Do It?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6551896/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6498794/pasted-from-clipboard.png)
Magical
Photo: Lego
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6551871/Screen_Shot_2019-09-17_at_9.38.01_PM.png)
Build
Config
Route
Component
Services
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6551890/Screen_Shot_2019-09-17_at_9.38.10_PM.png)
Dependency Injection
Made with: mecabricks.com
-Ember Guides
Ember applications utilize the dependency injection ("DI") design pattern to declare and instantiate classes of objects and dependencies between them.
Which Supports...
Photo: Big Partnership/PA Wire
Photo: LitFilmFest
Dependency Inversion Principle
Photo: The Lego Movie
Depend On Abstractions
Not Concretions
Made with: mecabricks.com
1x2 Interface
2x2 Interface
Made with: mecabricks.com
Made with: mecabricks.com
Deeper Look
Photo: Lego
Module Lookup
Photo: Lego
//goodbricks/app/routes/bricks.js
import Route from '@ember/routing/route';
export default class Bricks extends Route {
model() {
return [1,2,3];
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6556687/Screen_Shot_2019-09-18_at_9.18.05_PM.png)
ES6 Modules
Asynchronous Module Definitions
goodbricks/
|-- app
|-- routes
| |-- bricks.js
| |-- sets.js
|-- controllers
| |-- bricks.js
| |-- sets.js
--> goodbricks/routes/bricks
--> goodbricks/routes/sets
--> goodbricks/controllers/bricks
--> goodbricks/controllers/sets
https://github.com/amdjs/amdjs-api/wiki/AMD
Module ID
AMD Modules
Container
owner.lookup('route:bricks')
resolve('route:bricks')
hasRegistration('route:bricks')
Registry
Resolver
goodbricks/routes/bricks
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600712/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600712/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6600712/pasted-from-clipboard.png)
Supports Addons
goodbricks
|-- app
|-- components
| |-- brick-card.js
| |-- brick-details.js
|-- helpers
|-- brick-name.js
ember-cli-clipboard
|-- app
|-- components
| |-- copy-button.js
goodbricks
|-- app
|-- components
| |-- brick-card.js --> goodbricks/components/brick-card
| |-- brick-details.js
| |-- copy-button.js
|-- helpers
|-- format-brick-name.js
--> goodbricks/components/brick-card
--> goodbricks/components/brick-details
--> goodbricks/components/copy-button
--> goodbricks/helpers/format-brick-name
APP ADDON
Module ID
MERGED
AMD Modules
Container
owner.lookup('component:copy-button')
Registry
Resolver
goodbricks/components/copy-button
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
Photo: twoclevermoms.com
Single Module Registry
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6540505/pasted-from-clipboard.png)
ember-power-select
ember-data
ember-cp-validations
ember-cli-clipboard
ember-ember-modal-dialog
ember-composable-helpers
ember-cli-flash
ember-concurrency
ember-tooltips
ember-c3
ember-font-awesome
ember-animated
ember-tooltips
vertical-collection
ember-gridstack
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6595298/pasted-from-clipboard.png)
WHAT IF I TOLD YOU
YOU CAN BRING YOUR
APP INTO YOUR ADDON
Photo: Lego
Ember Apps Can Inject Customization Into Addons
Patterns For Customizable Duplos®
🎛 Configuration
🧩 Providers
🔌 Plugins
♟ Extend & Replace
Photo: CC0 1.0
create-data-app
Part Num | Brick Type | Brick Color | Brick Count |
---|---|---|---|
3003 | 2x2 | Red | 13 |
3003 | 2x2 | Blue | 21 |
3001 | 4x2 | Red | 34 |
$ ember new goodbricks
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6607786/Screen_Shot_2019-10-02_at_9.46.59_AM.png)
$ ember install navi-reports
//goodbricks/app/router.js
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
import { reportRoutes } from 'navi-reports/router';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('my-sets');
reportRoutes(this, {/* options */});
});
export default Router;
goodbricks
|-- app
|-- routes
|-- bricks.js
|-- sets.js
navi-reports
|-- app
|-- routes
| |-- navi-reports
|-- new.js
|-- report
|-- clone.js
|-- save-as.js
...
APP ROUTES ADDON ROUTES
goodbricks
|-- app
|-- routes
|-- bricks.js
|-- sets.js
|-- navi-reports
|-- new.js
|-- report
|-- clone.js
|-- save-as.js
...
Configuration
Photo: fllcasts
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6511360/pasted-from-clipboard.png)
App Config
'use strict';
module.exports = function(environment) {
let ENV = {
modulePrefix: 'goodbricks',
environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
};
...
return ENV;
};
//goodbricks/config/environment.js
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6518036/pasted-from-clipboard.png)
Addon Configuration
getOwner(this).resolveRegistration('config:environment');
import config from 'ember-get-config'; //via addon
Using Configuration
import config from 'goodbricks/config/environment';
Within Addon Tree
module.exports = function(/* environment, appConfig */) {
return {
navi: {
dataSources: [/* {
name: 'example',
uri: 'https://data.naviapp.io/v1',
type: 'fili',
timeout: 60000
}*/],
predefinedIntervalRanges: {
day: ['P1D', 'P7D', 'P14D'],
month: ['current/next', 'P1M', 'P3M'],
year: ['current/next', 'P1Y', 'P2Y']
},
FEATURES: {
enableScheduling: false,
enablePDFExport: false,
enabledNotifyIfData: false
}
}
};
};
Core Settings
Refinements
Feature Flags
//navi-reports/config/environment.js
Navi Default Config
module.exports = function(/* environment, appConfig */) {
return {
...
navi: {
dataEpoch: '2019-01-01',
dataSources: [{
name: 'brickData',
uri: 'https://data.goodbricks.io/v1',
type: 'fili'
}],
FEATURES: {
enableScheduleReports: true
...
}
}
};
};
//goodbricks/config/environment.js
Host App Override Config
import config from 'ember-get-config';
const dataSources = config.navi.dataSources;
export default class NaviDataAdapter {
...
_buildURLPath(request, options) {
const { uri } = dataSources.find(d => d.name = options.name);
...
return `${uri}/${namespace}/${table}/${timeGrain}/${dimensions}`;
}
...
}
{{#if (feature-flag "enableScheduleReports")}}
<NaviExportReport>
</NaviIcon name="clock-o"> Schedule
</NaviExportReport>
{{/if}}
Get Data Source URI
Check Feature Flag
Run Time
Photo: Cyril Byrne
Configuration Can Be A Beast
Photo: Lego
📐 1000+ Metrics
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6574263/Screen_Shot_2019-09-23_at_9.00.21_PM.png)
Photo: Rebecca Alvy
Get The API Team To Do It
Navi
API
What tables, metrics, dimensions do you have?
{
tables: [{
name: "bricks",
metrics: [...]
}],
metrics: [... ]
dimension: [...]
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6583683/Screen_Shot_2019-09-25_at_5.19.09_PM.png)
Dimensions
Metrics
Photo: Lego
Providers
Allows An Application To Provide One Of Multiple Implementations
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564132/Screen_Shot_2019-09-20_at_11.14.04_AM.png)
Made with: mecabricks.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564134/Screen_Shot_2019-09-20_at_11.14.32_AM.png)
2x2
Interface
Made with: mecabricks.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564135/Screen_Shot_2019-09-20_at_11.14.42_AM.png)
LEGO 3943v2
Made with: mecabricks.com
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564137/Screen_Shot_2019-09-20_at_11.26.02_AM.png)
LEGO 4591
Made with: mecabricks.com
Services
Photo: La Petite Brique
Notification Service
...
@service naviNofications;
@action
async saveReport(report) {
await report.save();
this.naviNotifications.add({
message: 'Report was successfully saved!',
type: 'success'
});
}
...
goodbricks/services/navi-notifications
Provider Usage
Interface
import Service from '@ember/service';
import { assert } from '@ember/debug';
export default class extends Service {
add(/* options */) {
assert("NaviNotifications must implement `add`");
}
clearMessages() {
assert("NaviNotifications must implement `clear`");
}
}
//navi-core/services/navi-base-notifications.js
Provider Interface
goodbricks
|-- app
|-- services
|-- navi-notifications.js
$ ember g service navi-notifications
goodbricks/services/navi-notifications
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564204/Screen_Shot_2019-09-20_at_12.42.31_PM.png)
ember-cli-flash
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564197/Screen_Shot_2019-09-20_at_12.39.25_PM.png)
ember-cli-notifications
...
import Notification from 'navi-core/services/navi-base-notifications';
export default class FlashMessageService extends Notification {
@service
notificationMessages; //ember-cli-notifications
add(options = {}) {
const clearDuration = TIMEOUTS[options.timeout];
const { message, type } = options;
return this.notificationMessages[type](message, {
autoClear: true,
clearDuration
});
}
clearMessages() {
this.notificationMessages.clearAll();
}
}
//goodbricks/app/services/navi-notifications.js
Concrete Provider
Components
Photo: Lego
<NaviIcon />
<span>
</NaviIcon "download"> Export
</span>
goodbricks/components/navi-icon
Provider Usage
goodbricks
|-- app
|-- components
| |-- navi-icon.js
|-- templates
|-- components
|-- navi-icon.hbs
$ ember g component navi-icon
goodbricks/components/navi-icon
import NaviIcon from 'navi-core/components/navi-icon';
const ICON_MAP = {
download: 'cloud-download'
...
};
export default class extends NaviIcon {
get normalizedName() {
return ICON_MAP[this.name];
}
}
{{!-- goodbricks/app/templates/components/navi-icon.hbs --}}
<i aria-hidden="true" class="d-icons d-{{this.normalizedName}}"></i>
//goodbricks/app/components/navi-icon.js
Concrete Provider
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564535/Screen_Shot_2019-09-20_at_3.02.45_PM.png)
denali icons
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6564541/Screen_Shot_2019-09-20_at_3.04.49_PM.png)
font awesome
Can Be Sourced From
App or Addon
Photo: 1LittleCraftyCorner
Plugins
Plugins Allow You To Add Additional Functionality To Your Application
Visualizations
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6527117/Screen_Shot_2019-09-10_at_7.17.49_PM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6527148/Screen_Shot_2019-09-10_at_7.37.25_PM.png)
display
configure
select
Registration & Discovery
AMD Modules
Container
owner.lookup('route:bricks')
resolve('route:bricks')
hasRegistration('route:bricks')
Registry
Resolver
goodbricks/routes/bricks
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6562026/blueprint.png)
Knows All The Things
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6583737/Screen_Shot_2019-09-25_at_5.41.13_PM.png)
Module Registry
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6617903/Screen_Shot_2019-10-04_at_1.08.15_PM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6605263/Screen_Shot_2019-10-01_at_9.17.47_PM.png)
/* global require */
...
const TYPE = 'navi-visualization-manifest';
const REGEX = new RegExp(`^${modulePrefix}/${TYPE}/([a-z-]*)$`);
export default NaviVisualizationService extends Service {
...
all() {
const modules = Object.keys(require.entries); //all modules
const owner = getOwner(this);
return modules.filter(module => REGEX.test(module))
.map(vis => REGEX.exec(vis)[1])
.map(name => owner.lookup(`${TYPE}:${name}`));
}
});
Custom Visualization
$ember g navi-visualization lego-bar-chart
installing navi-visualization
create app/components/navi-visualizations/lego-bar-chart.js
create app/components/navi-visualization-config/lego-bar-chart.js
create app/navi-visualization-manifests/lego-bar-chart.js
create app/models/lego-bar-chart.js
create app/templates/components/navi-visualizations/lego-bar-chart.hbs
create app/templates/components/navi-visualization-config/lego-bar-chart.hbs
installing navi-visualization-test
create tests/integration/components/navi-visualizations/lego-bar-chart-test.js
create tests/integration/components/navi-visualization-config/lego-bar-chart-test.js
Blueprint
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6614824/Screen_Shot_2019-10-03_at_9.40.19_PM.png)
Extend & Replace
Replace Default Functionality With App Specific Logic
Made with: mecabricks.com
WHO WINS?
myaddon
|-- app
|-- components
|-- brick-card.js
goodbricks
|-- app
|-- components
|-- brick-card.js
ADDON APP
LET THE
WOOKIE HOST APP WIN
Photo: Lego Ideas
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6566440/Screen_Shot_2019-09-21_at_2.41.47_PM.png)
Part Numbers
Brick Counts
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6578766/Screen_Shot_2019-09-24_at_6.15.23_PM.png)
goodbricks/components/navi-cell-renderers/dimension
goodbricks
|-- app
|-- components
|-- navi-cell-renderers
|-- dimension.js
$ ember g component navi-cell-renderers/dimension
goodbricks/components/navi-cell-renderers/dimension
{{#if (eq name "partNumber")}}
<div class="navi-cell-renderer--lego-dim__container">
<div>
<img src="{{legoImgBaseUrl}}/{{value}}.png" alt="brick">
</div>
<span>{{value}}</span>
</div>
{{else}}
<span>{{value}}</span>
{{/if}}
import CellComponent from 'navi-core/components/cell-renderers/dimension';
export default class extends CellComponent {
classNames = ['navi-cell-renderer--lego-dim'];
}
// goodbricks/app/components/navi-cell-renderers/dimension.js
{{!-- goodbricks/app/templates/components/cell-renderers/dimension.hbs --}}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6574487/pasted-from-clipboard.png)
Escape Pod Hatch
Photo: Lego
-
🧪 Allows For Experimentation
-
👷♂️ Abstractions Are Hard
-
⏰ Patterns Take Time
-
🎧 Feedback For Formalization
Photo: dailytelegraph
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6607663/Screen_Shot_2019-10-02_at_9.20.32_AM.png)
Looking Towards The Future
Photo: Lego
Addon Ecosystem Has Been a Big Win
Duplo Ecosystem
Photo: Lego
Photo: brickmania
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6613073/pasted-from-clipboard.png)
Photo: Lego
Photo: Sarah Goldschadt
Photo: Andy Baird
-
Standardized App Structure
-
Canonical Way To Style & Theme
-
Formalized Registration & Injection
-
Common State Management
-
Type Safety
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6481708/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6481710/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6481722/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6498963/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/991169/images/6595525/pasted-from-clipboard.png)
Abstractions
time
Photo: theprisonerandthepenguin.com
Citizen Developer
Photo: Jon Kilroy
Thanks
Photo: Jon Kilroy
Questions
Photo: CC0 1.0
What about testing Duplos?
What about namespacing?
What about Embroider?
Photo: Jon Kilroy
Photo: Lego
-
Acceptance Test Duplo Integrations
-
Integration Test Custom Provider
-
Functional Test API Integrations
navi-${name}.js
<Navi::Component>
Build Time DI
Photo: sugarbeecrafts.com
Building Ember Apps with Duplos®
By jkusa
Building Ember Apps with Duplos®
- 1,656