Redirect if Invalid id is given - ember.js

I have the following Routes
App.Router.map(function() {
this.resource('gradebooks', function() {
this.resource('gradebook', { path: ':gradebook_id' });
});
});
App.GradebooksRoute = Em.Route.extend({
model: function() {
return this.store.find('gradebook');
}
});
App.GradebookRoute = Em.Route.extend({
model: function(params) {
var id = params.gradebook_id;
var gradebook = this.store.find('gradebook', id);
var self = this;
gradebook.then(null, function(reason) {
self.transitionTo('gradebooks');
});
return gradebook;
}
})
Templates:
<ul>
{{#each}}
<li {{bind-attr class="isActive:active"}}>
{{#link-to "gradebook" this}}
{{title}}
{{/link-to}}
</li>
{{/each}}
</ul>
{{outlet}}
It's works fine and dandy except when an invalid id is given. (ex. #/gradebooks/invalid_id).
Currently, the redirection works great (I do get the error Error while loading route: TypeError: Cannot read property 'id' of undefined, but it still redirects. However, the resulting list of gradebooks has the invalid gradebook in the list.
If I manually navigate to #/gradebooks/invalid_id_1, #/gradebooks/invalid_id_2, #/gradebooks/invalid_id_3, etc., the invalid gradebook gets added to the list of gradebooks every time.
Any ideas why? Or is there a better solution?

instead of manually handling the find model promise its better to let ember do it, it will trigger an error event if the find
promise is rejected
App.GradebookRoute = Em.Route.extend({
model: function(params) {
var id = params.gradebook_id;
return this.store.find('gradebook', id);
},
actions: {
error: function(reason) {
alert(reason); // "FAIL"
// Can transition to another route here, e.g.
// transitionTo('gradebooks');
// Uncomment the line below to bubble this error event:
// return true;
}
})
more info http://emberjs.com/guides/routing/asynchronous-routing/

Related

Remove a sticky parameter

I have an application which displays flight information. All list of all flights is available via http://localhost:4200/flights and a list of all flights from Frankfurt via http://localhost:4200/flights?from=FRA
Below the displayed list are two links for the user to toggle between the views. Unfortunately once a user clicked on the http://localhost:4200/flights?from=FRA link he/she has no way of going back to http://localhost:4200/flights via clicking on the corresponding link. The from=FRA parameter is sticky.
How can I link to the http://localhost:4200/flights page and get rid of the from=FRA parameter?
The code I use:
app/templates/flights.hbs
<ul>
{{#each flight in filteredFlights}}
<li>{{flight.code}} {{flight.from}} - {{flight.to}}</li>
{{/each}}
</ul>
<ul>
<li>{{#link-to 'flights'}}All flights{{/link-to}}</li>
<li>{{#link-to 'flights' (query-params from="FRA")}}Flights from Frankfurt{{/link-to}}</li>
</ul>
app/controllers/flights.js
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['from','to'],
from: null,
to: null,
filteredFromFlights: function() {
var from = this.get('from');
var flights = this.get('model');
if (from) {
return flights.filterBy('from', from);
} else {
return flights;
}
}.property('from', 'model'),
filteredFlights: function() {
var to = this.get('to');
var flights = this.get('filteredFromFlights');
if (to) {
return flights.filterBy('to', to);
} else {
return flights;
}
}.property('from', 'to', 'model')
});
app/routes/flights.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('flight');
}
});
Just pass 'null':
{{#link-to 'flights' (query-params from='null')}}All flights{{/link-to}}

Emberjs route model with view

My app has a page where I'm using the view to display the data from other template with my view like this :
<script type="text/x-handlebars" data-template-name="enquiry">
[...] // some other information display before
{{view App.EnquirySelectedVehicleView}}
</script>
<script type="text/x-handlebars" data-template-name="selectedVehicle">
// Here is my content
</script>
My map looks like this :
this.resource('enquiry', { path: '/enquiry/:enquiry_id'}, function() {
this.route('selectedVehicle');
});
After reading the doc I just did this in my view :
App.EnquirySelectedVehicleView = Ember.View.extend({
templateName: 'selectedVehicle'
});
So far so good, its showing the text from my template. But I need to return data from an ajax call in this template (selectedVehicle) automatically, like its fetching the data when you are on /enquiry/1/.
I've done this in my router :
App.EnquirySelectedVehicle = Ember.Object.extend({});
App.EnquirySelectedVehicleRoute = Ember.Route.extend({
model: function() {
console.log('DEBUG: SelectedVehicle Model');
App.SelectedVehicle.vehicleStock(this)
}
});
App.EnquirySelectedVehicle.reopenClass({
vehicleStock: function(that) {
console.log('DEBUG: Fetch vehicle stock');
// Here come the ajax call
}
});
But my issue is that route is never call.. How can I return some value from my selectedVehicleRoute when I'm on the /enquiry/1 page in a view template ? (not sure if I ask it correctly)
Thanks for the help !
[edit]
#Fanta : I think I begin to understand how I can do that :
App.EnquiryRoute = Ember.Route.extend({
beforeModel: function(transition) {
this.controllerFor('login').send('isSession', transition);
},
model: function(param) {
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
var modelData = {enquiry: {}, vehicleStock: {}};
Ember.$
.get(host + '/enquiry/' + param['enquiry_id'], function(data) {
console.log('DEBUG: Enquriry GET OK id = ' + param['enquiry_id']);
modelData.enquiry = data.enquiry;
Ember.$.get(host + '/vehiclestock/' + data.enquiry.VehicleStockId, function(data) {
console.log('DEBUG: VehicleStock GET OK id = ' + data.enquiry.VehicleStockId)
console.log(data);
modelData.vehicleStock = data.vehicleStock;
resolve(modelData);
});
});
});
return promise;
}
});
It seems to work, now I have to figure it out how to display my Object :) but thank you for your help, that actually make me resolve it by a different way !
For future reference, just go to https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md and you'll see two links, one to JSFiddle and one to a JSBin with the basic setup.
Are you sure the route is not being called ? I created a Fiddle, http://jsfiddle.net/NQKvy/817/ if you check the JS console, you'll see in the log:
DEBUG: SelectedVehicle Model
DEBUG: Fetch vehicle stock

emberjs: transitionToRoute Error no method 'addArrayObserver

From the 'job' route I am trying to transition to 'careers' route using following code.
<script type="text/x-handlebars" data-template-name="job">
<button {{action 'backToCareers' this}}>Back</button>
</script>
The controller with following gives 'Uncaught TypeError: Object # has no method 'addArrayObserver' ' error.
CareerApp.JobController = Ember.ObjectController.extend({
backToCareers: function(){
this.transitionToRoute('careers');
}
});
If I change the code(see below) to provide model object the error changes to 'Uncaught More context objects were passed than there are dynamic segments for the route: careers '
CareerApp.JobController = Ember.ObjectController.extend({
backToCareers: function(){
var jobs = CareerApp.Job.findAll();
this.transitionToRoute('careers', jobs);
}
});
Following is the code of my Model and the router
CareerApp.Job = Ember.Model.extend({
refNo: '',
title: ''
});
CareerApp.Job.reopenClass({
findAll: function(){
return $.getJSON("http://site/jobs").then(
function(response){
var jobs = Ember.A();
response.forEach(function(child){
jobs.pushObject(CareerApp.Job.create(child));
});
return jobs;
}
);
}
});
Router code
CareerApp.Router.map(function(){
this.resource('careers', {path: '/'});
this.resource('job', {path: '/jobs/:job_id'});
});
CareerApp.CareersRoute = Ember.Route.extend({
model:function(){
return CareerApp.Job.findAll();
}
});
CareerApp.CareersController = Ember.ArrayController.extend({
gradJobCount: function () {
return this.filterProperty('isExp', false).get('length');
}.property('#each.isExp')
});
The model hook is expected to return an array but you return a jQuery promise object. findAll should return an empty array which is filled when the callback is executed.
findAll: function() {
var jobs = [];
$.getJSON("http://site/jobs").then(function(response){
response.forEach(function(child){
jobs.pushObject(CareerApp.Job.create(child));
});
});
return jobs;
}
As you pass jobs to CarreersController, this one needs to be an ArrayController, maybe you have to define it manually

Emberjs serialize doesn't call model on {{linkTo}}

I have a route with the following format #/books/book/:bookId
When i am on the #/books and i click into the {{linkTo}}, then my model is not being
executed and the following error is being returned.
Error while loading route: TypeError {}
Uncaught TypeError: Object 4 has no method 'addArrayObserver'
I know that a model is only being executed when you refresh the page with your browser,
so i used the serialize, which according to the docs it should allow my model to be executed
when i click into the {{linkTo}, but it doesn't. It returns the above error.
utils.get is an Ajax call which returns a json back...
Could you tell me what i am doing wrong?
Thank you.
App.Router.map(function() {
this.resource("books", function() {
this.resource("book", {path: "/book/:bookId"});
});
});
var BookModel = Ember.Object.extend({});
BookModel.reopenClass({
find: function(bookId) {
var assets = Ember.A();
utils.get('book/' + bookId, function(response) {
response.assets.forEach(function(c) {
assets.pushObject(BookModel.create(c));
})
});
return assets;
}
});
var BooksModel = Ember.Object.extend({});
BooksModel.reopenClass({
findAll: function() {
var books = Ember.A();
utils.get('books', function(response) {
response.books.forEach(function(c) {
books.pushObject(BooksModel.create(c));
})
});
return books;
}
});
var BookRoute = Ember.Route.extend({
model: function(params) {
return BookModel.find(params.bookId);
}
,serialize: function(model, params) {
return { bookId: model };
}
});
<h2>books</h2>
<ul class="nav nav-tabs nav-stacked">
{{#each item in model}}
<li>
{{#linkTo "book" item.id}}{{item.name}}{{/linkTo}}
</li>
{{/each}}
</ul>
{{outlet}}
Basically every object (like your BookRoute) needs to be under your App namespace for ember to be found:
App.BookRoute = Ember.Route.extend({
...
});
Also you should pass the model object to the linkTo helper:
{{#linkTo "book" item}}{{item.name}}{{/linkTo}}
and then in your serialize hook you then do:
serialize: function(model) {
return { bookId: model.id };
}
Hope it helps.

{{action}} link with transitionTo using relationship id

Given a view with a context like { id: 1, form_id: 5}, I want to create an {{action}} link to the form using the form_id.
My view code looks like:
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
And the action in my router looks like:
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
I get an error that reads:
Uncaught Error: assertion failed: You must specify a target state for event 'showForm' in order to link to it in the current state 'root.index'.
I'm guessing that the problem is with the way I'm setting up the context for transitionTo, but I haven't been able to figure out the correct solution.
Here is the full code to reproduce the problem:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="group">
{{action showForm form_id href=true}}
</script>
MyApp = Ember.Application.create({
autoinit: false
});
MyApp.router = Ember.Router.create({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
// Throws error:
// You must specify a target state for event 'showForm' in
// order to link to it in the current state 'root.index'
//
showForm: function(router, event) {
var form_id = event.context;
router.transitionTo('root.form', { id: form_id });
},
// Won't work because form deserialize finds id, not form_id
//showForm: Em.Route.transitionTo('root.form'),
// This won't work either
// showForm: Em.Route.transitionTo('root.form', { id: this.form_id }),
connectOutlets: function( router, context ){
var group = Em.Object.create({ id:1, form_id: 5 });
router.get( 'applicationController' ).connectOutlet( 'group', group );
}
}),
form: Ember.Route.extend({
route: '/form/:id',
serialize: function( router, context ){
return { id: context.id }
},
deserialize: function( router, context ){
var form = Em.Object.create({ id: 5, name: 'my form' });
return MyApp.Form.find( context.id );
},
connectOutlets: function( router, context ){
// left out for fiddle example
}
})
})
});
MyApp.ApplicationController = Ember.Controller.extend({});
MyApp.GroupController = Em.ObjectController.extend({});
MyApp.GroupView = Em.View.extend({ templateName: 'group' });
MyApp.initialize(MyApp.router);​
And the cooresponding fiddle:
http://jsfiddle.net/jefflab/LJGCz/
I was able to come up with an ugly solution to this problem using a computed property as the context of my action. The key snippets are:
<script type="text/x-handlebars" data-template-name="group">
<a {{action showForm view.formHash href=true}}>go to form</a>
</script>
MyApp.GroupView = Em.View.extend({
templateName: 'group',
formHash: function() {
return { id: this.get('controller').get('form_id') };
}.property('form_id')
});
And the working fiddle is here:
http://jsfiddle.net/jefflab/pGv8u/
However, after talking to Tom Dale, it is clear that the "right way" to solve to solve this problem is by using materialized objects instead of id values. If you are using Ember data, this is a great use case for the "sideloading" belongsTo feature.