Ember.JS - Return & Display Parameter from URL - ember.js

I'm building my first Ember app and I'm trying to get it to pass and display a parameter in the view, grabbing it from the URL...
So basically if someone went to index.html#/quotes/5
I want the view to display "TEST: 5"
App.js:
App.Router.map(function(){
this.resource('quote', function(){
this.resource('quoteNumber', {path: ':quote_id'});
});
});
App.quoteNumberRoute = Ember.Route.extend({
model: function(params){
return(params.quote_id);
}
});
HTML:
<script type="text/x-handlebars" id="quote">
Test: {{outlet}}
</script>
<script type="text/x-handlebars" id="quoteNumber">
{{quote_id}}
</script>
so if I go to "example.com/index.html#/quote/3"
I'd simply want the view to display "TEST: 3",
if I went to "example.com/index.html#/quote/10"
I'd want the view to display "TEST: 10"
but right now it displays nothing, and I can't find what's missing.

The main issue is the use of quote_id in the template. Inside the template you are telling handlebars to grab the property quote_id off the model provided for that template.
Dissecting the model associated with that route you'll see that the model itself is the quote_id value, 5 or 7 etc. The model that would fit your template would be { quote_id: 5 } that way Ember/Handlebars can search for the property quote_id on the model and bind to that value.
Here's an example of what you're trying to do (note, I could have just returned the params object itself).
http://emberjs.jsbin.com/OxIDiVU/309/edit
I also added a convenient link, but you could type in any url and get what you were desiring, http://emberjs.jsbin.com/OxIDiVU/309#/quote/123123
PS. You didn't show, nor mention, but you also need an application template. This is the root of your application, if you plan on having nothing in it, you can just put a {{outlet}}
Ex.
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
or for short
<script type="text/x-handlebars">
{{outlet}}
</script>

Related

Custom view helper in Ember.js, "You can't use appendChild outside of the rendering process"

I want to bind my custom view's class to a controller property.
[javascript]
App.IndexController = Ember.Controller.extend({
headerClass: "a"
});
App.TestHeaderView = Ember.View.extend({
classNames: ["test-header"],
classNameBindings: ["headerClass"],
headerClass: null,
templateName: "views/test-header"
});
[templates]
<script type="text/x-handlebars" data-template-name="index">
{{view App.TestHeaderView text="view helper" headerClass=controller.headerClass }}
<hr />
{{input value=headerClass}}
</script>
<script type="text/x-handlebars" data-template-name="views/test-header">
<small>{{view.text}}</small>
</script>
The result is predictable: everything works. I can enter the class name in the text box and see it reflected in the view.
So now I want to extend this and add my own helper that wraps the {{view}} call.
[javascript]
Ember.Handlebars.helper("test-header", function (options) {
return Ember.Handlebars.helpers.view.call(this, App.TestHeaderView, options);
});
[templates]
<script type="text/x-handlebars" data-template-name="index">
{{test-header text="custom helper" headerClass=controller.headerClass}}
</script>
Nothing special right? Except, I keep getting this:
Uncaught Error: You can't use appendChild outside of the rendering process
For full working jsbin, click here.
It seems this should work. I'm just wrapping the ember's view helper pretty much exactly. What am I missing?
I figured it out.
The trick is in the contexts array in the options hash.
When you call {{view App.MyView}} from handlebars, Ember's view helper gets in its options.contexts array the "context" in which it should search for "App.MyView" property - usually the current controller. In this case, "App.MyView" will be resolved regardless of the context, but I guess Ember keeps the context around and uses it to resolve bound properties.
When I called:
{{test-header text="custom helper" headerClass=controller.headerClass}}
there was no first argument from which to draw the context. Therefore, when I passed the call along to the view helper:
return Ember.Handlebars.helpers.view.call(this, App.TestHeaderView, options);
... there was no context passed along in the options.contexts array.
The way I fixed this is:
Ember.Handlebars.helper("test-header", function (options) {
options.contexts = [this].concat(options.contexts);
return Ember.Handlebars.helpers.view.call(this, App.TestHeaderView, options);
});
IMO Ember should do a better job here. They should either figure out a context from reference, or throw an error (a preferred option).

