Emberjs: How to refresh parent template after model create - ember.js

I'm building an app that has the following code:
Routes:
App.Router.map(function() {
this.resource('gradebooks', function() {
this.resource('gradebook', { path: ':gradebook_id' });
});
});
App.IndexRoute = Em.Route.extend({
redirect: function() {
this.transitionTo('gradebooks');
}
})
App.GradebooksRoute = Em.Route.extend({
setupController: function(controller, model) {
Em.$.getJSON('/data/gradebooks/get', function(data) {
controller.set('model', data);
});
}
});
App.GradebookRoute = Em.Route.extend({
model: function(params) {
var id = params.gradebook_id;
return Em.$.getJSON('/data/gradebooks/get/' + id);
}
})
Controllers:
App.GradebooksController = Em.ArrayController.extend({
isActive: false,
actions: {
createGradebook: function(newTitle) {
var self = this;
if(newTitle.trim()) {
Em.$.get('/data/gradebooks/add/' + newTitle, {}, function(json) {
Em.$('#create-gradebook-modal').modal('hide').find('input').val('');
self.transitionToRoute('gradebook', json.id);
}, 'json');
}
}
}
});
Templates (gradebooks.hbs):
<div class="sidebar">
<div class="btn-container">
<button class="btn btn-block btn-info" data-toggle="modal" data-target="#create-gradebook-modal">
<i class="fa fa-book"></i>
Create Gradebook
</button>
</div>
<ul>
{{#each}}
<li {{bind-attr class="isActive:active"}}>
{{#link-to "gradebook" _id}}
{{title}}
{{/link-to}}
</li>
{{/each}}
</ul>
</div>
<div class="content">
<div class="container-fluid">
{{outlet}}
</div>
</div>
Templates (gradebook.hbs):
<h1>{{title}} <small>{{_id}}</small></h1>
<div class="btn-container">
<button class="btn btn-default"><i class="fa fa-pencil"></i> Rename</button>
<button class="btn btn-danger"><i class="fa fa-trash-o"></i> Trash</button>
</div>
What I'm having trouble with is under the GradebooksController.actions.createGradebook, I send a request to create the model and then I transition to the gradebook I just created. The creation and transition works just fine, but I want the sidebar (gradebooks.hbs) to show the newly created gradebook.
I think it has something to do with updating the model in the GradebooksRoute. How can I make it fetch for the models from the server again after creating a gradebook.

To elaborate on #fanta's comment, you'll want to do something like this:
App.GradebooksController = Em.ArrayController.extend({
isActive: false,
actions: {
createGradebook: function(newTitle) {
var self = this;
if(newTitle.trim()) {
var gradebook = Em.$.getJSON('/data/gradebooks/add/' + newTitle, {}, function(json) {
Em.$('#create-gradebook-modal').modal('hide').find('input').val('');
self.transitionToRoute('gradebook', json.id);
}, 'json');
self.get('content').pushObject(gradebook);
}
}
}
});

Related

key events inside a specific div in ember.js

I created a auto complete search bar in ember.js. The problem that i am having is be able to to press the down key and and go ever the listing that the autocomplete div is giving me. Thank you in advance...
<li {{bind-attr class="opacitySet:border:field"}}>
<i class="icon-search"></i>
{{input type="search" placeholder="Search Keyword" focus-out="focusOut" focus-in="focusIn" value=searchText}}
<div {{bind-attr class=":ee opacitySet::hide"}}>
{{#each searchResults}}
<h4><i class="icon-doc-text" style="padding:0;"></i>{{this}}<img src="assets/images/Arrow.png" class="pull_right"></h4>
{{/each}}
</div>
</li>
===========controller=============
App.FaqController = Ember.Controller.extend({
showAllAnswers: false,
opacitySet: false,
searchText: '',
searchResults: function(){
var searchText = this.get('searchText');
if(!searchText){ return;}
var regex = new RegExp(searchText, 'i');
return question.filter(function(name) {
return name.trim();
return name.match(regex);
});
}.property('searchText'),
actions:{
toggleBox: function(){
this.toggleProperty('showMessageBox');
},
hideBox: function(){
this.set('showMessageBox', false);
},
toggleAllAnswers: function(){
this.toggleProperty('showAllAnswers');
},
focusOut: function(){
this.set('opacitySet', false);
},
focusIn: function(){
this.set('opacitySet', true);
},
}
});

Form submission through REST api

Im new to Ember and I am trying to create a record by submitting a form. This is the code I've written so far:
App.CharactersNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('character', {name: '', race: ''});
}
});
<form {{action "createCharacter" on="submit"}}>
<div class="form-group">
<label>Name</label>
{{input value=characterName class="form-control"}}
</div>
<div class="form-group">
<label>Race</label>
{{input id=characterRace class="form-control"}}
</div>
{{#link-to 'characters'}}<button class="btn btn-default">Back</button>{{/link-to}}
<button class="btn btn-default" type="submit">Create</button>
</form>
App.CharactersNewController = Ember.ObjectController.extend({
actions: {
createCharacter: function() {
var name = this.get('characterName'),
race = this.get('characterRace');
if (!name || !race) { return false }
// create new character
var character = this.store.createRecord('character', {
name: name,
race: race
});
this.set('characterName', '');
this.set('characterRace', '');
character.save();
}
}
})
Right now, the code stops at the var character = this.store.createRecord line in the controller, but no errors are raised in the console
Thanks

Why is the URL not updated when I click the link for that route

I am using EmberJs version 1.4.
When I click on one of the links I would expect the URL to include the id of the selected widget but nothing appears and when I look at the params parameter in the route model hook it has no properties and I would expect the id to be one of its properties so could someone help me to understand what am I missing?
In other words I would expect the URL to become awesome.html#/widgets/5 but it always is awesome.html#/widgets
Thank you!
This is my ember code:
window.Awesome = Ember.Application.create();
Awesome.Router.map(function() {
this.resource("awesome", {path: "/"}, function(){
this.route('login');
});
this.resource("widgets", function () {
this.resource('widget', { path: '/:widgetId' }, function () {
this.route('general', { path: 'info' });
this.route('configuration');
this.route('operations');
})
});
});
Awesome.WidgetsRoute = Awesome.AuthenticationRoute.extend({
model: function(){
//TODO: Call a service to get the model.
return { widgets: [{ widgetId: 1, widgetName: "Great Widget" }, { widgetId: 2, widgetName: "Fantastic Widget" }, { widgetId: 3, widgetName: "Brutal Widget" }] };
}
});
Awesome.WidgetIndexRoute = Awesome.AuthenticationRoute.extend({
model: function (params) {
var receivedWidgetId = params.widgetId;
return { widgetName: "Hardcoded Widget", widgetId: receivedWidgetId };
}
});
These are the templates:
<script type="text/x-handlebars" data-template-name="widgets">
<section class="span3 left-section">
<div class="btn-group-vertical btn-group-justified registration-actions-menu">
<button id="createNewWidget" class="btn btn-link">Create New Widget</button>
<button id="joinWidgetTeam" class="btn btn-link">Join Widget Team</button>
</div>
<div class="registered-widgets-menu">
<div class="btn-group-vertical">
{{#each widget in widgets}}
{{#link-to 'widget' widget class="btn btn-link"}}{{widget.widgetName}}{{/link-to}}
{{/each}}
</div>
</div>
</section>
<section class="span8">
{{outlet}}
</section>
</script>
<script type="text/x-handlebars" data-template-name="widget">
<div id="widgetOptions">
<!-- TODO: Change the anchors for handlebars link-to helpers. -->
<h1>{{widgetName}}</h1> <h5>{{widgetId}}</h5>
<ul id="widgetNavigation">
<li>Widget Info</li>
<li>Widget Configuration</li>
<li>Widget Operations</li>
</ul>
</div>
<div id="widgetContent">
<!-- TODO: Design some awesome widget content. -->
Some awesome widget content
</div>
</script>
I also have an authentication route from which the other routes inherit. Even though I don't believe it has something to do with the issue I'll include just in case I am wrong.
Awesome.AuthenticationRoute = Ember.Route.extend({
beforeModel: function(transition){
if(!Awesome.get('loggedUser')){
this.redirectToLogin(transition);
}
},
redirectToLogin: function(transition) {
var loginController = this.controllerFor('awesome.login');
loginController.set('attemptedTransition', transition);
this.transitionTo('awesome.login');
}
});
It looks like it's totally working to me, when you click on one of the widgets
http://emberjs.jsbin.com/mohex/1
Additionally it looks like you're mixing up the WidgetIndexRoute and WidgetRoute. The widget resource should be displayed like this (though this is unrelated to the issue you're describing)
Awesome.WidgetRoute = Awesome.AuthenticationRoute.extend({
model: function (params) {
var receivedWidgetId = params.widgetId;
return { widgetName: "Hardcoded Widget", widgetId: receivedWidgetId };
}
});

Ember.js load page by URL

Copied example from emberjs official site called Mailbox (under ROUTING).
App.Mailbox = Em.Object.extend();
App.Mailbox.reopenClass({
find: function(id) {
if (id) {
return App.FIXTURES.findBy('id', id);
} else {
return App.FIXTURES;
}
}
});
App.Router.map(function() {
this.resource('mailbox', { path: '/:mailbox_id' }, function() {
this.resource('mail', { path: '/:message_id' });
});
});
App.ApplicationRoute = Em.Route.extend({
model: function() {
return App.Mailbox.find();
}
});
App.MailRoute = Em.Route.extend({
model: function(params) {
return this.modelFor('mailbox').messages.findBy('id', params.message_id);
}
});
fixtures:
App.FIXTURES = [
{
name: "Inbox",
id: "inbox",
messages: [
{
id: 1,
subject: "Welcome to Ember",
from: "tomster#emberjs.com",
to: "user#example.com",
date: new Date(),
body: "Welcome to Ember. We hope you enjoy your stay"
}, {
id: 2,
subject: "Great Ember Resources",
from: "tomster#emberjs.com",
to: "user#example.com",
date: new Date(),
body: "Have you seen embercasts.com? How about emberaddons.com?"
}]}, {
name: "Spam",
id: "spam",
messages: [
{
id: 3,
subject: "You have one the Nigerian lottery!!!111ONEONE",
from: "419#thereallotteryhonest.ng",
to: "user#example.com",
date: new Date(),
body: "You have ONE the lottery! You only have to send us a small amount of monies to claim your prize"
}]}, {
name: "Sent Mail",
id: "sent-mail",
messages: [
{
id: 4,
subject: "Should I use Ember",
from: "user#example.com",
to: "tomster#emberjs.com",
date: new Date(),
body: "Ember looks pretty good, should I use it?"
}]}];
and html:
<script type="text/x-handlebars">
<div class="url">URL: {{target.url}}</div>
<aside>
<ul>
<li><h2>Mailboxes</h2></li>
{{#each}}
<li>
{{#link-to "mailbox" this currentWhen="mailbox"}}
<span class="count">
{{messages.length}}
</span>
{{name}}
{{/link-to}}
</li>
{{/each}}
</ul>
</aside>
<section class="main">
{{outlet}}
</section>
</script>
<script type="text/x-handlebars" id="index">
<div class="index">
<h1>TomsterMail</h1>
<span class="tomster"></span>
</div>
</script>
<script type="text/x-handlebars" id="index">
<div class="mail">
<dl>
<dt>From</dt>
<dd>{{from}}</dd>
<dt>To</dt>
<dd>{{to}}</dd>
<dt>Date</dt>
<dd>{{date date}}</dd>
</dl>
<h4>{{subject}}</h4>
<p>{{body}}</p>
</div>
</script>
<script type="text/x-handlebars" id="mailbox">
<table>
<tr>
<th>Date</th>
<th>Subject</th>
<th>From</th>
<th>To</th>
</tr>
{{#each messages}}
{{#link-to "mail" this tagName=tr}}
<td>{{date date}}</td>
<td>{{view.isActive}}{{subject}}</td>
<td>{{from}}</td>
<td>{{to}}</td>
{{/link-to}}
{{/each}}
</table>
{{outlet}}
</script>
<script type="text/x-handlebars" id="mailbox/index">
<div class="mailbox-index">
Select an email
</div>
</script>
When I go to url /index.html#/inbox/1 following exception thrown:
Assertion failed: Cannot call get with 'id' on an undefined object.
I have web application which needs same functionality, but with server requests (not from FIXTURES)
Try changing your code for retrieving the model:
return this.modelFor('mailbox').get('messages').findBy('id', params.message_id);
In Ember, you need to use get() to retrieve properties and associations
\
If i replace this.modelFor('mailbox').get('messages').findBy('id', params.message_id); with ajax call then everything works as expected
App.MailRoute = Em.Route.extend({
model: function(params) {
var ref = this;
return $.get("/inbox/"+params.message_id).then(function(data){
return data.findBy('id', params.message_id);
});
}
});
});

Router understanding issue with Ember 1.0 RC6

The following code worked fine with RC4 but it doesn't work with RC6. index.html#/users/1/edit doesn't fill the form to edit the user entry. Can anybody tell me what I have to change to get this working with the new router?
app.js
App = Ember.Application.create();
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' }, function() {
this.route('edit');
});
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UserController = Ember.ObjectController.extend();
App.UserEditRoute = Ember.Route.extend({
model: function() {
return this.modelFor("user")
},
renderTemplate: function() {
this.render({ into: 'users' });
},
setupController: function(controller, model) {
if (model.get('isNew') == false) {
var map = this.map || Ember.Map.create();
this.map = map;
var transaction = this.get('store').transaction();
if (map.get(model)) {
transaction = map.get(model);
} else {
map.set(model, transaction);
transaction.add(model);
}
}
},
events: {
commitThis: function(model) {
var map = this.map;
var transaction = map.get(model);
transaction.commit();
map.remove(model);
App.Router.router.transitionTo('users.index')
},
rollbackThis: function(model) {
var map = this.map;
var transaction = map.get(model);
transaction.rollback();
transaction.add(model);
App.Router.router.transitionTo('users.index')
}
}
});
App.UserEditController = Ember.ObjectController.extend({
save: function(model) {
this.send('commitThis', model)
},
undo: function(model) {
this.send('rollbackThis', model)
}
});
App.Store = DS.Store.extend({
adapter: 'DS.FixtureAdapter'
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
App.User.FIXTURES = [{
id: 1,
firstName: "Yehuda",
lastName: "Katz"
}, {
id: 2,
firstName: "Tom",
lastName: "Dale"
}]
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Userlist Demo</title>
<link href="css/bootstrap.css" rel="stylesheet">
<style>
body {
padding-top: 60px;
}
</style>
<link href="css/bootstrap-responsive.css" rel="stylesheet">
</head>
<body>
<script type="text/x-handlebars">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="#">Demo</a>
<div class="nav">
<ul class="nav">
<li>{{#linkTo 'users'}}Users{{/linkTo}}</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Demo Ember.js Application</h2>
<p>
A list of all users can be found {{#linkTo 'users'}}here{{/linkTo}}.
</p>
</script>
<script type="text/x-handlebars" data-template-name="users">
<div class='row'>
<div class='span7'>
<table class='table table-striped'>
<thead>
<tr>
<th>First name</th>
<th>Last name <i class="icon-arrow-down"></i></th>
<th></th>
</tr>
</thead>
<tbody>
{{#each this itemController="user"}}
<tr {{bindAttr class="isDirty:warning"}}>
<td>{{firstName}}</td>
<td>{{lastName}}</td>
<td>
{{#unless isNew}}
{{#linkTo 'user.edit' this activeClass="disabled" classNames="btn btn-small"}}<i class="icon-edit"></i> Edit{{/linkTo}}
{{/unless}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<div class='span5'>
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="user/edit">
<h2>Edit</h2>
<p><strong>First name</strong><br>{{input value=firstName type=text tabindex=1}}</p>
<p><strong>Last name</strong><br>{{input value=lastName type=text tabindex=2}}</p>
<p>
<button {{action 'save' this}} {{bindAttr class=":btn :btn-small :btn-primary content.isDirty:enabled:disabled"}} tabindex=4>Save changes</button>
<button {{action 'undo' this}} {{bindAttr class=":btn :btn-small content.isDirty:enabled:disabled"}} tabindex=5>Undo changes</button>
</p>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars.js"></script>
<script src="js/libs/ember.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/libs/md5.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Pretty sure this is because App.UserEditRoute.setupController is not calling _super. Strange cause I thought that breaking change was back in RC4. Anyway, try this:
App.UserEditRoute = Ember.Route.extend({
// ...
setupController: function(controller, model) {
this._super(controller, model);
// ...
}
}
Possibly related: Seems like setupController is saving state (this.map) on the route object. Surprised it works at all, for sure has potential to cause problems. Instead setupController should set properties on the local controller or model arguments, or use this.controllerFor() to access another controller.
In this case seems like a lot of code is not necessary, could just save/rollback on the model itself. So to simplify:
App = Ember.Application.create();
App.Router.map(function() {
this.resource('users', function() {
this.resource('user', { path: ':user_id' }, function() {
this.route('edit');
});
})
});
App.UsersRoute = Ember.Route.extend({
model: function() {
return App.User.find();
}
});
App.UserController = Ember.ObjectController.extend();
App.UserEditRoute = Ember.Route.extend({
model: function() {
return this.modelFor("user")
},
renderTemplate: function() {
this.render({ into: 'users' });
},
events: {
save: function(model) {
model.save().then( function() {
App.Router.router.transitionTo('users.index')
}, function() {
alert('save failed!');
});
},
undo: function(model) {
model.rollback();
App.Router.router.transitionTo('users.index')
}
}
});
App.Store = DS.Store.extend({
adapter: 'DS.FixtureAdapter'
});
App.User = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string')
});
App.User.FIXTURES = [{
id: 1,
firstName: "Yehuda",
lastName: "Katz"
}, {
id: 2,
firstName: "Tom",
lastName: "Dale"
}]
See working example here: http://jsbin.com/ixucos/2/edit