Can someone better explain with is going on with the implied index route and controllers in Ember JS?
See this example, why is the behavior different in these two examples?
Index route explicitly defined
http://jsbin.com/ILAP/1/
The index route is implied
http://jsbin.com/ILAP/2/
What is confusing to me is why the nesting behavior works in the second example but not the first.
This is the students/student route structure:
students
----index
----student
--------index
First case
Index route explicitly defined
Templates:
<script type="text/x-handlebars" data-template-name="students">
{{ outlet }}
</script>
<script type="text/x-handlebars" data-template-name="students/index">
... omitted ...
<div class="well">
Expecting to render student template here:
<br />
{{ outlet }}
</div>
</script>
<script type="text/x-handlebars" data-template-name="student">
<h2>Student</h2>
<h3>{{name}}</h3>
<h4>{{grade}}</h4>
<h4>{{gpa}}</h4>
</script>
When you make a transition to student.index, first is appended the student template and after student/index. Because you dont have override the default conventions, via renderTemplate. The place where the template is rendered, is into the main outlet (an outlet without name) aka {{outlet}}, of the parent route template. So student template will be inserted in the students main outlet. Not students/index, because it's the sibling. This is the reason why all your content is replaced. And the student/index will be inserted in student
Second case
The index route is implied
Templates:
<script type="text/x-handlebars" data-template-name="students">
... omitted ...
<div class="well">
Expecting to render student template here:
<br />
{{ outlet }}
</div>
</script>
<script type="text/x-handlebars" data-template-name="student">
<h2>Student</h2>
<h3>{{name}}</h3>
<h4>{{grade}}</h4>
<h4>{{gpa}}</h4>
</script>
This time, like the previous sample, the resolution of the templates don't change. But this time we don't have the student/index. So first is rendered the student in students outlet, after because the student/index is missing, it's is ignored. The final result is the template where you expect.
Summary
Using the default conventions. Templates will be rendered in the parent, not sibling, parent route template.
Related
I have a nested route that looks like:
/products
/products/:productId
And the /products template renders with a sidebar listing all the products, and a "main" content <div> in the center that is populated with a specific product when clicked. ie:
<div id="productsSidebar">
... navigation tree ...
</div>
<div id="mainContainer">
{{outlet}}
</div>
However, I would like for the {{outlet}} to be replaced with a message like
Please select a product to the left.
When there is no child route, ie. we're just looking at /products and the main <div> is otherwise completely empty.
I haven't found any reference/tutorial demonstrating if this is possible - is there a standard way to render a "default" block of HTML if there no outlet available?
You can use the products index route template to display that html text. You may need to generate the route with something like ember g route products/index
Iam getting started with MeteorJS/Iron Router/Blaze and I'm trying to figure out how to set placeholder for template.
I have a menu and a footer, which will change only partialy, but between them is the main content which should be based on current route. Point is that i need some sort of pointer, like in angular "ng-view".
Eg.
<menu></menu>
{{some sort of placeholder}}
<footer></footer>
and in router:
Router.route('/', function () {
//render some template exactly into placeholder, dont append it to the bottom
});
Maybe there is something wrong with my understanding of meteorjs templating, I just base my way of solving this problem on angular templating.
not sure I got your question but here is an answer:
You can do that with the {{> yield}} tag. It will define an dynamic area which will show the view corresponding to the current route.
You can also "design" a layout for your entire app, like you did, for example layout.html :
<template name="layout">
<div class="container">
<header class="navbar">
<div class="navbar-inner">
<a class="brand" href="{{pathFor 'home'}}">My app</a>
</div>
</header>
<div id="main" class="row-fluid">
{{> yield}}
</div>
</div>
</template>
And tell Iron-Router that your app has to fit this layout :
Router.configure({
layoutTemplate: 'layout'
});
// Then just define your routes
Router.map(function() {
this.route('home', {path: '/'});
});
You may notice I used {{pathFor 'home'}} in the layout. Iron Router let you use route "names" so it won't do anything if you change the URL.
I suggest you read an awesome book called Discover Meteor !
So the concept is simple: You come to the app, you have a default template, with a default navigation element. You click a link in that navigation element it renders a new template: #/rails.
From here the default navigation needs to be hidden, and your new nav needs to be rendered.
The way i attempted to approach this, seems a bit silly: what I did was,
SG.Router.map(function(){
this.resource('rails');
});
$(document).ready(function() {
if($('#rails-nav').length !== 0){
$('#main-nav').hide();
}
});
Now the issue with this is that if you go from the default application template, to the rails template via the link - you get two navs unless you refresh that page. My friend stated that I should use something like:
{{outlet nav}} and then render a navigation based on template. The issue is I don't know how to set this up and I have been looking all over the ember site.
Could some one help me out?
If i understood correctly, when you say default template you mean the application template, where all other templates are rendered within its {{outlet}} helper.
There are several approaches to achieve what you want, but a simple one i think would be to use the index template as your default template. In this case evertyhting will be much simpler and work as you require, since you can specify whether the navigation element is shown by placing it inside a template or not.
http://emberjs.jsbin.com/sume/1/edit
hbs
<script type="text/x-handlebars">
<h2> Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
This is the default template using <i>index</i>
<br/>
<br/>
<span style="background-color:grey">this is the nav part {{#link-to 'rails'}}go to rails{{/link-to}}</span>
</script>
<script type="text/x-handlebars" data-template-name="rails">
this is the rails app
</script>
js
App = Ember.Application.create();
App.Router.map(function() {
this.resource("rails");
});
Also to make this answer a bit more relevant to the title of the question, in order to do something when a template has rendered one way would be to use the didInsertElement callback of the View class.
App.RailsView = Ember.View.extend({
didInsertElement:function(){
if($('#rails-nav').length !== 0){
$('#main-nav').hide();
}
}
});
http://emberjs.jsbin.com/dido/1/edit
I'm using the following Ember.js stack:
DEBUG: -------------------------------
DEBUG: Ember : 1.1.2
DEBUG: Handlebars : 1.0.0
DEBUG: jQuery : 2.0.2
DEBUG: -------------------------------
I declared an application.hbs Handlebars template which renders my App.ApplicationView
{{#view App.ApplicationView}}
{{outlet 'modal'}} {{outlet 'notificationCollection'}}
{{#if isUserAuthenticated}}
{{render sidemenu authenticationState=isAuthenticated}}
{{outlet 'upperNotification'}}
<div id="main" class="main">
{{outlet}}
</div>
{{else}}
{{outlet}}
{{/if}}
{{/view}}
and for debugging purposes I added the gotcha to the classNames array of App.ApplicationView. When I now inspect the DOM, I can see that App.ApplicationView is rendered twice (and also boxed):
...
<div id="appRoot" class="ember-application">
<div id="ember444" class="ember-view gotcha">
<div id="ember445" class="ember-view gotcha">
...more content..
</div>
</div>
</div>
...
EDIT:
I should have tried it first, before posting it here at stackoverflow... if I remove the {{#view App.ApplicationView}}{{/view}} declaration, it get's only rendered once, as expected ;)
So, the question which remains a miracle to me is is Ember.js really generating an enclosing <div id="ember666" class="ember-view"></div> for every {{outlet}}??
Why would Ember.js handle it this way? How can I prevent it from rendering App.ApplicationView twice? And, besides that, is Ember.js really generating an enclosing <div id="ember666" class="ember-view"></div> for every {{outlet}}? (I'm seeing this behaviour within my application)
By default, yes {{outlet}} will enclose your template in a <div>. However, you can change that behavior by specifying a view class on the outlet and defining the view class' tagName property to whatever you want.
For example, the outlet
{{outlet viewClass=App.HeaderContainer}}
With view
App.HeaderContainer = Ember.ContainerView.extend({
tagName: 'header'
});
will wrap the outlet in a <header> tag.
I've been trying to learn Ember.js for the last two weeks and I've really struggled. I'm hoping for an 'a-ha' moment but each new feature I try to implement always results in hours of failed testing. I just don't seem to be grasping the framework. I feel like I'm working against it. I'm hoping someone can explain a path forward through this simple example.
I am creating a web app that allows the user to pick products that they will sell to a client. There is a list of products they can chose from and then a list of products they've selected.
I imagine a left-column with navigation controls and a main column showing either the selected products or new products they can add to the order. Here is the basic template:
<script type="text/x-handlebars" data-template-name="pc">
<div id="nav">{{outlet nav}}</div>
<div id="main">{{outlet main}}</div>
</script>
Here is the left navigation template:
<script type="text/x-handlebars" data-template-name="nav">
<div class="button">{{#linkTo "pc.add"}}Add Products{{/linkTo}}</div>
</script>
Here is the selected products template:
<script type="text/x-handlebars" data-template-name="selectedProducts">
{{#each p in controller}}
<div class="product">
<h4>{{p.name}}</h4>
</div>
{{/each}}
</script>
Here is the available products template:
<script type="text/x-handlebars" data-template-name="addProducts">
<div id="addProducts" class="addProducts">
{{#each p in controller}}
<div class="product">
<h4>{{p.name}}</h4>
</div>
{{/each}}
<button {{action "addSelectedProducts"}}>Add Selected Products</button>
<button {{action "back"}}>Back</button>
</div>
</script>
I can load the 'pc' template with some already selected products. Great. I can also navigate to the 'Add Products' template. Great. But when I click 'Add Selected Products', I can't figure out how to move the selected products into the controller/model behind the 'Selected Products' template and then get that template to re-render in place of the 'Add Products template'. It's really two question. How do I update the model of another controller from within a different controller? And, how do I then transition from an event to another route?
Can someone show me how you would design the Route(s) and Controllers? I know that's asking a lot. I"m mostly interested in seeing how you respond to an event in the AppProductsController, update SelectedProductsController's model, and then transition to SelectedProductsRoute and have it re-render the template.
I want to believe this is an amazing framework but I just keep hitting walls.
Andrew
How do I update the model of another controller from within a different controller?
Connect controllers using the needs property. So something like:
//in AddProductsController
needs: ['selectedProducts']
addSelectedProducts: function() {
// Now selectedProductsController can be accessed via the controllers property
otherController = this.get('controllers.selectedProducts');
// add the selected ones...
}
See http://emberjs.com/guides/controllers/dependencies-between-controllers/
how do I then transition from an event to another route?
//in AddProductsController
this.transitionToRoute('blogPosts');
See http://emberjs.com/api/classes/Ember.Controller.html#method_transitionToRoute