Ember.js: ArrayController undefined in template

Problem:
I am kind of struggling with the organization of my first ember app. The current issue is that the my Items ArrayController is not defined in my dashboard template:
<script type="text/x-handlebars" data-template-name="dashboard">
{{#if controllers.items}}
<p class="alert alert-error">Dashboard can access item's info - Nice!</p>
{{else}}
<p class="alert alert-error">Dashboard cannot access items... :-/</p>
{{/if}}
</script>
Likely cause: *
**EDIT: after talking with #conrad below, I'm kind of questioning this:*
I had a similar issue in an earlier post and kingpin2k suggested the cause was that I:
"never created anything that uses the options controller".
This is probably the case here as well. This quick screencast shows that a breakpoint on my ArrayController is not hit on page load - but it is hit when I inspect the Items controller in the Ember inspector tool (eg, Ember creates the ArrayController object right then for the first time).
Apparent non-solutions:
My Dashboard controller says it needs the Items controller. I guess that isn't enough to instantiate the ArrayController?
App.ItemsController = Ember.ArrayController.extend({
len: function(){
return this.get('length');
}.property('length'),
totalCost: function() {
return this.reduce( function(prevCost, item){
return parseInt(item.get('values').findBy('type', 'cost').price, 10) + prevCost;
}, 0);
}.property('#each.values')
[more computed properties...]
});
App.DashboardController = Em.Controller.extend({
needs: ['items'],
itemsLength: Ember.computed.alias('controllers.items.len'),
itemsTotalCost: Ember.computed.alias('controllers.items.totalCost'),
[more computed properties...]
});
Furthermore, each item in Items is being rendered in my items template. I guess that does not create the missing controllers.items either...
<script type="text/x-handlebars" data-template-name="items">
{{#each}}
[these render fine]
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="display">
<!-- DISPLAY TEMPLATE -->
{{!- DASHBOARD -}}
{{render dashboard}}
{{!- ITEMS -}}
{{render 'items' items}}
</script>
So then.. what?
I can imagine many possible avenues, but haven't gotten any of them to work yet:
Specify the Items ArrayController in {{render dashboard}}?
Some configuration in a Route?
Maybe my templates/routes are not correctly arranged?
You could make sure that the ItemController is instantiated in the dashboard template by calling it in the DashboardController's init function:
App.DashboardController = Em.Controller.extend({
needs: ['items'],
init: function() {
this._super();
this.get('controllers.items.length');
}
});
/edit:
removed the part that was not helpful

Ember js version 1: Route hierarchy / activation

We have a scenario along these lines:
Quote
--->Create
So route names quote and quote.create.
The issue is that we need to render the templates into the main outlet. So in our main route (that all other are inherited from) we have this:
renderTemplate: function() {
this.render({ into: 'application' });
}
When I navigate to quote it renders the quote view. From there I navigate to quote.create and it renders the create view. However, going back to quote from quote.create renders nothing.
How can I get around this?
When I go back to the \quote url route 'quote.index' is sought. Since it is defined 'automagically' nothing happens. When I define the route explicitly ember tries to find the quote.index template and view and these do not exist.
A workaround I tried is to have this:
App.QuoteIndex{Route|Controller|View} = App.Quote{Route|Controller|View}.extend()
EDIT:
Hey diddle-diddle, here is my fiddle :) http://jsfiddle.net/EbenRoux/Mf5Dj/2/
Ember.js does not rerender a parent view when transitioning to a parent route, so using into with a parent view template is not recommended.
There is an easier way to create what you are trying to: use a quote/index route:
<script type="text/x-handlebars" data-template-name="application">
<h1>Rendering Issue</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="quote">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="quote/index">
<h2>Quote View</h2>
{{#linkTo 'quote.create'}}Create a new quote{{/linkTo}}
</script>
<script type="text/x-handlebars" data-template-name="quote/create">
<h2>Quote Create View</h2>
<p>Some controls would go here.</p>
{{#linkTo 'quote'}}Go back to quote view{{/linkTo}}
</script>
App = Ember.Application.create({});
App.ApplicationRoute = Ember.Route.extend({
activate: function () {
this.transitionTo('quote');
}
});
App.Router.map(function () {
this.resource('quote', function () {
this.route('create');
});
});
See http://jsfiddle.net/eYYnz/

EmberJS - adding some template in separated file

I'm a newbie yet on EmberJS, started to learn a week ago. Well, I have some .html file named contactsTemplate.html that is a template what I want to use. Something like this below:
<script type="text/x-handlebars" data-template-name="contacts">
<h2>My Contact List</h2>
{{#each contact in contactList}}
{{contact.name}}
{{/each}}
</script>
Great. I have a index.html file that contains an {{outlet}} expression. It means that every new content will be loaded into this space, right?
<script type="text/x-handlebars" data-template-name="application">
<h1>My Application</h1>
{{outlet}}
</script>
Ok... To manage this template named application, I've created a Controller object.
App.ApplicationController = Ember.ObjectController.extend({
...
});
Same to contacts template:
App.ContactsController = Ember.Controller.extend({
contactList: []
});
I've created two routes to manage this transition between pages:
App.Router.map(function() {
this.route("index", "/");
this.route("contacts", "/contacts");
});
My doubt is:
How can I associate this separated files to a route? I mean, when I call on browser http://mysite.com/ the index.html file should be loaded. When I call http://mysite.com/#/contacts the contactsTemplate.html should be loaded.
Sorry for the long text, and thanks for any help!
By default each declared this.route(routeName) in Router map, will have a [routeName]Controller, [routeName]Route, and expect a [routeName] template, following these conventions described in http://emberjs.com/guides/concepts/naming-conventions/.
To change from one route to other, you can use:
Inside of a controller this.transitionToRoute(routeName)
Inside of a route this.transitionTo(routeName)
Inside of a template {{#link-to "routeName" }}My route{{/link-to}}
You can find more information in the routing guide http://emberjs.com/guides/routing
I hope it helps

How to have an element load with the application in Ember.js

I am trying to create my first ember application but I can't have a template load with the application template:
Below is a simplified version of my layout:
<script type="text/x-handlebars">
{{outlet}}
</script>
How do I get the another template into the outlet section on page load?
Thanks in advance
For example if my second template is:
<script type="text/x-handlebars" id="yellow">
<p>Something</p>
</script>
#Marcio answer is correct, ember does create an implicit index route.
However the most important part here is that if you have, say a route like yellow that you want to be rendered inside the application's {{outlet}} (instead of the implicit index template) then you need to define a path for that route so the correspondent named template, (in your case yellow) will be rendered into the application's {{outlet}} instead of the implicit index:
App.Router.map(function() {
this.route('yellow', {path: '/'})
});
<script type="text/x-handlebars" data-template-name="application">
<h1>Hi</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="yellow">
<h2>Welcome from yellow</h2>
</script>
If you don't do this then ember will search for an index template to be rendered inside the outlet, and since you doesn't have one, nothing will be displayed. So again, by defining a path like "/" for your yellow route you instruct ember to use the yellow template instead of the index.
Of course, if you rename your yellow template to index then it will be picked up by ember and rendered inside the application outlet and you don't need to specify a path for the yellow route.
Example jsfiddle.
Hope it helps.
When you create a route like this:
App.Router.map(function() {
this.route('yellow')
});
and a transition is performed to yellow route.
For example, via {{#link-to 'yellow'}}Yellow{{/link-to}} or changing the url tohttp://www.yourhost.com#/yellow`
A handlebar template is appended in the dom, with the same name like the route:
<script type="text/x-handlebars" id="yellow">
<p>Something</p>
</script>
By default ember create a route('index') in your router mapping, like this:
App.Router.map(function() {
this.route('yellow')
// this.route('index', { path: '/' })
});
So creating a index template will work
<script type="text/x-handlebars" id="index">
Welcome
</script>
When the user navigate to the url http://www.yourhost.com, he will see Welcome.
See this demo http://jsfiddle.net/marciojunior/Db49u/