Trying to build an off-canvas panel using Ember.JS components was proving difficult. It did not help that most the results still referenced the now deprecated Ember.View
.
Background
I had built the off-canvas panel to animate using CSS3 transitions and animations instead of a more traditional JavaScript approach. The implementation required jQuery to add the .is-visible
class to a <div class="cd-panel">...</div>
.
Final Answer
The answer I found was to use RSVP.js
implementation of Promises
that comes as part of Ember.JS.
// app/components/story-panel/component.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
close: function() {
var promise = new Promise(function(resolve, reject) {
this.$('.cd-panel').removeClass('is-visible');
setTimeout(function() {
resolve(true);
}, 600);
}.bind(this));
promise.then(function() {
return this.sendAction('close');
}.bind(this));
}
},
becomeVisible: function() {
setTimeout(function() {
this.$('.cd-panel').addClass('is-visible');
}.bind(this), 100);
}.on('didInsertElement')
});
Other considered options
In my hours long search for an answer to this problem, I discovered another approach that seemed to work (though for unknown reasons). The solution that relied upon the usage of Ember.run.next
.
This did seem to work for the becomeVisible
method, but I could not translate that into a working close
action.
As far as to why Ember.run.next
worked, I am still unsure. Looking at the Ember documentation for this method it calls out that there are normally better ways to resolve issues requiring the #next
call.
Code sample
<div class="cd-panel from-right">
<header class="cd-panel-header">
<h4>View Story Details</h4>
<a {{action 'close'}} class="cd-panel-close">Close</a>
</header>
<div class="cd-panel-container">
<div class="cd-panel-content">
{{model.title}}
{{model.description}}
</div>
</div>
</div>
// app/iterations/route.js
import Ember from 'ember';
export default Ember.Route.extend({
displayStory: false,
model: function(params) {
return this.store.query('iteration', params);
},
actions: {
openStoryPanel: function(story) {
return this.render('story-panel', {
outlet: 'story-panel',
into: 'application',
model: story,
controller: 'application'
});
},
closeStoryPanel: function() {
return this.disconnectOutlet({
outlet: 'story-panel',
parentView: 'application'
});
}
}
});
<!-- /app/templates/application.hbs -->
{{outlet "story-panel"}}
<!-- /app/templates/story-panel.hbs -->
{{story-panel model=model close='closeStoryPanel'}}