Ember can't find my template when using the renderTemplate hook - ember.js

I can’t figure out why Ember can’t find and render the template specified in my renderTemplate hook override. I have the following route configuration as per router.js:
this.route('posts', function() {
this.route('history', function() {
this.route('new', { path: '/new/:id'});
});
});
I’m also making use of my history index route. Therefore, my history.hbs is just an outlet, and all my templating for this route is actually housed in index.hbs. This way, I can render my new.hbs template on its own page.
In new.hbs, I’d like to render an outlet, in addition to some other content. To handle this, I attempted to override the renderTemplate hook in routes/new.js, like this:
renderTemplate() {
this.render('posts/history/test', {
into: 'new',
outlet: 'test',
});
}
I created the new template under templates/posts/history:test.hbs`. File structure for my templates folder looks like this:
templates > posts > history > new.hbs, test.hbs
Finally, in new.hbs, I added my outlet for this new template:
{{outlet 'test'}}
When I navigate to my new route, I get the following error:
Assertion Failed: You attempted to render into ’new' but it was not found. Anyone know how I can get past this?

I believe you can only render into an ancestor route.

Related

Override application.hbs template in Emberjs

In emberjs, I am in a situation that my application already has routes template that uses the application.hbs template, but now I want to create a new route templates that doesn't use application.hbs.
Is there any easy solution for that?
I have seen many answers but that doesn't match my specification and also my version of ember is 2.11.
Thank you.
Keep application.hbs as minimal and common across all routes as you can. What you shoud do is generate top level routes for your application.
Say you have a common setup where you have an authenticated section, post login, and a login section. It is common for pre and post login to have differing top level templates. Try something like this:
ember g route login
ember g route login/index
ember g route login/forgot-password
ember g route authenticated
ember g route authenticated/index
ember g route authenticated/profile
etc...
Your login.hbs would have its own style, and potentially child routes, which will assume that style and place subsequent nested templates in the {{outlet}} that others have mentioned.
File Structure:
routes/
-login/
----index.hbs
----forgot-password.hbs
-authenticated/
----index.hbs
----profile.hbs
login.hbs
authenticated.hbs
application.hbs
In the example above, login.hbs might look like this:
{{yellow-navbar}}
{{outlet}}
and authenticated.hbs like this:
{{pink-navbar}}
{{user.name}}
{{outlet}}
Now, the login/index.hbs and login/forgot-password.hbs templates will render in the login.hbs outlet. both of these pages will render a yellow navbar, and then their own content.
Because authenticated.hbs is another top level parent route, both authenticated/index.hbs and authenticated/profile.hbs will render their content beneath a pink navbar and a display of the current user's name.
if your application.hbs looked like this:
{{#general-site-container}}
<h2>Website Name</h2>
{{outlet}}
{{/general-site-container}}
Then all of the routes, both authenticated and login, will be in the general-site-container, and will all show the h2 with the website name.
An important note in this, and something that I see a lot of people get confused with, is that this folder structure does not dictate the actual path of the route.
The router might be configured like this, to avoid showing "authenticated" in the url:
Router.js
// login.index route assumed at root url /login
this.route('login', { path: '/login' }, function() {
// avail at /login/forgot-password
this.route('forgot-password', { path: '/forgot-password' }
});
//authenticated.index.hbs assumed at root avail at /
//notice that the authenticated parent route has a route of "/"
this.route('authenticated', { path: '/' }, function() {
// authenticated.profile route avail at /profile
this.route('profile', { path: '/profile' });
// as an exmaple you can further nest content to your desire
// if for instance your profile personal info section has a parent
// template/route
this.route('', { path: '/personal-info' }, function() {
this.route('address', { path: '/address' }); //profile/personal-info/address
});
});
i think you need to use {{outlet}} for achieve this.
create diffrent outlets where you need to show diffrent templates by overriding application template
{{outlet}} //application hbs default one
{{outlet "view1"}} // another template
{{outlet "view2"}} //another template
there should be view1.hbs and view2.hbs in order to render those templates

How show component only in a specific route?

I have a componente called hero (in application.hbs) and I wish display this componente only in home page.
I researched about how do this but without any success. Thanks!
After a few minutes and some searches on GitHub...
Just install ember install ember-truth-helpers and check the route name:
{{#if (eq currentRouteName 'index')}}
{{hero}}
{{/if}}
Glad to help!
I need more specifics, however, I am going to make the assumption that your home route is the '/' route.
The '/' route is actually your index route, so if you create an index.hbs file it will act as the template for your index route. And then your should just move the hero component to your index.hbs file.
I can't be sure your reasons, but I suspect that this could be a solution.
There is an invisible 'application' route... there is also an implicit 'index' route, but you can skip the confusion of that and just create a 'home' route and give it a path to the root. The application template will house the outlet - and then you can place your component just in the 'home' template;
(don't write an application route like this, but just for visualization)
Router.map(function() {
// overarching 'application' route
this.route('application', function() {
this.route('home', { path: '/' });
this.route('other');
});
});
Here is a twiddle with the full example in place. If this doesn't do what you want, then refer to the conditional suggestions. : )
Router.map(function() {
// here's an example of skipping of skipping the mysterious 'index' in another situation
this.route('books', function() {
this.route('books-list', { path: '/' });
this.route('book');
});
});
You can also render a component dynamically using component helper which save you a conditional statement inside your template.
The first parameter of the helper is the name of a component to render, as a string. So {{component 'blog-post'}} is just the same as using {{blog-post}}.
When the parameter passed to {{component}} evaluates to null or undefined, the helper renders nothing. When the parameter changes, the currently rendered component is destroyed and the new component is created and brought in.
So you can safely pass in anything to the component helper, in your case you can make the component name dynamically without worry an error will raised.
https://guides.emberjs.com/v2.1.0/components/defining-a-component/#toc_dynamically-rendering-a-component

Where to run jQuery after ember template renders?

With Views deprecated, I'm having trouble discerning where to run jQuery after a Template has rendered.
I have a simple index Template for a foods resource (templates/foods/index.hbs), and wish to run the following jQuery after index.hbs renders:
this.$('[data-toggle="tooltip"]').tooltip();
I know one can create a Component that then has a didInsertElement hook, but I'm not clear on how I'd route to the Component if I did this. Would I then "double dip" by having my apps/templates/foods/index.hbs Template simply call the new Component, which would have its own template that would be the original index.hbs Template?
I'm sure I'm missing something simple here. Anyone care to point me in the right direction?
You can call jQuery in route:
export default Ember.Route.extend({
actions: {
didTransition() {
Ember.run.next(this, 'initTooltip');
}
},
initTooltip() {
Ember.$('[data-toggle="tooltip"]').tooltip();
}
});
You can also create component and use it inside your template like this:
{{tooltip-wrapper}}
<!-- HTML here -->
{{/tooltip-wrapper}}
And call jQuery code in didInsertElement of tooltip-wrapper component.

how to render two pods content on the same page?

I'm new to ember/ember-cli and am slowly getting my head around the immense learning curve... I have come across an issue I was hoping someone could advise me on...
I have an App that displays a contact and then places tabbed content underneath the contact details, one tab contains some notes info the other some site locations info.
I essentially have a Bootstrap "Tabbed" section to my page. With (currently) two Tabs labelled "Sites" and "Notes". The idea being if you click Notes, you see content from the Notes pod and if you click Sites you see content from the Sites Pod.
To do this i am naming my outlets e.g.
{{outlet 'sites-tab'}}
and
{{outlet 'notes-tab'}}
i.e.
{{#em-tabs selected-idx=tab_idx}}
{{#em-tab-list}}
{{#em-tab}}Sites{{/em-tab}}
{{#em-tab}}Notes{{/em-tab}}
{{#em-tab}}...{{/em-tab}}
{{/em-tab-list}}
{{#em-tab-panel}}
{{outlet 'sites-tab'}}
{{/em-tab-panel}}
{{#em-tab-panel}}
{{outlet 'notes-tab'}}
{{/em-tab-panel}}
{{#em-tab-panel}}
<p>Future Use</p>
{{/em-tab-panel}}
{{/em-tabs}}
and using:
renderTemplate: function() {
this.render({
into: 'contacts.show', // the template to render into
outlet: 'notes-tab' // the name of the outlet in that template
});
}
in the two pods routes to place the content in the right place.
if i use the urls manually e.g:
contacts/5961168002383609856/sites
contacts/5961168002383609856/notes
Then the content is rendered into the relevant Tab (and the other is empty).
each pod structure is along the lines of:
app/pods/notes/-form/template.hbs
app/pods/notes/edit/controller.js
app/pods/notes/edit/route.js
app/pods/notes/edit/template.hbs
app/pods/notes/index/controller.js
app/pods/notes/index/route.js
app/pods/notes/index/template.hbs
app/pods/notes/new/controller.js
app/pods/notes/new/route.js
app/pods/notes/new/template.hbs
app/pods/notes/show/controller.js
app/pods/notes/show/route.js
app/pods/notes/show/template.hbs
app/pods/notes/base-controller.js
app/pods/notes/route.js
can you think of what would make ember-cli render both contents into each outlet on the same page?
my app/router.js contains:
Router.map(function() {
this.resource("contacts", function() {
this.route("new");
this.route("edit", {path: ':contact_id/edit'});
this.route("show", {path: ':contact_id'}, function(){
this.resource("notes", function() {
this.route('new');
this.route('edit', {path: ':note_id/edit'});
});
this.resource("sites", function() {
this.route('new');
this.route('edit', {path: ':site_id/edit'});
});
});
});
});
many thanks with any help you can suggest.. thanks.
EDIT:
OK, as per #Sam Selikoff suggestion I tried switching to components, doing:
ember generate component contact-sites
ember generate component contact-notes
created the files:
app/components/contact-notes.js
app/components/contact-sites.js
and
app/templates/components/contact-notes.hbs
app/templates/components/contact-sites.hbs
I then moved my template html from pods/notes/index/template.hbs into app/templates/components/contact-notes.hbs
This (with a few tweaks) seemed to display the content correctly. I then moved on to editing a Note. TO do this I have a button with an action: {{action "editNote" note}} so had to move my actions from pods/notes/index/route.js into app/components/contact-notes.js
for example:
app/components/contact-notes.js
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
newnote: function(note) {
console.log("NEW NOTE:", note.contact);
this.transitionTo('notes.new');
return false;
},
editNote: function(note) {
console.log("Edit Note:", this);
this._transitionTo('notes.edit', note);
return false;
}
}
});
but I cant seem to get the Edit Note Route to work. I either (using this._transitionTo('notes.edit', note); ) get an error saying:
DEPRECATION: Ember.View#transitionTo has been deprecated, it is for internal use only
or if i use this._transitionTo('notes.edit', note); I get a different error:
TypeError: currentState is undefined
if (currentState.enter) { currentState.enter(this); }
any thoughts on how I can get to a route from within a component? - thanks.
In general you shouldn't need to call render or use named outlets that often. Instead, use components, something like
{{#em-tabs selected-idx=tab_idx}}
{{#em-tab-list}}
{{#em-tab}}Sites{{/em-tab}}
{{#em-tab}}Notes{{/em-tab}}
{{/em-tab-list}}
{{#em-tab-panel}}
{{contact-sites site=contact.sites}}
{{/em-tab-panel}}
{{#em-tab-panel}}
{{contact-notes notes=contact.notes}}
{{/em-tab-panel}}
{{/em-tabs}}
Remember your URL structure is tied to how your interface renders, so if you want two things to show simultaneously, don't tie them to two distinct URLs.

Ember - specify controller for named outlet

I have a template called new, which has some input helpers to submit a new Request (subject and body). After them I have a named outlet tags, which should display a list of possible services we can add to the request (tags: fix, purchase, etc).
The problem is that when I navigate to new, only static data is displayed from the tags template ("inside tags template" would be displayed), but the #each does not loop at all.
If I add tags as a new resource into new, and navigate to new/tags, then the tags template would be rendered into both outlets of the new template ({{outlet}} and {{outlet tags}}, so my code is not faulty when it comes to displaying data, its just faulty when it comes to displaying it where and when I want to (only inside the new route).
Also, both my routes' models have a console.log message saying which route is accessed, and when I go to new only the new route displays a message.
I believe new does not know that it is supposed to use the tags controller, but I am clueless when it comes to Ember... (I do not want to get the tags via the new route, I want to use the tag route)
export default Ember.Route.extend({
model: function(){
console.log("in new");
},
setupController : function(controller, model){
controller.set("model", model);
},
renderTemplate: function() {
this.render();
this.render('tags', {
outlet: 'tagO',
into: "new",
controller: 'tags'
});
}
});
It's easier to just type {{render 'tags' someModel}} from the template than to programmatically do it in the renderTemplate hook and named outlet. You need to make the someModel available on the controller that you are currently in.
You'll want to hook up multiple models on the controller, see: EmberJS: How to load multiple models on the same route?
Example: http://emberjs.jsbin.com/OxIDiVU/1051/edit