Im following the example emberjs guides
...
this.route('author', { path: '/author/:post_userName' });
...
App.PostsAuthorRoute = Ember.Route.extend({
model: function(params) {
return App.Post.find({userName : params.userName});
},
serialize:function(model) {
return { post_userName: model.get('userName')};
}
});
Then here is the link to
Author {{#linkTo 'posts.author' post }} {{post.userName }} {{/linkTo}}
The fun is when I click on the link I get a routing error
Error while loading route: TypeError {}
Uncaught TypeError: Object [Object Object] has no method 'slice'
But when I reload the page, the full data appears.
How can I solve the routing error, really I don't understand why I get the error and is solved on reload the page
Here is the jsbin of a similar case.
http://jsbin.com/aZIXaYo/31/edit
The problem is with the object you're passing to your link-to. You're doing this :
Author {{#linkTo 'posts.author' post }} {{post.userName }} {{/linkTo}}
which passes, a Post to the author route. Passing a model to link-to causes the model hook on the route to be skipped and the passed model is used instead. When you hit reload, the model hook is executed and the model for PostsAuthor route is set to be a collection of Post objects, and then things work as expected.
To do things The Ember Way (TM), you'd want to have an Author model that was related to your Post model. Then you'd have an AuthorRoute and AuthorController that needs a PostsController. In your link you'd pass post.author, and then use the setupController hook to prime the collection for the PostsController. Something like this:
App.Post = DS.Model.extend({
author : DS.belongsTo('post'),
title : DS.attr('string')
});
App.Author = DS.Model.extend({
name : DS.attr('string'),
userName : DS.attr('string')
});
App.AuthorRoute = DS.Route.extend({
model : function(){ // not called when the author is passed in to #link-to
return this.store.find('author',{userName : params.post_userName})
},
setupController : function(controller,model){
this._super(controller,model);
this.controllerFor('posts').set('content', this.store.find('post',{userName : model.userName}))
}
});
App.AuthorController = Ember.ObjectController.extend({
needs : ['posts']
});
App.PostsController = Ember.ArrayController.extend({
sortProperties : ['name']
});
Then the template :
Author {{#linkTo 'posts.author' post.author }} {{post.author.name }} {{/linkTo}}
Related
I created an ember demo,A parent view and it's child
this is the parent view
<h1>A list of Todo Tasks</h1>
<ul>
{{#each model as |todo|}}
<li>{{#link-to "todos.details" todo}}{{todo.desc}}{{/link-to}}</li>
{{/each}}
</ul>
{{outlet}}
and Its js login is
import Ember from 'ember';
export default Ember.Route.extend({
model (){
return [{
"id" : 1,
"desc" : "Try use ember"
},
{
"id" : 2,
"desc" : "Add it to github"
},
];
}
});
but when i use the link-to and navigate the data didn't show unless i refresh the page
This is the child view
<h2>The Details for <span style="color: green;">{{model.name}}</span> is : </h2>
{{#if model.error}}
<p>{{model.message}}</p>
{{else}}
<ul>
{{#each model.steps as |task|}}
<li>{{task.do}}</li>
{{/each}}
</ul>
{{/if}}
{{outlet}}
and its js logic
import Ember from 'ember';
export default Ember.Route.extend({
model(params){
if(params.id == "1"){
return {
name : "Ember SetUp",
steps : [{
id :1,
do : "Download Ember Cli"
},
{
id :2,
do : "Generate New Project"
},
{
id :3,
do : "Generate Pages&Routes"
}
]
};
}
else{
return {
error : true,
name : "Not Found",
message : "There is no task with this id"
}
}
}
});
iam using ember 2.5 and this is part of the router
this.route('todos', function() {
this.route('details',{path : "/:id"});
});
{{#link-to "todos.details" todo}}
Since you are providing the object todo, so it will not execute the model hook.
so try
{{#link-to "todos.details" todo.id}}
Refer here: https://guides.emberjs.com/v2.13.0/routing/specifying-a-routes-model/#toc_dynamic-models
Note: A route with a dynamic segment will always have its model hook
called when it is entered via the URL. If the route is entered through
a transition (e.g. when using the link-to Handlebars helper), and a
model context is provided (second argument to link-to), then the hook
is not executed. If an identifier (such as an id or slug) is provided
instead then the model hook will be executed.
Ack, OP of video here. Sorry about that. Small misspeaking on my part, I should address this in the comments of the video and try to revise that. Sorry for the confusion! D:
I have a form within the new-phone route which populates a phone model. This model has a belongsTo relationship with the client.
App.Router.map(function() {
this.resource("client", { path: "/client/:id" }, function(){
this.resource("phone", function(){
this.route("new-phone")
})
})
})
App.Client = DS.Model.extend({
name: DS.attr(),
phone: DS.belongsTo("Phone", { async: true })
});
App.Phone = DS.Model.extend({
name: DS.attr(),
model: DS.attr(),
year: DS.attr()
});
When I complete the form, the response from the server comes back correctly with the newly created record.
I'm getting data from JSON driven API.
So I'm posting to:
POST: /api/client/1/phone
I have set the transition to go back to the phone.index page (after the save is complete) which in turn (should) fire the model hook (GET request: /api/client/1/phone) and get the new data for the (phones.index) page. But for some reason, I get a 'data is null' error from Ember. It doesn't even seem to make the request before this error appears.
If I use the HTTP requester outside of the Ember app, the data appears.
App.ClientRoute = Ember.Route.extend({
model: function(){
return this.store.find("client", 1)
}
});
App.PhoneIndexRoute = Ember.Route.extend({
model: function(){
return this.modelFor("client").get("phone").then(function(data){
//Reload to manually get new data
return data.reload();
});
}
})
This is the version of Ember I'm using:
DEBUG: Ember : 1.8.1
DEBUG: Ember Data : 1.0.0-beta.11
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery : 1.10.2
I dont think you need a new route to show a form for person's phone. Instead make a property to toggle when user clicks for phone form
Let's say your template looks like this
/user/1.hbs
{{model.user_name}} {{model.first_name}}
{{model.phone.number}}
{{#if showPhoneForm}}
<div class="form">
{{input value=model.phone.number placeholder="phone nr"}}
</div>
<div class="button" {{action "saveUser"}}> Save </button> // Save form data, hide user form, updaet user phone data
{{else}}
<div class="button" {{action "makePhoneForm"}}> Add phone </button> // Create form for user
{{/if}}
controllers/user/
showPhoneForm: false,
actions: {
makePhoneForm: function() {
this.set('showPhoneForm', true);
},
saveUser: function() {
this.get('model.phone').then(function(phoneRecord) {
phoneRecord.save().then(function(){
this.set('showPhoneForm', false);
}.bind(this):
}
}
I've been trying to figure this out for most of today and it's driving me insane, because I think i'm almost there, but just can't figure the last part out...
I have a route, called Map, which renders a sidebar, and within it has a named outlet for sidebar content:
map.hbs:
<div id="map-container">
{{render sidebar}}
<div id="map-canvas">
</div>
</div>
...
sidebar.hbs:
<div id="content-menu">
{{outlet sidebar-content}}
</div>
Each menu item in my sidebar has a custom action called loadModule, which performs a render of a named view into the sidebar-content outlet (using {{action 'loadModule' 'sidebar.module'}}):
var MapRoute = App.AuthenticatedRoute.extend({
actions: {
loadModule: function(module) {
//load a valid view template into the view
this.render(module,
{
into: 'sidebar',
outlet: 'sidebar-content'
});
}
}
});
module.exports = MapRoute;
Any action within the controller for that view works fine, I can trigger them from buttons etc, or by calling them in a didInsertElement in the SidebarModuleViews.
My issue is that I can't define a model for these views, so if I try and get data from my API in any of their Controllers, it won't render that data out to the templates.
I tried to use link-to, but I couldn't make the template append to the current viewport, rather than refreshing the entire page, which defeats the point of having a sidebar (I don't want the route to change)
var SidebarUserController = App.ApplicationController.extend({
actions: {
doSomething: function() {
alert('SOMETHING');
},
fetchUserProfile: function() {
//do something
var mod = this.store.find('profile', App.Session.get('uid'));
}
}
});
I can trigger either of those actions from the rendered template once it's rendered, however, although my store updates with the record, the handlebars helpers in the sidebar/user.hbs do not populate with the model data.
Here is my model:
var Profile = DS.Model.extend({
uid: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
gender: DS.attr('string'),
DOB: DS.attr('date'),
email: DS.attr('string')
});
module.exports = Profile;
and here is my sidebar/user.hbs:
<div class="container">
<button {{action 'doSomething'}}>Do A Thing</button>
<h1>{{firstName}} {{lastName}}</h1>
<h4>{{id}}</h4>
{{#if isAuthenticated}}
<a href="#" {{action 'logout'}}>Logout</a>
{{/if}}
</div>
In that template, the firstName, lastName and id fields do not populate, even though i'm pulling the data from the API and successfully storing it.
Additionally, if it helps, my router.map for sidebar/user looks like this:
this.resource('sidebar', function() {
this.route('user');
});
I believe that the fundamental issue here is that I can't work out how to set the model for the controller without triggering the route. Am I going about this wrong?
Ok so i've worked this out for my particular instance. It may not be the best way of doing it, but it's what I need:
In my MapRoute, I setup the model and controller for my additional sidebar menus in the setupController function. Doing this allows me to load critical data (such as user profile etc), on page load, and I can still retain the render function for each sidebar module in the Route, which will allow the intial data to load, and still allow me to update the model data for each sidebar module controller on subsequent functions:
map_route.js:
actions: {
loadModule: function(module) {
this.render(module, {into: 'sidebar', outlet: 'sidebar-content'});
}
},
setupController: function(controller, profile) {
var model = this.store.find('profile', App.Session.get('uid'));
var controller = this.controllerFor('sidebar.user');
controller.set('content', model);
},
...
I want to implement a system that shows me the newest posts. For this I do not want to use the index action from the user as this is already taken for another post function but a "newest" action. It is showed on the index route with a {{ render "postNewest" }} call. I would prefer to load the data in the PostNewestController or PostNewestView instead of the route for abstraction reasons.
I tried two ideas to achieve this, but none worked so far:
create a custom adapter and add a findNewest() method: the findNewest() method is sadly not found when trying to call in the init method of the controller.
write the request directly into the init method and then update with store.loadMany(payload): data is successful request. However, I do not know how to access the data from the template and set the content of the controller.
Is there any way for this?
EDIT:
Here is the source code to better understand the problem:
PostModel.js
App.Post.reopenClass({
stream: function(items) {
var result = Ember.ArrayProxy.create({ content: [] });
var items = [];
$.getJSON("/api/v1/post/stream?auth_token=" + App.Auth.get("authToken"), function(payload) {
result.set('content', payload.posts);
});
return result;
}
});
PostStreamController.js
App.PostStreamController = Ember.ArrayController.extend({
init: function() {
this.set("content", App.Post.stream());
},
});
index.hbs
{{# if App.Auth.signedIn}}
{{ render "dashboard" }}
{{else}}
{{ render "GuestHeader" }}
{{/if}}
{{ render "postStream" }}
postStream.hbs
{{#each post in model}}
<li>{{#linkTo 'post.show' post data-toggle="tooltip"}}{{post.name}}{{/linkTo}}</li>
{{else}}
Nothing's there!
{{/each}}
PostShowRoute.js
App.PostShowRoute = Ember.Route.extend({
model: function(params) {
return App.Post.find(params.post_id);
},
setupController: function(controller, model) {
controller.set('content', model);
},
});
I Had this issue too. Just add init in your controller, and define the model you want to get there.
In your Controller
App.PostRecentController = Ember.ArrayController.extend({
init: function() {
return this.set('content', App.Post.find({
recent: true
}));
}
});
In your template
{{#each post in content}}
{{post.body}}
{{/each}}
I would recommend you check EMBER EXTENSION, it will give you a good idea of the naming, and see if everything is missing.
I figured out the problem. The Post model has a belongsTo relationship to another model. This relationship is not loaded by Ember so in the stream() method I have to load this manually. Then everything works as expected.
After following the "Outlets" guide on Emberjs.com (http://emberjs.com/guides/outlets/) I am unable to show the title and body of the post in the Post (child/singular) template. Have any of you run into this? Here's the rundown:
# PostsTemplate
{{#each post in controller}}
<h1><a {{action showPost post href=true}}>{{post.title}}</a></h1>
<div>{{post.body}}</div> <------ title and body show up correctly here
{{/each}}
# PostTemplate
<h1>{{title}}</h1> <---- but title and body are EMPTY here
<div class="body">
{{body}}
</div>
{{outlet}}
# Router
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
redirectsTo: 'posts'
}),
posts: Ember.Route.extend({
route: '/posts',
showPost: Ember.Route.transitionTo('post'),
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('posts', App.Post.find());
}
}),
post: Ember.Route.extend({
route: '/posts/:post_id',
connectOutlets: function(router, post) {
console.log(post); <------------------ This tells me the post has the write attributes.
router.get('applicationController').connectOutlet('post', post); #This post does not show up in PostTemplate above!
}
})
});
When I run console.log(post), and inspect the post, I see that I have something like the following:
Class = {
id: "1",
_data:
{
attributes:
{
body: "a",
title: "a"
}
},
title: null,
body: null
}
Anyone have any ideas as to why I'm not seeing the title or body attributes show up in the view?
p.s. the Post model is an Ember data model that correctly retrieves the Post with id 1 from a Rails application.
I believe you can use {{content.title}} and {{content.body}} since your Post model should be set to the content of the postController. An easy way to debug this within the view is to put {{content}} in the view which will render the type of model that the item is (such as App.Post).