I'm following an ember tutorial-
http://coding.smashingmagazine.com/2013/11/07/an-in-depth-introduction-to-ember-js/
and have edited it to include a new model and route. The original tutorial is a CRUD app for a collection of users. I now want to expand the app to deal with a list of subjects that these users might study. The router.jsfile now looks like this-
App.Router.map(function(){
this.resource('users', function(){
this.resource('user', { path:'/:user_id' }, function(){
this.route('edit');
});
this.route('create');
});
this.resource('subjects', function(){
this.resource('subject', {path: '/:subject_id'}, function(){
this.route('edit');
});
this.route('create');
});
});
(Subjects are a separate route because I want to be able to create separate routes for now)
I've added a subjects.js model which looks like this:
App.Subject = DS.Model.extend({
name : DS.attr(),
});
App.Subject.FIXTURES = [{
id: 1,
name: 'History',
}, {
id: 2,
name: 'Biology',
}];
a subjectsController:
App.SubjectsController = Ember.ArrayController.extend({
sortProperties: ['name'],
sortAscending: true // false = descending
});
a subjectsRoute:
App.SubjectsRoute = Ember.Route.extend({
model: function(){
return this.store.find('subject');
}
});
and a template in my index which looks like this:
<script type = "text/x-handlebars" id = "subjects">
<ul>
{{#each subject in controller}}
<li>{{subject.name}}</li>
{{/each}}
</ul>
</script>
I have added all my dependencies and followed the exact same steps in the tutorial as I did for the users CRUD app described in it, but now when I go to my browser, nothing renders. Can anyone see why?
In the tutorial described for you, there is a missing route to catch wrong paths, and redirect to the users route, maybe you need to include it to show something, since there isn't a index route to show some initial page.
Update your code with the following:
router.js
App.Router.map(function(){
this.resource('users', function(){
this.resource('user', { path:'/:user_id' }, function(){
this.route('edit');
});
this.route('create');
});
this.resource('subjects', function(){
this.resource('subject', {path: '/:subject_id'}, function(){
this.route('edit');
});
this.route('create');
});
// this is our 404 error route - see MissingRoute just bellow
this.route('missing', { path: '/*path' });
});
// this handles wrong routes - you could use it to redirect to a 404 route or like here to redirect to the index page
App.MissingRoute = Em.Route.extend({
redirect: function(){
this.transitionTo('users.index');
}
});
Related
If I have in the router map:
this.resource('detail', { path: '/detail/:type' }, function() {
...
});
And I retrive the currentPanth in my Ember Application code:
currentPath: '',
ApplicationController : Ember.Controller.extend({
updateCurrentPath: function() {
App.set('currentPath', this.get('currentPath'));
console.log('currentPath',App.currentPath);
}.observes('currentPath')
}),
When I navigate in my app, I get the route names by console, but when It is "detail" I get "detail.index". How can I get the type?
you only have access to the params in the route, ie. when you are defining your model:
App.Router.map(function() {
this.resource('photo', { path: '/photos/:photo_id' });
});
App.PhotoRoute = Ember.Route.extend({
model: function(params) {
return Ember.$.getJSON('/photos/'+params.photo_id);
}
});
Or you can also use paramsFor, also in the route only.
Depending on what you are trying to acomplish maybe query params suit better
I've set up two routes in Iron-Router: 'home' (a paged list of all post) and 'doc' (a detail view). The home page loads just fine, but the detail view can only be loaded if the home page has been viewed previously. Otherwise it will render empty – and it can't be used as a permalink.
This will always load:
http://localhost:3000/
This will only load if 'home' has been viewed before:
http://localhost:3000/doc/tZFawq8cgf43hZBaJ
the routes:
Router.map(function() {
this.route('home', {
path: '/'
});
this.route('doc', {
path: '/doc/:_id',
data: function() {
return MyPix.findOne({_id: this.params._id});
}
});
});
the doc template:
<template name="doc">
<h1>{{this.name}}</h1>
<img src="{{ this.url store='OriginalRetinaPix' }}" width="{{ this.metadata.width }}" height="{{ this.metadata.height }}" />
</template>
publish/subscribe:
Meteor.publish('MyPix', function(cursor) {
Counts.publish(this, 'numberOfPosts', MyPix.find(), { noReady: true });
return MyPix.find({}, {sort: {uploadedAt: -1}, limit: 4, skip: cursor});
});
if(Meteor.isClient) {
Session.setDefault('docCursor', 0);
console.log('docCursor: ' + Session.get('docCursor'));
Meteor.autorun(function(){
Meteor.subscribe('MyPix', Session.get('docCursor'));
})
}
btw: the project on GitHub
On your "doc" route, you should use the waitOn in order to have the data ready on page load. Add a loading template in the Router.configure as well
I recommend you to upgrade to the new iron:router routes declarations and also add meteorhacks:subs-manager for better cache on the subscriptions.
This is an example that should work in your case
var subs = new SubsManager();
Router.route('/doc/:_id', {
name: 'doc',
template: 'doc',
waitOn: function() {
return subs.subscribe('aPix', this.params._id);
},
data: function() {
return {
apix: MyPix.findOne({
_id: this.params._id
})
};
}
});
and on the server side create a publications.js
Meteor.publish('aPix', function(id) {
check(id, String);
return MyPix.find(id);
});
Use this.
Router.map(function() {
this.route('home', {
path: '/'
});
this.route('doc', {
path: '/doc/:_id',
waitOn: function(){
return Meteor.subscribe('MyPix');
},
data: function() {
return MyPix.findOne({_id: this.params._id});
}
});
});
Also you subscription should look like this.
Meteor.publish('MyPix', function(cursor) {
//Counts.publish(this, 'numberOfPosts', MyPix.find(), { noReady: true });
return MyPix.find({});
});
Also Add, meteor add sacha:spin, because when you have a lot of people, the subscription will be have a little delay.
Add this to each route.
loadingTemplate: "loading"
<template name="loading">
{{> spinner}}
</template>
Router.onBeforeAction("loading");
Just in case you are showing 100+ images on 'home' and someone enter and have a slow connection, he will think that the page load empty, or something.
You only subscribe to a subset of all the documents. If you directly go to /doc/tZFawq8cgf43hZBaJ, the document with the id tZFawq8cgf43hZBaJ may not be part of the subset of documents you receive on the client.
Note: if this answer is correct, you should be able to directly go to /doc/<id> for those documents showing up first on the home page (on the first page, when the session variable docCursor is 0).
I'm trying to poll for more data using the documented model.reload() function
App.ModelViewRoute = Ember.Route.extend({
actions: {
reload: function() {
this.get('model').reload();
}
}
});
But i'm getting an error message saying...
undefined is not a function TypeError: undefined is not a function
Is there a better way of doing this, it seems like I cannot access the model in this way from the route?
Here is the router
App.Router.map(function() {
this.route('video', { path: '/videos/:video_id' });
});
Here is the route
App.VideoRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('video', params.video_id);
},
actions: {
reloadModel: function() {
// PROBLEM HERE
// this.get('model').reload();
Ember.Logger.log('reload called!');
}
}
});
Here is the model
App.Video = DS.Model.extend({
title: DS.attr('string'),
status: DS.attr('string')
});
And the templates
<script type="text/x-handlebars" data-template-name="application">
<h1>Testing model reloading</h1>
{{#link-to "video" 1}}view problem{{/link-to}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="video">
<h1>Video</h1>
<h2>{{title}}</h2>
{{model.status}}
<p><button {{action 'reloadModel'}}>Reload model</button></p>
</script>
I've made a jsbin of the issue here:
http://jsbin.com/wofaj/13/edit?html,js,output
I really can't understand why the reload gives me this error. Any advice would be much appreciated.
Thanks
Since model already exists as a hook on Ember.Route, you cannot get that as a property.
Instead you can do the following:
this.modelFor('video').reload();
Technically you could do this.get('currentModel').reload(); too, but that's undocumented and probably won't be available in the future.
The refresh method of the route would do what you're after
App.VideoRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('video', params.video_id);
},
actions: {
reloadModel: function() {
this.refresh()
}
}
});
API docs
The route model function provides a hook to load your controller data. There is a specific section at the ember guide.
1) If you want to access your content, it would be like:
reload: function() {
this.controller.get('content');
}
2) reload is a method available of ember-data objects. In your example, you are loading a js object ({ id:2, title:"Test video title 2", status:"downloading"}).
My routes looks like this:
App.Router.map(function () {
this.resource('index', { path: '/' }, function() {
this.resource('label', { path: '/label' }, function() {
this.route('notes', { path: '/:label_id' });
});
this.route('manage', { path: '/manage' });
});
});
Users should only visit label.notes and manage routes. I'm trying to find solution how to implement redirection from index for example to label.notes. One of methods described in documentation is:
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('label.notes', 0);
}
});
Here is jsbin with full example http://jsbin.com/UnasOse/1/edit
This works if user navigates by clicking links, but if manage opened by url, or page updated user will be redirected to notes.
So, how to implement redirection only then user opens root url (index)?
Using the your current router mapping, you have the routes index.manage and label.notes.
When the page is refreshed in the index.manage, first it will transition to the parent route, in that case the index, and after to manage. But in you index you have the redirect, so the manage isn't processed.
Just remove of your mapping the resource('index') and update your manage route to reflect the new configuration, so link-to index.manage will become manage, and data-template-name=index/manage to manage etc.
The updated route mapping is the following:
App.Router.map(function () {
// this.route('index', { path: '/' }); generated by ember
this.resource('label', { path: '/label' }, function() {
this.route('notes', { path: '/:label_id' });
});
this.route('manage', { path: '/manage' });
});
You can keep your IndexRoute, because ember create by default a this.route('index', { path: '/' }). So your IndexRoute will execute just when the user go to http://yoursite.com/
You can see that sample in action in this jsbin http://jsbin.com/ucanam/1024/edit
I have a app with RestAdapter that takes proper data from server:
App.AFile= DS.Model.extend({
name: DS.attr( 'string' ),
...
App.Store = DS.Store.extend({
revision: 13,
adapter: DS.RESTAdapter.extend({url: "myapi"})
});
And a map like this:
App.Router.map(function() {
this.resource('allfiles', { path: '/allfiles' });
this.resource('onefile', {path: '/onefile/:onefile_id' });
And routes defined like this:
App.allfilesRoute = Ember.Route.extend({
model: function()
{
return App.AFile.find();
}
});
App.onefileRoute = Ember.Route.extend({
model : function(params)
{
return App.AFile.find(params.onefile_id);
}
});
And those templates:
<script type="text/x-handlebars" data-template-name="allfiles">
{{#each controller}}
{{#linkTo onefile this}}open{{/linkTo}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="onefile">
{{name}}
</script>
It works like this: user opens app and it displays allfiles template with a link called open. The link opens a template called onefile and passes the onefile_id to it.
When i open app and click open it works and displays proper name of one file. URL is set to #/onefile/1 where 1 is the onefile_id. So it works fine.
But when i refresh page (#/onefile/1) than name is not displayed anymore.
I've checked what is going on and in onefileRoute model function before i return the id and it occurs that App.AFile has null values for all fields defined. And after app loads those values are filled properly in the App.AFile object but are not displayed on the view.
So it looks like RestAdapter gets data after view display.
How to make it work?
App.onefileRoute = Ember.Route.extend({
model : function(params)
{
var m = App.AFile.find(params.onefile_id);
// at this point m might not be resolved, so name could be empty
return m;
},
afterModel: function(model, transition){
// at this point it should be resolved, what happens here?
console.log(model.get('name'));
}
});
My guess would be your endpoint is returning the wrong information for a model by id.