Suchita Doshi
suchitadoshi
suchitadoshi1987
suchita009
suchita009
Journey Of EmberJS
Ember Octane
Classic vs Octane Epic comparison
Migration tools & References
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
"Data Down, Actions Up" approach
Roadmap for a lot of further improvements:
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}}
Octane
2.x
1.x
3.x
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;
}
}
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
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 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:
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}}
/>
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