Reputation: 2101
Open a form in a lightbox to create a new "event"; the opened form should be bookmarkable.
{{action}}
tags, but could not find one that opened in its own route.The fields in the form were not being tied to a backing model so "null" was being posted to my servlet (a Spring controller).
My very first iteration was not too far off from the final outcome (jsfiddle). The thing that finally made it works swapping this:
EP.EventsNewRoute = Ember.Route.extend({
...
setupController : function(controller, model) {
controller.set("model", model);
},
...
});
...for this:
EP.EventsNewRoute = Ember.Route.extend({
...
setupController : function(controller, model) {
this.controllerFor("events-new").set("model", model);
},
...
});
Why does the setupController function need to call controllerFor
in order to properly set up the model?
And finally, since I struggled to find a fully-functional example, I wanted to make this accessible (and hopefully discover improvements).
Here's the fiddle: http://jsfiddle.net/6thJ4/1/
Here are a few snippets.
<script type="text/x-handlebars">
<div>
<ul>
{{#linkTo "events.new" tagName="li"}}
Add Event
{{/linkTo}}
</ul>
</div>
{{outlet events-new}}
</script>
<script type="text/x-handlebars" data-template-name="events-new">
<form>
<div>
<label>Event Name:</label>
{{view Ember.TextField valueBinding="name"}}
</div>
<div>
<label>Host Name:</label>
{{view Ember.TextField valueBinding="hostName"}}
</div>
</form>
</script>
...
EP.Router.map(function() {
this.resource("events", function() {
this.route("new");
});
});
EP.EventsNewRoute = Ember.Route.extend({
model : function() {
return EP.Event.createRecord();
},
setupController : function(controller, model) {
//controller.set("model", model); // Doesn't work? Why not?
this.controllerFor("events-new").set("model", model); // What does this do differently?
},
...
});
EP.EventsNewController = Ember.ObjectController.extend({
save : function() {
this.get("content.transaction").commit(); // "content.store" would commit _everything modified_, we only have one element changed, so only "content.transaction" is necessary.
}
});
EP.EventsNewView = Ember.View.extend({
...
});
EP.Event = DS.Model.extend({
name : DS.attr("string"),
hostName : DS.attr("string")
});
Upvotes: 2
Views: 565
Reputation: 5075
Why does the setupController function need to call controllerFor in order to properly set up the model?
Ember makes URLs a very integral part of its conventions. This means that the state
of your application is represented by the route it is on. You've grokked most of this correctly. But there are couple of subtle nuances, that I will clarify below.
First consider an app with the following URLs,
And say clicking on a post in the list at /posts
takes you to /posts/1
.
Given this scenario, there 2 ways a user will get to see the post at /posts/1
.
/posts
and clicking on the 1st post./posts/1
, via bookmarks etc.In both these cases, the PostRoute
for /posts/1
will need the model corresponding to Post
id 1.
Consider the direct typing scenario first. To provide a way to lookup the id=1 post model, you would use,
model: function(params) {
return App.Post.find(params.post_id);
}
Your template for post will get the model and it can render using it's properties.
Now consider the second scenario. Clicking on post with id=1 takes you to /posts/1
. To do this your template would use linkTo
like this.
{{#linkTo 'post' post}} {{post.title}} {{/linkTo}}
Here you are passing in the post
model to the linkTo
helper. It then serializes the data for the post into a URL, ie:- '/posts/1'. When you click on this link Ember realizes that it needs to render the PostRoute
but it already has the post model. So it skips the model hook and directly calls setupController
.
The default setupController
is setup to simply assign the model on the controller. It's implemented to do something like,
setupController: function(controller, model) {
controller.set('model', model);
}
If you do not need to set custom properties on your controller, you don't need to override it. Note: if you are augmenting it with additional properties you still need to call _super
to ensure that the default setupController behaviour executes.
setupController: function(controller, model) {
this._super.apply(this, arguments);
controller.set('customProp', 'foo');
}
One final caveat, If you are using linkTo
and the route does not have dynamic segments
, then the model hook is still called. This exception makes sense if you consider that you were linking to the /posts
route. Then the model hook has to fire else Ember has no data to display the route.
Which brings us to the crux of your question. Nearly there, I promise!
In your example you are using linkTo
to get to the EventsNewRoute
. Further your EventsNewRoute
does not have dynamic segments so Ember does call the model
hook. And controller.set("model", model);
does work in so much as setting the model on the controller
.
The issue is to do with your use of renderTemplate
. When you use render
or {{render}}
helper inside a template, you are effectively getting a different controller to the one you are using. This controller is different from the one you set the model
on, hence the bug.
A workaround is to pass the controller in the options, which is why renderTemplate
gets this controller as an argument.
renderTemplate: function(controller) {
this.render("events-new", {
outlet : "events-new", controller: controller
});
}
Here's an updated jsfiddle.
Final Note: Unrelated to this question, you are getting the warning,
WARNING: The immediate parent route ('application') did not render into the main outlet and the default 'into' option ('events') may not be expected
For that you need to read this answer. Warning, it's another wall of text! :)
Upvotes: 2