How to have nested routes without nested templates - javascript

I have ember routes set up like so:
App.Router.map(function() {
this.resource("subreddit", { path: "/r/:subreddit_id" }, function() {
this.resource('link', { path: '/:link_id'} );
});
});
But I want the to view each link in a completely separate template. In other words, I want to render a different block of html, rather than render the link html into subreddit's {{outlet}}.

When you hit /r/xxx in the browser Ember will look for two templates related to subreddit. First it looks for 'subreddit' and then it looks for 'subreddit/index'. If 'subreddit' if found, then it is rendered and then if 'subreddit/index' is also found, it is rendered into the {{outlet}} of 'subreddit'. If 'subreddit' is not found Ember will just move on to 'subreddit/index'. The 'subreddit' template is also rendered for any sub-paths that are nested under /subreddit, such as the 'link' template for /r/xxx/yyy. 'subreddit' is kind of like a "per model layout" template that allows you to wrap (decorate) all of the sub-path templates.
Something like this should let you keep your nested routes, and allow you to use Ember default behavior.
<script type="text/x-handlebars" data-template-name="subreddit">
<p>A static header bar for this subreddit could go here, or not.</p>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="subreddit/index">
<p>
All of the details of the subreddit go here, this will be rendered
into the {{outlet}} of "subreddit".
</p>
</script>
<script type="text/x-handlebars" data-template-name="link">
<p>
Link view goes here. This template will replace "subreddit/index"
in the {{outlet}} of "subreddit".
</p>
</script>

If you want to render the links in separate templates you should not nest your routes:
App.Router.map(function() {
this.resource("subreddit", { path: "/r/:subreddit_id" });
this.resource('link', { path: '/:link_id'});
});
A different approach you can take could be to use named outlet's and override renderTemplate of the corresponding route and render the template in the place of the named outlet's, have a look here for more info on that: http://emberjs.com/guides/routing/rendering-a-template/
Hope it helps.

Related

Using if to have different elements on different pages in Meteor.js

