I have created template and controller both called navbar. The code that i have in controller is simply
import Ember from 'ember';
export default Ember.Controller.extend({
isLogged: true,
});
and that in template is
{{#if isLogged}}
{{#link-to 'login' class="uk-button uk-button-danger"}}Login{{/link-to}}
{{#link-to 'signup' class="uk-button uk-button-danger"}}Join now{{/link-to}}
{{else}}
{{#link-to 'dashboard' class="uk-button uk-button-danger"}}Dashboard{{/link-to}}
{{/if}}
<button class="uk-button uk-button-large uk-button-primary uk-width-1-1" disabled={{isLogged}}>Test button</button>
The same does not seem to be working. Am i going wrong somewhere ?
The template and controller were generated using ember generator itself and the code above is the only modifications that i made.
EDIT:
Exploring the documentation, i noticed that the name of controller should be same as defined in route. Now navbar is only a template which i import using partial is there any workaround i might use for the same ?
You should use a component for this.
Use {{partial}} only if you want to render a template in the current context. Use it only if your .hbs files became to big, but use it rarely. Often a component is the better choice.
Also there you should almost never use {{render}}. You can almost always refactor it to a component.
Also using {{render}} with a model param is deprecated.
The problem was that 'partial' will only provide the template not the controllers. The correct way for this would be to use render which will import views and controllers also.
Related
I try to create a custom component for my application with emberJS, I have followed the quickstart tutoriel and now I try to create a upload button as a component.
It seems I don't have code issues but my component don't render on my main page. I have use the ember component generator to create it
here my upload-button.hbs :
<button type="button">{{#buttonText}}</button>
now my upload-button.js :
import Component from '#ember/component';
export default Component.extend({
actions: {
showExplorer(){
alert()
}
}
});
for the moment I simply put a alert() method in showExplorer()
and now my main page, application.hbs :
<upload-button #buttonText="Uploader un fichier" {{action "showExplorer"}}/>
{{outlet}}
I expect to see my button but I just have a blank page with no code error.
I suspect that's a really stupid mistake but I can't find it.
Glad you decided to try out Ember.js :) Your upload-button.hbs and upload-button.js file looks good. However, there are a couple of issues here.
The component, when invoked using the angle-bracket syntax (<... />), the name should be CamelCased to distinguish between HTML tags and Ember components. Hence, we need to invoke the upload-button component as <UploadButton />.
You defined your action, showExplorer, inside the component (upload-button.js) file, but, referenced in the application.hbs file. The data in the backing component file can only be accessed inside the component's .hbs file because of the isolated nature of the component architecture. Also, we can only attach an action using {{action}} modifier to a DOM element and not to the component itself. So,
we need to remove the action binding from application.hbs file,
{{!-- application.hbs --}}
<UploadButton #buttonText="Uploader un fichier"/>
and add the same inside the upload-button.hbs file:
{{!-- upload-button.hbs --}}
<button type="button" {{action "showExplorer"}}>{{#buttonText}}</button>
A working model can be found in this CodeSandbox
Hope you find learning Ember, a fun process!
I have this code in my index.hbs file:
<button {{action 'showHotspots'}} class="btn btn-success padding">{{hotspotCategory.name}}</button>
I have the showHotspots method in my routes/index.js file:
showHotspots: function( selection ) {
this.toggleProperty( 'idFromButton' );
}
The method should toggle this part in my routes/index.js:
{{#if idFromButton}}
<p>Test1</p>
{{/if}}
However this is not working, when I do it as a component it works but I need the Test1 on a specific place on my page (below all the generated buttons). When I do it in components the Test1 gets displayed between the buttons.
Instead of working with a component I want to do this in my index.js file so I can control the structure of the page.
In short: on my index.hbs i have a if-statement that checks if a variable is true. I want to set this variable from my routes/index.js file (or from somewhere else, doesn't matter really) using a method that is called in either the same index.hbs file or from a component-1.hbs file.
if i understand the question , you using component, you should use method "showHotspots" on the actions of your component, if not you should use this method on the actions of your controller.
it's better to describe more about your question.
edit: if you using old version of ember js, this is an example
//#### controller index.js
App.IndexController = Ember.Controller.extend({
idFromButton: false,
actions: {
showHotspots: function( selection ) {
this.toggleProperty( 'idFromButton' );
}
}
});
//#### index.hbs (your index controller template)
<button {{action 'showHotspots'}} class="btn btn-success padding">some text</button>
{{#if idFromButton}}
<p>Test1</p>
{{/if}}
The (simple) solution was:
this.controller.toggleProperty( 'idFromButton' );
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}}
I have a component that is wrapping content defined by another template. I want an action on the template to trigger a method in my surrounding component. Is this possible?
Here is what I have for my template. Note this is shortened for brevity.
{{#drop-down}}
<div class="menu-selector clickable" {{action "toggleDropdown"}}>
</div>
{{/drop-down}}
This is my component:
DropDownComponent = Ember.Component.extend
showDropdown: false
actions:
toggleDropdown: ->
#toggleProperty 'showDropdown'
`export default DropDownComponent`
I can verify that everything else in my component is working. If I put the action in my component that loads this template, it works fine. But that's not where I want it.
So you would like to send an action to particular components. Take a notice that
A component is a custom HTML tag whose behavior you implement using JavaScript and whose appearance you describe using Handlebars templates. They allow you to create reusable controls that can simplify your application's templates.
and
An Ember.Component is a view that is completely isolated.
You are probably using wrong tool here. You should use instead custom view.
App.DropdownView = Ember.View.extend
showDropdown: false
elemendId: 'dropdown'
actions:
toggleDropdown: ->
#toggleProperty 'showDropdown'
return;
{{#view 'dropdown'}}
<div>
<div class="menu-selector clickable" {{action "toggleDropdown"}}>
</div>
{{/view}}
Then you can send an action to view by
Ember.View.views.dropdown.send('toggleDropdown');
Demo: http://jsbin.com/zoqiluluco/1/
I need to combine linkTo and action helpers in Ember.js. My code is:
{{#link-to 'index'}}<span {{action 'clear'}}>Clear</span>{{/link-to}}
But I would like to make this something like this:
{{#link-to 'index' {{action 'clear'}} }}Clear{{/link-to}}
And also:
<li>
{{#link-to 'support'}}
<span {{action 'myAction' 'support'}}>Support</span>
{{/link-to}}
</li>
To:
<li>
{{#link-to 'support' {{action 'myAction' 'support'}} }}Support{{/link-to}}
</li>
How can I achieve this?
Solution
Check my answer for Ember 2.0 compatible, OK for SEO solution.
Ember Link Action addon
This is OK for SEO solution!
Install addon
ember install ember-link-action
Usage
You can pass closure action as invokeAction param to {{link-to}} component:
{{#link-to 'other-route' invokeAction=(action 'testAction')}}
Link to another route
{{/link-to}}
To pass parameters to action you can use:
{{#link-to 'other-route' invokeAction=(action 'testAction' param1 param2)}}
Link to another route
{{/link-to}}
Compatibility
Automated test suite confirms that addon works with 1.13 up to latest Ember 3 releases.
It works with a release, beta and canary versions of Ember.
Addon GitHub repository. Contributions are welcome.
Update: See Michael Lang's comment below for Ember 1.8.1+
The problem with Myslik's answer (not using link-to at all but instead using an action and then transitionToRoute) is that it's useless for SEO, search engine bots will see nothing.
If you want what your link is pointing to to be indexed, it's easiest to have a good old <a href=x> in there. It's best to use link-to so that your link URLs are kept in sync with your route URLs. The solution I use gives both an action to do the work and a handy link-to to index the pages.
I override some functionality of Ember.LinkView:
Ember.LinkView.reopen({
action: null,
_invoke: function(event){
var action = this.get('action');
if(action) {
// There was an action specified (in handlebars) so take custom action
event.preventDefault(); // prevent the browser from following the link as normal
if (this.bubbles === false) { event.stopPropagation(); }
// trigger the action on the controller
this.get('controller').send(action, this.get('actionParam'));
return false;
}
// no action to take, handle the link-to normally
return this._super(event);
}
});
Then I can specify which action to take and what to pass the action in Handlebars:
<span {{action 'view' this}}>
{{#link-to 'post' action='view' actionParam=this}}
Post Title: {{title}}
{{/link-to}}
</span>
In the controller:
App.PostsIndexController = Ember.ArrayController.extend({
actions: {
view: function(post){
this.transitionToRoute('post', post);
}
}
}
This way, when I cache a rendered copy of the page and serve that to an indexing bot, the bot will see a real link with an URL and follow it.
(note also that transitionTo is now deprecated in favour of transitionToRoute)
None of these combinations will work in Ember.js, but you do not need to combine these two helpers. Why don't you just use action helper and let it bubble to controller or route? There you can use transitionToRoute in controller or transitionTo in route.
For example in controller you could have code like this:
App.PostsController = Ember.ArrayController.extend({
clear: function () {
// implement your action here
this.transitionToRoute('index');
}
});
This works fine in 1.6.0-beta.5:
<span {{action "someAction"}}>
{{#link-to "some.route"}}
Click Me
{{/link-to}}
</span>
The link will happen and then the click will bubble up to the action handler. It's documented (albeit indirectly) here.
Edit: corrected syntax in opening link tag
I like Cereal Killer's approach for its simplicity, but unfortunately it exhibits a problem for me. When the browser navigates to another route, it restarts the Ember application.
As of Ember 2.6, the following simple approach does the trick:
<span {{action 'closeNavigationMenu'}}>
{{#link-to 'home' preventDefault=false}}
Go Home
{{/link-to}}
</span>
This achieves the following:
navigates to route 'home'
action 'closeNavigationMenu' is invoked
on mouseover, browser displays link that will be followed (for SEO and better UX)
browser navigation does not result in reboot of Ember app
Having the same problem, i found this simple solution:
{{#linkTo eng.rent class="external-button"}}<div class="internal-button" {{action "updateLangPath"}} >X</div>{{/linkTo}}
then, managing the css classes external-button and internal-button in the stylesheet, i made sure that the "internal-button" was covering the whole "external-button" area; in this way it is not possible to click on the external-button without clicking on the internal-button.
It works well for me; hope it can help...
This is how I solved this in our demo application for the O'Reilly Ember.js book: https://github.com/emberjsbook.
You can see the complete source here: https://github.com/emberjsbook/rocknrollcall
In the view:
{{#if artistsIsChecked}}
{{#if artists.length}}
<h3>Artists</h3>
<ul class="search-results artists">
{{#each artists}}
<li><a {{action 'viewedArtist' this.enid }}>{{name}}</a></li>
{{/each}}
</ul>
{{/if}}
{{/if}}
And the controller:
App.SearchResultsController = Em.ObjectController.extend({
actions: {
viewedArtist: function(enid) {
this.transitionToRoute('artist', enid);
},
viewedSong: function(sid) {
this.transitionToRoute('song', sid);
}
},
needs: ['artists', 'songs'],
artistsIsChecked: true,
songsIsChecked: true,
artists: [],
songs: []
});
This is what it would look like after Ember 3.11.0 with the on modifier.
<LinkTo
{{on "click" this.recordMetrics}}
#route="post.see-all"
#model={{#model}}
#bubbles={{false}}>
Link Me
</LinkTo>
bubbles shown just to illustrate the LinkTo API.
https://blog.emberjs.com/ember-3-11-released/
Ember's link-to tags use routes to open new views, so you can perform whatever functionality you wanted to put in the link's 'action' attribute in the setupController method of the target route instead of in a controller action.
See here in the Ember guide:
http://emberjs.com/guides/routing/setting-up-a-controller/
This only works if you want to perform the action every time the route is accessed, however, as opposed to with specific links.
Make sure to include the controller.set('model', model); line along with whatever else you put in there.