Suchita Doshi
A paradigm shift
suchitadoshi
suchitadoshi1987
suchita009
suchita009
When I am away from my computer...
Agenda
Journey Of EmberJS
- Ember 1.x
- Ember 2.x
- Ember 3.x
Ember Octane
- Native Classes
- Glimmer Components
- Templates in Octane
- Tracked Properties
- Modifiers & Decorators
Classic vs Octane Epic comparison
Migration tools & References
Of
The
JOURNEY
Ember 1.x
- Convention over configuration (A new mental model)
- Built-in Routing capability
- Ember-data
- View Driven architecture
- Two way data bindings
- Attribute bindings using {{bind-attr}}
export default Ember.View.extend({
classNameBindings: ['isActive'],
isActive: true,
firstName: 'John',
lastName: 'Doe',
// Computed Property
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName'),
fullNameChanged: function() {
// deal with the change
}.observes('fullName')
});
<div {{bind-attr title=post.popup}}></div>
Route
Model
Controller
+
Templates
Model
View
+
Templates
Ember 2.x
- Component driven
- “Glimmer rendering engine” adoption
- Better binding with properties
{{bind-attr}} - Better template scoping
-
"Data Down, Actions Up" approach
-
Roadmap for a lot of further improvements:
- HTML syntax for component invocation
- Routes to drive Components approach
export default Ember.Component.extend({
classNameBindings: ['isActive'],
isActive: true,
firstName: 'John',
lastName: 'Doe',
// Computed Property
fullName: Ember.computed('firstName', 'lastName', function() {
return this.get('firstName') + ' ' + this.get('lastName');
}),
fullNameChanged: Ember.observer('fullName', function() {
// deal with the change
})
});
Route
Controller
+
Templates
Component
+
Templates
Model
Model
// v2.x
<div title="{{post.popup}}"></div>
<div {{bind-attr title=post.popup}}></div>
// v1.x
// v1.x
{{!-- 1.x version --}}
{{#each post in posts}}
{{!-- `post` references
the current iteration --}}
{{/each}}
{{#each posts}}
{{!-- the outer context
is no longer accessible --}}
{{/each}}
// v2.x
{{!-- 2.x version --}}
{{#each posts as |post|}}
{{!--`post` references
the current iteration --}}
<p>{{post.id}} {{post.title}}</p>
{{/each}}
Ember 3.x (Road to Octane)
- Cleanup Cleanup Cleanup
- Remove support for IE9, IE10 and PhantomJS.
- Remove bower support
- Native Classes
- Glimmer Components
- Angle Brackets Invocation
- @tracked properties
- Modifiers & Decorators
- Lots of documentation
Evolution of EmberJS
Octane
2.x
1.x
3.x
Native Classes
-
ES6 Class syntax
-
Increased performance
-
Smooth learning curve
-
More aligned Javascript community
-
Ability to share code more easily
-
No more `.get`'s
-
Cleaner and easier to read
import { computed } from '@ember/object'
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
console.log(`Created ${this.fullName}...`);
}
@computed('firstName', 'lastName')
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
let phoenix = new Person('Jean', 'Gray');
import EmberObject, { computed } from '@ember/object';
const Person = EmberObject.extend({
init(props) {
this._super(props);
console.log(`Created ${this.get('fullName')}...`);
},
fullName: computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
})
});
let phoenix = Person.create({ firstName: 'Jean', lastName: 'Gray' });
// Classic Ember Object Syntax
// Native Class Syntax
-
Offers simpler, ergonomic, and declarative approach
-
Easier to understand
-
Fewer hooks and properties
-
No more implicit wrappers
-
Namespaced arguments
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
export default class AddRsvp extends Component {
@tracked noOfGuests = 5;
get isMaxGuestExceeded() {
return this.noOfGuests > this.args.maxGuests;
}
}
Glimmer Components
import Component from '@ember/component';
export default Component.extend({
tagName: 'label',
noOfGuests: 5,
isMaxGuestExceeded: computed('noOfGuests') {
return this.noOfGuests > this.maxGuests;
}
});
{{noOfGuests}}
<label> {{this.noOfGuests}} </label>
// Classic Component
// Glimmer Component
Templating in Octane
-
Angle Brackets syntax
-
In line with the HTML standards
-
Capital Case Syntax
-
Easy to distinguish from helpers, properties etc.
-
-
Named arguments
-
Denoted with `@` symbol
-
Easy differentiation from local and external properties
-
-
Required `this`
-
Provides clear point-of-origin
-
Easy to read and understand
-
{{employee-details
name=employeeName
empId=employeeId
addEmployee=(action 'addEmployee')
}}
<EmployeeDetails
@name={{this.employeeName}}
@empId={{@employeeId}}
@addEmployee={{this.addEmployee}}
/>
// Classic templating syntax
// Octane templating syntax
Tracked Properties
-
@tracked syntax
-
Explicit declarations
-
Cleaner code
-
Reduces complexity
-
No More `.set`'s
import EmberObject, { computed } from '@ember/object';
const Person = EmberObject.extend({
firstName: 'Tom',
lastName: 'Dale',
count: 0,
fullName: computed('firstName', 'lastName', function() {
this.set('count', this.get('count') + 1);
return `${this.firstName} ${this.lastName}`;
}),
});
import { tracked } from '@glimmer/tracking';
class Person {
@tracked firstName = 'Tom';
@tracked lastName = 'Dale';
@tracked count = 0;
get fullName() {
this.count = this.count + 1;
return `${this.firstName} ${this.lastName}`;
}
}
// Classic syntax
// @tracked syntax
Modifiers and Decorators
-
Modifiers:
-
Functions or classes used directly in templates
-
Applied directly to elements
-
Allows targeting specific elements more easily
-
Easy to reuse
-
-
Decorators:
-
Abstracts functionality
-
Improved Developer experience
-
<span>{{this.formattedCount}}</span>
<button {{on "click" this.increment}}>Increment</button>
<button {{on "click" this.decrement}}>Decrement</button>
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class Counter extends Component {
@tracked count = 0;
get formattedCount() {
return this.count.toString().padStart(3, '0');
}
@action
increment() {
this.count++
}
@action
decrement() {
this.count--
}
}
import { readOnly } from '@ember/object/computed';
import Component from '@ember/component';
import { computed } from '@ember/object';
import layout from './template';
export default Component.extend({
layout,
tagName: 'label',
attributeBindings: ['for'],
classNames: ['toggle-text', 'toggle-prefix'],
classNameBindings: ['labelType'],
for: readOnly('switchId'),
isVisible: readOnly('show'),
isAwesome: true,
labelValue: computed('isAwesome', function() {
return this.get('isAwesome') ?
'awesome-label' : 'lesser-awesome-label';
}),
type: computed('value', {
get() {
return this.get('value') ? 'on' : 'off';
}
}),
click(e) {
e.stopPropagation();
e.preventDefault();
this.sendToggle(this.get('value'));
}
});
{{#if hasBlock}}
{{yield label}}
{{else}}
{{label}}
{{/if}}
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class ToggleLabel extends Component {
@tracked isAwesome = true;
get type() {
return this.args.value ? 'on' : 'off';
}
get labelValue() {
return this.isAwesome ?
'awesome-label' : 'lesser-awesome-label';
}
@action
handleClick(e) {
e.stopPropagation();
e.preventDefault();
this.args.sendToggle(this.args.value);
}
}
<label
for="{{@switchId}}"
{{on 'click' this.handleClick}}
class="
toggle-text
toggle-prefix
{{this.type}}-label
{{if @show 'is-visible' 'is-hidden'}}
"
>
{{#if @hasBlock}}
{{yield @label}}
{{else}}
{{@label}}
{{/if}}
</label>
// Classic Component syntax
// Octane Component syntax
{{x-toggle-label
label=labelVal
value=false
sendToggle=(action 'sendToggle')
}}
<ToggleLabel
@label={{this.labelVal}}
@value=false
sendToggle={{this.sendToggle}}
/>
Tools & Goodness
-
Ember Codemods: https://github.com/ember-codemods
-
Ember Atlas: https://www.notion.so/The-Ember-Atlas-4094f81c86c34badb4a562ed29414ae1
-
The Octane Edition of Ember: https://emberjs.com/editions/octane/
-
Ember Blog - Octane is Here: https://blog.emberjs.com/2019/12/20/octane-is-here.html
Thank you!
Ember Octane - A Paradigm Shift
By Suchita Doshi
Ember Octane - A Paradigm Shift
Ember JS is a great choice when it comes to building web apps of any size. However, it does have a reputation for having a steep learning curve along with other issues like the size of the framework, not being productive out of the box, etc. Ember JS's newest edition Ember Octane addresses most of such concerns & rescues the troubled developers. Curious about what features Ember Octane offers? and why is it so different from the previous versions of Ember? This session would cover some important aspects of Ember Octane & an overview of Ember's transformation from its previous versions to Octane.
- 1,972