I have a certain div class (landing), that I only want to have appear on certain routes. I am using a "main" template that has my header and footer (with a >yield included between for each pages unique code). However, there is an element in the header that I only want to appear on the landing page ('/' route).
I am using Iron Router.
Is there a way to do this easily with an #if in meteor? Thanks!
Here's a generic route name equality testing helper:
Template.registerHelper('routeEquals',function(name){
return Router.current().route.getName() === name;
});
Then in any template you can do (for example):
{{#if routeEquals '/'}}
in the / route
{{else}}
Not in the / route
{{/if}}
You want to create a helper on your header template that reactively checks the current route. I haven't used Iron Router in a while, but believe Router.current().route.getName() is reactive. Try the following:
Template.header.helpers({
onLanding() {
return Router.current().route.getName() === '<landingRouteName>';
},
});
<template name="header">
{{#if onLanding}}
<div>I should only show on the landing page!</div>
{{/if}}
</template>
Or, alternatively (and maybe optimally - it would keep the template loaded, rather than injecting it into the DOM when you hit landing), you can use the helper to apply a class like
<div class="{{#if onLanding}}isVisible{{/if}}>...</div>
You can put a session flag in the onBeforeAction of the landing route and then, on the header, check if that session is valid and show the div
Something like this
this.route("landing", {
path: "/landing",
onBeforeAction: function () {
Session.set('showDiv', true);
}
});
<template name='main'>
<header>{{#if helper_getShowDivFlag}}<div></div>{{/if}}</header>
</template>
helper_getShowDivFlag: function() {
return Session.get('showDiv');
}
Alternatively, checking the router name in the main template and show/hide the div
<template name='main'>
<header>{{#if helper_showDivFlag}}<div></div>{{/if}}</header>
</template>
helper_showDivFlag: function() {
return Router.current().route.getName() == 'landing'
}
Or if you don't want to write code, use this package and check the router :D
<template name='main'>
<header>{{#if isActiveRoute 'landing' }}<div></div>{{/if}}</header>
</template>
You can accomplish this using the data property of your route.
Router.route('/', {
name: 'home',
data: function() {
return {
showMySpecialHeaderWidget: true
};
}
});
Then you can access this data property in your template.
<template name="main">
<header>
{{#if showMySpecialHeaderWidget}}
<div>My special header widget</div>
{{/if}}
</header>
{{> yield}}
</template>
Using this method, you don't need to worry about updating your template if you ever change the names of your routes, and it's easy to show your widget on multiple routes by including this data property.

Override Ember application.hbs template

I have an Ember JS application with 99% of it's resources on the root level, which the user has to be logged in to access. All those resources, with the exception of /login, have the same template.
/login, however, has an entirely different template (no nav bar, etc). What's the best way to override the application.hbs template for the login page?
Most of the answers I see in other questions involve a sort of parent route for the authenticated routes, like so:
this.route('login');
this.route('main', { path: '/' }, function() {
this.route('posts');
});
That solution yields the right URL's, but it means link-to helpers and such have to use 'main.posts', etc routes, which rubs me the wrong way, and I'd like not to have that extraneous route level all over the place.
In Rails, I'd just say something like layout: nil, and and that would prevent the main template from being applied. Is there any equivalent in Ember, or another way to achieve the same goal cleanly?
To get rid of {{link-to 'main.posts'}}. You can add {resetNamespace:true} to a given route.
this.route('main', {path:'/'}, () => {
this.route('posts', {resetNamespace:true});
});
{{link-to 'posts'}} will navigate to your posts route
I got another solution
{{!-- application.hbs --}}
{{#if session.isAuthenticated}}
{{outlet}}
{{else}}
Then the welcome/login page goes here
{{/if}}
Inspired by samselikoff in this post
======EDIT======
Maybe we can even do this
//in app/routes/login.js
export default Ember.Route.extend({
renderTemplate: function() {
this.render({ outlet: 'un-auth' });
}
});
and
{{!-- application.hbs --}}
{{#if session.isAuthenticated}}
{{outlet}}
{{else}}
{{outlet "un-auth"}}
{{/if}}

Meteor iron-router not inserting template into the correct layout

I have set up a route
this.route('force', {
path: '/force',
template: 'showForce',
layoutTemplate: 'mainpanel'
});
The showForce template is very simple
<template name="showForce">
<p>show force</p>
</template>
as is the mainPanel template
<template name="mainpanel">
{{ yield }}
</template>
but that template has a parent like this
<template name="page_logged_in">
{{> header}}
<div id="side-panel">
{{> sidepanel}}
</div>
<div id="main-panel">
{{> mainpanel}}
</div>
</template>
The route does render the template to the page but not in the yield position. It displays as a DOM sibling to the page_logged_in template.
Any ideas why this is happening?
Thanks
Iron Router is a bit tricky to get used to.. You should be using the named yields feature and letting Iron Router put the templates in instead of trying to insert the with {{> templateName}}. I created a demo repo based on your code to show how to achieve the results you desire and it's located here: https://github.com/copleykj/ConnorAtherton
Don't forget to do your router configuration too
Router.configure({
layoutTemplate: 'mainpanel'
});
Your route should also be something like this
this.route('showForce', {
path: '/force',
layoutTemplate: 'mainpanel'
});
The 'template' should be the first param. Without the layoutTemplate configured I think iron router uses the body as the yield which is why you might be seeing the dom displaced.

display number of items in ember.js

I need a very easy thing and looking on the web the solutions founded tell me that my code is right. But clearly isn't.
I just need to display how many notes(models) I have in my app:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> Notes and bookmarks </title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<script type="text/x-handlebars" data-template-name="index">
<div class="wrap">
<div class="bar">
<input type="text" class="search" />
<div class="bar-buttons">
<button> NEW </button>
<button> HOME </button>
</div>
</div>
<aside>
<h4 class="all-notes">All Notes {{all}}</h4>
{{#each item in model}}
<li>
{{#link-to 'note' item}} {{item.title}} {{/link-to}}
</li>
{{/each}}
</aside>
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="main">
<section>
<h2>Hellooo</h2>
</section>
</script>
<script type="text/x-handlebars" data-template-name="note">
<section>
<div class="note">
{{#if isEditing}}
<h2 class="note-title input-title"> {{edit-input-note value=title focus-out="modified" insert-newline="modified"}} </h2>
<p class="input-body"> {{edit-area-note value=body focus-out="modified" insert-newline="modified"}} </p>
{{edit-input-note value=url focus-out="modified" insert-newline="modified"}}
{{else}}
<h2 {{action "editNote" on="doubleClick"}} class="note-title" > {{title}} </h2>
<button {{action "removeNote"}} class="delete"> Delete </button>
<p {{action "editNote" on="doubleClick"}}> {{body}} </p>
{{input type="text" placeholder="URL:" class="input" value=url }}
{{/if}}
</div>
</section>
</script>
<script src="js/libs/jquery-1.9.1.js"></script>
<script src="js/libs/handlebars-1.0.0.js"></script>
<script src="js/libs/ember-1.1.2.js"></script>
<script src="js/libs/ember-data.js"></script>
<script src="js/app.js"></script>
<script src="js/router.js"></script>
<script src="js/models/note_model.js"></script>
<script src="js/controllers/note_controller.js"></script>
<script src="js/controllers/notes_controller.js"></script>
<script src="js/views/note_view.js"></script>
</body>
</html>
My model:
Notes.Note = DS.Model.extend ({
title: DS.attr('string'),
body: DS.attr('string'),
url: DS.attr('string')
});
Notes.Note.FIXTURES = [
{
id: 1,
title: 'hello world',
body: 'ciao ciao ciao ciao',
url: ''
},
{
id: 2,
title: 'javascript frameworks',
body: 'Backbone.js, Ember.js, Knockout.js',
url: '...'
},
{
id: 3,
title: 'Find a job in Berlin',
body: 'Monster, beralinstartupjobs.com',
url: '...'
}
]
And my notes controller:
Notes.NotesController = Ember.ArrayController.extend ({
all: function () {
var notes = this.get('model');
return notes.get('lenght');
}.property('model')
});
I think that's all the important code for make this work but if you need others part I will add.
Why i can't see the number of my notes in {{all}} ?
In your notes.hbs template you should be able to do {{length}}.
I made a few adjustments to the code that you provided and got it to work the way that i think you expected it to work. I am going to try to explain where I think you may have been sent for a loop as it relates to your App. Ember takes some getting use to for sure.
One thing that is important is that Ember uses naming conventions to help you wire up and associate your Router, Routes, Controllers, Templates correctly with each other and also help you to look at code and know what Ember expects. Ember also gives you free stuff, yaaay for free stuff.
So when Ember boots up you get some default assets for free the ApplicationRoute, ApplicationController, and the application template these are always present in Ember even if you never explicitly define them. However if you need to use them just define them and add whatever code. In addition to those you get an IndexRoute, IndexController and index template which are present at the top of Ember like those other assets.
Remember the application template, which is at the highest level of the app, think of it as the parent of all templates. Other templates will be put into it and rendered, including the index template that you got for free. Now this is where things get a little tricky.
If you define a router like this in Ember for example. You still have that index template which you can use as you were and you also now have a template called note.
App.Router.map(function() {
this.resource('note');
});
Can you guess the names for the Controller and Route associated with the note resource using Ember naming conventions? I'll leave that for homework. Now when Ember boots with this Router if you defined a index template (just like you were) it will automatically be pushed into the application templates {{outlet}}and rendered. A note on the application template if all its doing is basically being and 'outlet' to render stuff Ember will do that just fine by default.
Behind the scenes the default application template may look something like this. I just put the <script> tags to highlight its the application template.
<script type="text/x-handlebars">
{{outlet}}
</script>
The template without a data-template-name is used as the application template. You can put in data-template-name="application" if you like but its not necessary unless you are using build tools. Now back to the Router stuff.
If you define a Router like this something happens that is important to realize.
App.Router.map(function() {
this.resource('note', { path: '/'});
});
By adding {path: '/'} in that resource you are overriding the /, which is the based url of you application. Now '/' is not associated with index template that you got for free its the note template. When Ember boots it will automatically push the note template into the application outlet.
Moving along, in your code you had few other things that were also conflicting. Remember with naming conventions if ask Notes.NotesController for something Ember by default will look for a Notes.NotesRoute and a notes template, attention to the pluralization, but in your code your code you have a note template.
The other conflict is with the index template, again with the naming conventions Ember is going to look for the IndexController for the {{all}} property and IndexRoute to the supply the model. In your code its on the Notes.NotesController.
And finally dont forget if you are using and adapter to define it:
Notes.ApplicationAdapter = DS.FixtureAdapter.extend({});
Sorry for the long answer but i hope it clear up things
Here the jsBin.
http://emberjs.jsbin.com/UPaYIwO/7/edit
Happy Coding!!
PS. You could have put {{model.length}} in the template and it would have accomplished the same thing. But in Ember there is a few ways to do the same thing.
There is also a typo:
return notes.get('lenght');
Lenght should be length;
return notes.get('length');

Ember.js - build a link dynamically using input values

Using ember.js I have an input:
{{input id="my_input" name="my_input" type="text"}}
Then I want to create a link, using linkTo, but want the value of the input to be part of the href. Like this:
{{#linkTo 'my_resource' my_input}}the link{{/linkTo}}
I have the resource defined like this:
App.Router.map(function() {
this.resource("my_resource", {path: ":my_input"});
});
Is that possible?
Thanks.
Definitely:
http://emberjs.jsbin.com/eFAGixo/1/edit
App.Router.map(function() {
this.resource('search', {path:'search/:search_text'});
});
App.SearchRoute = Ember.Route.extend({
model: function(params){
// at this point params.search_text will have
// what you searched for
// since you are sending a string, it will hit the model
// hook, had you sent an object it would have skipped this
// hook and just used the object as your model
return { searchInThisRoute: params.search_text};
}
});
<script type="text/x-handlebars" data-template-name="application">
<h2>Welcome to Ember.js</h2>
{{input value=searchText}}
{{#link-to 'search' searchText}} Go To Search{{/link-to}}
{{outlet}}
</script>
PS: This actually seems like it would make more sense as a button and using Ember Actions, but this works just as dandy.

Categories