Skip to content

Instantly share code, notes, and snippets.

@JamesChevalier
Last active November 25, 2015 16:39
Show Gist options
  • Save JamesChevalier/3628ca3ff4a12f1b2625 to your computer and use it in GitHub Desktop.
Save JamesChevalier/3628ca3ff4a12f1b2625 to your computer and use it in GitHub Desktop.
An Ember Route/Template/Component setup that displays a list of Posts.
  • There's a page that displays a list of Posts.
  • Each Post in the list links out to its individual Post page.
  • Both pages contain a form to add a new Post - this is the Component.
  • Additionally, the form can be shown/hidden.

The problem is that the actions need to exist in all possible Routes.

The new-post component bubbles up to whichever Route it's being called in. I'm calling it from the Posts Route, so I need to handle the savePost there... I'm also calling it from the Post Route, so I need to handle the savePost there, as well.

Similar logic applies to the togglePostForm action, as well.

<!-- Template for New Post Component, hidden by default -->
<form {{action 'save' on='submit'}} class="ui transition hidden new-body-form">
<div class="ui input">
{{input type="text" value=postTitle placeholder="Title"}}
</div>
<div class="ui input">
{{input type="text" value=postBody placeholder="Body"}}
</div>
<input class="ui button" type="submit" value="Add">
<button class="ui button" {{action 'cancel'}}>Cancel</button>
</form>
// New Post Component
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
save: function() {
// This bubbles up to whichever Route is currently in use...
// If the New Post Component is displayed in the Posts Route and the Post Route...
// Then the savePost action must exist in both Route files
this.sendAction('save', this);
},
cancel: function() {
this.set('postTitle', '');
this.set('postBody', '');
$('.new-post-button').toggleClass('active');
$('.new-post-form').transition('fade');
}
}
});
<!-- Template for Post (standard Route/Template setup) -->
<div class="ui three item menu">
<a class="item new-post-button" {{action 'togglePostForm'}}>New Post</a> <!-- toggle display of form -->
<a class="item">Second Link</a>
<a class="item">Third Link</a>
</div>
{{new-post save="savePost"}}
<h2>{{model.title}}</h2>
<p>{{model.body}}</p>
// Route for Post (standard Route/Template setup)
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.store.find('post', params.post_id);
},
actions: {
// This is the action that the sendAction bubbled up to
savePost: function(post_component) {
var post = this.store.createRecord('post', {
title: post_component.get('postTitle'),
body: post_component.get('postBody')
});
post.save();
post_component.set('postTitle', '');
post_component.set('postBody', '');
post_component.set('postLink', '');
},
// Similarly, the togglePostForm action call exists in both routes
togglePostForm: function() {
$('.new-post-button').toggleClass('active');
$('.new-post-form').transition('fade');
}
}
});
<!-- Template for Posts (standard Route/Template setup) -->
<div class="ui three item menu">
<a class="item new-post-button" {{action 'togglePostForm'}}>New Post</a> <!-- toggle display of form -->
<a class="item">Second Link</a>
<a class="item">Third Link</a>
</div>
<h2>Posts</h2>
{{new-post save="savePost"}}
<div class="ui middle aligned divided list">
{{#each model as |post|}}
<div class="item">
<div class="right floated content">
<div class="ui red button" {{action 'deletePost' post}}>Delete</div>
</div>
<img class="ui avatar image" src="/icon-post.png">
<div class="content">
{{#link-to "post" post}}{{post.title}}{{/link-to}}
</div>
</div>
{{/each}}
</div>
// Route for Posts (standard Route/Template setup)
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('post');
},
actions: {
// This is the action that the sendAction bubbled up to
savePost: function(post_component) {
var post = this.store.createRecord('post', {
title: post_component.get('postTitle'),
body: post_component.get('postBody')
});
post.save();
post_component.set('postTitle', '');
post_component.set('postBody', '');
post_component.set('postLink', '');
},
// Similarly, the togglePostForm action call exists in both routes
togglePostForm: function() {
$('.new-post-button').toggleClass('active');
$('.new-post-form').transition('fade');
}
}
});
// Routes available to the Ember app
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType
});
Router.map(function() {
this.route('posts');
this.route('post', { path: '/posts/:post_id' });
});
export default Router;
@barelyknown
Copy link

You have two primary options if you want to DRY the action.

  1. Put the action in routes/applications.js (which is a shared ancestor).
  2. Nest the post route under the posts route and then posts would be the shared ancestor. Here's how to do that:
this.route('posts', function() {
  // you should reset the namespace if you want to refer to the routes the same
  // way as you currently are
  this.route('post', { path: ':post_id', resetNamespace: true });
}); 

Depending on the situation other solutions could work, but this is a good start if you just want to have a shared action.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment