I am trying to accomplish something like this, in an ember view:
{{#if loggedIn}}
<p> I'm Logged In! </p>
{{else}}
{{view App.LoginView contentBinding="App.UserInfo"}}
{{/if}}
This doesn't work out of the box because the context for LoginView ought to be loginController, and *that controller's content" ought the be App.UserInfo.
This discussion has some related notes, and suggests outlets:
Instantiate a controller class using the {{view}} helper?
Outlets provide a clean solution to this - for example, I could do:
{{#if loggedIn}}
<p> I'm Logged In! </p>
{{else}}
{{outlet login}}
{{/if}}
and then have the router connect the controller for this view (call it homeController) to the login outlet with LoginView and some context.
However, using outlets, if the loggedIn property changes, the outlet isn't reconnected/redrawn, so if I log in and then log out again I get a blank page.
Is there a nice way to either bind the appropriate controller and controller content using the view helper, or set up the outlet in a way that makes it redraw appropriately if the loggedIn property changes?
Related
Is there a way in EmberJS to have a separate template for login that doesn't render the rest of the application template? Ideally I would like to create a login component, but I can't figure out how to render it on the login route without it being wrapped in the application template.
Unfortunately there isn't. There might be some very clever hacks, but there's no way out of the box. However, I had the same problem and solved it using a simple if statement. You can use the currentRouteName property to get the current route name, which allows you to do something like this:
Application Controller
isLoginRoute: Ember.computed('currentRouteName', {
get() {
return (this.get('currentRouteName') === 'login');
}
})
Application Template
{{#if isLoginRoute}}
{{outlet}}
{{else}}
<div>
<span>Some content</span>
{{outlet}}
</div>
{{/if}}
I want to use itemcontroller to a render a list of comments as well as perfom CRUD on the comment. The CRUD aspect works fine. But there are two things not working wekll and they are described below. Here is the jsfiddle.
Just click on the add Comment button to see it add an additional edit form underneath the existing one.
When I click on the button to create newComment which uses the CommentNewController, it automatically also renders EmBlog.CommentEditController and comment edit form along side the form for new comment. Both forms are independent of each and use different controllers and templates, so I don't understand why rendering the form for newComment automatically adds an empty form for editComment.
The second issue is that I have an edit button which is surrounded by an #if helper. If the #if helper is true then the edit form should be displayed. To toggle the #if helper to true, I created a button that contains the {{action editComment }}. When I click the button, the edit form is not rendered. But note that, when the template first renders, it automatically displays an edit form, even when the edit button has not been clicked.
Relevant template and controllers ares pasted below.
It is when the post/show template renders that an edit form is automatically displayed without waiting for an edit button to be clicked, which sets the #if helper to true
<script type="text/x-handlebars" data-template-name="posts/show">
//displays the add button link
{{render 'comment.New' }}
<br/>
**render the comments and use CommentEdit as itemController**
{{render 'comments' comments}}
</script>
Comments Template
<script type='text/x-handlebars' data-template-name='comments'>
<ul>
{{#each controller itemController="CommentEdit"}}
<li>{{body}}</li><br/>
{{partial 'comment/edit'}}
{{/each}}
</ul>
</script>
It seems this #if helper is bye-passed as the form is rendered without clicking edit button and when you click edit button, it does nothing
<script type='text/x-handlebars' data-template-name='comment/_edit'>
{{#if controller.editComment}}
{{#if model}}
<form {{action save content on='submit'}}>
{{view Ember.TextArea valueBinding="content.body" placeholder="body"}}
<button type="submit"> save comment </button>
<button {{action cancel content}}> Cancel</button>
</form>
{{/if}}
{{/if}}
<br/>
<div>
<button {{action editComment }} {{bindAttr disabled="isEditingComment"}}> Edit Comment</button>
When you click on the addComment button, it adds a new empty edit form but it shouldn't even be calling the edit form
<script type='text/x-handlebars' data-template-name='comment/new'>
{{#if controller.isAddingNew}}
{{partial 'comment'}}
{{/if}}
<div>
<button {{action addComment}} {{bindAttr disabled="isAddingNew"}}>Add Comment</button>
</div>
</script>
The comment partial for adding new comment
<script type='text/x-handlebars' data-template-name='_comment'>
<form {{action save content on='submit'}}>
<button {{action cancel content}}> Cancel</button>
</form>
</script>
The controllers
EmBlog.CommentNewController = Em.ObjectController.extend({
needs: ['postsShow'],
isAddingNew: false,
addComment: function(){
this.set('isAddingNew', true);
}
});
EmBlog.CommentEditController = Em.ObjectController.extend({
isEditingComment: false,
editComment: function() {
this.set('isEditingComment', true);
}
});
EmBlog.CommentsController = Em.ArrayController.extend({
needs: ['postsShow'],
itemController: 'CommentEdit'
});
Thanks.
working jsfiddle based on mike's answer. Update the ember-data implementation to use Emberjs1.0Rc-6 and the CommentNewController now using Kris Seldon's buffered save as explained here, to avoid error: Attempted to handle event willSetProperty rootState.loaded.updated.inFlight. Thesame code but using ember-model as datastore instead of ember-data: http://jsfiddle.net/TVe4X/4/ and updated to use Emberjs 1.0.0-RC6 and current ember-model : http://jsfiddle.net/tHWM4/5/
When I click on the button to create newComment which uses the CommentNewController, it automatically also renders EmBlog.CommentEditController and comment edit form along side the form for new comment. Both forms are independent of each and use different controllers and templates, so I don't understand why rendering the form for newComment automatically adds an empty form for editComment.
When you click newComment, a new (unsaved) comment is created. Since your comments template uses the each helper to loop over all comments, it is updated to have a new entry. You could get around this by filtering the list based on model state (ie show if isNew), hiding new comments via css or better yet refactor to show the new-comment-form inline. Of course the comment body is blank so you normally would just see a new bullet. But the edit form shows up as well, because of the issue below.
The second issue is that I have an edit button which is surrounded by an #if helper. If the #if helper is true then the edit form should be displayed. To toggle the #if helper to true, I created a button that contains the {{action editComment }}. When I click the button, the edit form is not rendered. But note that, when the template first renders, it automatically displays an edit form, even when the edit button has not been clicked.
Agreed the {{#if editComment}} helper is not working - it always evaluates to true. This is because editComment is a function not a property. Instead you probably want to reference the property isEditingComment.
Updated jsfiddle here: http://jsfiddle.net/mgrassotti/TVe4X/1/
The jsfiddle.
From the posts#index template, I can create a new comment by using the #linkTo helper which goes to the PostNewComment Route and renders the post/newcomment form. If I click save the newly created comment is persisted using the 'save event' inside the PostNewComment Route.
You can uncomment the line below in post/comments" template, to see it working
{{#linkTo "post.newComment"}} Add comment{{/linkTo}}
I changed my UI to use a controller isAddingNew button and the render helper to determine When to display the form and now if I click the save button, I get:
Uncaught TypeError: Cannot call method 'one' of null
This how I render it:
<p> {{render "post.newComment" }} </p>
I suspect it is a scope issue because the error is only triggered when 'save' is clicked after using the render helper.
To reach the 'add new comment' button:
click -> post -> a post title -> click comments link -> add comment
Is there a way to make the 'Post/newComment form' displayed via the 'render helper' in the post/comments template to use the 'save event' defined in the PostNewComment Route.
Right now clicking on the 'save button' which is defined in that form goes directly to the parent route ie PostCommentsRoute instead of going to its own route probably because I displaying the form via the render helper.
I thought calling 'save' should go to its own controller and then bubble to its own route where it is actually defined, before attempting to bubble up the hierarchy to the PostComments Route.
There's probably a few alternatives, but this works and is pretty idiomatic: http://jsfiddle.net/GabSY/4/
In particular:
{{#if model}}
<form {{action save content on='submit'}}>
{{view Ember.TextArea valueBinding="content.body" placeholder="body"}}
<button type="submit"> save comment </button>
<button {{action cancel}}> Cancel</button>
</form>
{{else}}
<button {{action open}}> Add comment </button>
{{/if}}
The reason you were getting the Uncaught TypeError: Cannot call method 'one' of null error was that the PostNewCommentController's model was never set. What I ended up doing was using the open action on the PostNewCommentController to set the model of the controller, which can be used in a Handlebars {{if}} to determine whether the form should be displayed or not.
I prefer this approach to the alternative of setting content/model (they are aliases to each other) of the PostNewCommentController from within the PostCommentsRoute's setupController method because if you go down that route, it's easy to start mixing concerns between not-very-related controllers and routes. In my approach, all the logic for setting the content/model of the new comment takes place in the controller for new comments, which makes sense since a new comment no longer has its own route to initialize this data.
(Note: I am using Ember version 1.0.0-rc.3)
I'm trying to catch the 'click' of a {{linkTo}} using a view, so that I can do additional stuff (basically scroll the list of users in the sidebar) besides merely loading the new template. Me being relatively new to this (but having read the documentation!), I thought the following would just work:
"users" template:
{{#each user in users}}
{{#view App.ClickView}}
{{#linkTo user user}}{{ user.name }}{{/linkTo}}
{{/view}}
{{/each}}
the view code:
App.ClickView = Ember.View.extend({
click: function(evt) {
// do stuff
}
});
and for context, the layout template:
<div id='sidebar'>
{{#each user in users}}
{{#linkTo user user}}{{ user.name }}{{/linkTo}}
{{/each}}
</div>
<div id='main'>
{{ outlet }}
</div>
Referring back to the users template, you can see that each {{linkTo}} is contained within a view. I'm expecting for a click on that {{linkTo}} to therefore bubble up to, and caught by the view (App.ClickView). Unfortunately, it doesn't. It seems like the click is somehow not being bubbled up to the view when it's happens on a {{linkTo}}... What should I do?
Note #1:
If I replace the {{linkTo}} (only in div#main! I don't intend to replace the ones in div#sidebar) with an <a> element, it works, and the click gets caught by the view. However, I'm not so sure that i want to go down this route (I'd have to replicate the functionality of the {{linkTo}}!). And I'm thinking that there ought to be a better way to do this. Is there?
Note #2:
*Note that I'm also aware that i can put my intended "do stuff" code in renderTemplate() of the UserRoute, but the problem with that is that the effect will happen for every link to that route (including the ones in the sidebar - which is not what I want). I want the scroll to only trigger for specific {{linkTo}}s - specifically the {{linkTo}}s in div#main.
I would suggest using an anchor (<a>) tag with {{action ... target="view"}} in it instead of linkTo, apply your conditional logic in the view, and then if appropriate, re-send to the controller (this.get('controller').send(actionName), let it bubble to the router, and do a transitionTo in a router event handler.
Template controlled by someParentController
{{#each post in content}}
{{view App.PostView postBinding="post"}}
{{/each}}
Setting an instance of the controller on the view
App.PostView = Ember.View.extend
post: null # set when the view is created
controller: App.PostController.create()
templateName: 'post.handlebars'
Now my view instance has the context instead of my controller instance. Is there a cleverer way to handle this? I would use an {{outlet}} if I was routing to a particular post, but the main template is displaying all posts. I want each to post to have its own controller though. It doesn't seem right to create an outlet for every post since you cannot namespace a dynamic number of outlets.
You can bypass the view entirely by using the following syntax on your action helpers in post.handlebars.
{{action someMethodOnController context="post" target="controller"}}