Emberjs serialize doesn't call model on {{linkTo}} - ember.js

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.

Related

property in route undefined in controller

In the IndexRoute of my Ember hello world app, I start a setInterval function that I wish to allow the end user to turn off (with clearInterval) by clicking a dom element in the template, which triggers an action in the IndexController. So, the setIntervalId is set in the IndexRoute, and I need to pass it to clearInterval in the IndexController, but the way I have it below, the setIntervalId is undefined. I also tried to use App.IndexRoute.setIntervalId to no avail.
How would I accomplish this?
(function() {
window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_ACTIVE_GENERATION: true
});
App.IndexRoute = Ember.Route.extend({
setIntervalId: 0,
model: function() {
this.setIntervalId = setInterval(this.someInterval, 5000)
},
someInterval: function(){
var datasource = 'http://hackernews/blahblah';
return new Ember.$.ajax({url: datasource, dataType: "json", type: 'GET'}).then(function(data){
return data;
})
},
});
App.IndexController = Ember.ObjectController.extend({
actions: {
clearTimeout: function(){
console.log('clearing interval', this.setIntervalId); //undefined
clearInterval(this.setIntervalId);
}
}
})
})();
template
<script type="text/x-handlebars" data-template-name="index">>
<h1>Hi Babe</hi>
{{ outlet }}
<label {{action "clearTimeout" on="click"}}>clear timeout</label>
</script>
To set the model, you need to return the value in the route’s model function:
model: function() {
return this.setIntervalId = setInterval(this.someInterval, 5000)
}
To access the model in the controller, you need to use this.get('model').
actions: {
clearTimeout: function(){
console.log('clearing interval', this.get('model');
clearInterval(this.get('model'));
}
}

Redirect if Invalid id is given

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/

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

Ember Router transitionTo nested route with params

App.Router.map(function() {
this.resource('documents', { path: '/documents' }, function() {
this.route('edit', { path: ':document_id/edit' });
});
this.resource('documentsFiltered', { path: '/documents/:type_id' }, function() {
this.route('edit', { path: ':document_id/edit' });
this.route('new');
});
});
And this controller with a subview event that basically transitions to a filtered document
App.DocumentsController = Ember.ArrayController.extend({
subview: function(context) {
Ember.run.next(this, function() {
//window.location.hash = '#/documents/'+context.id;
return this.transitionTo('documentsFiltered', context);
});
},
});
My problem is that this code works fine when Hash of page is changed.
But when I run the above code NOT w/ the location.hash bit and w/ the Ember native transitionTo I get a cryptic
Uncaught TypeError: Object [object Object] has no method 'slice'
Any clues?
Thanks
UPDATE:
App.DocumentsFilteredRoute = Ember.Route.extend({
model: function(params) {
return App.Document.find({type_id: params.type_id});
},
});
{{#collection contentBinding="documents" tagName="ul" class="content-nav"}}
<li {{action subview this}}>{{this.nameOfType}}</li>
{{/collection}}
The problem is that your model hook is returning an array, while in your transitionTo you are using a single object. As a rule of thumb your calls to transitionTo should pass the same data structure that is returned by your model hook. Following this rule of thumb i would recommend to do the following:
App.DocumentsController = Ember.ArrayController.extend({
subview: function(document) {
var documents = App.Document.find({type_id: document.get("typeId")});
Ember.run.next(this, function() {
return this.transitionTo('documentsFiltered', documents);
});
}
});
Note: I assume that the type_id is stored in the attribute typeId. Maybe you need to adapt it according to your needs.