Ember: handle multiple actions in a tag for a component? - ember.js

I have a component in which I would like to handle the 'mouseEnter' and 'mouseLeave' events using action on the same tag.
Is there a way to do this, other than changing my component to a view?

This will work fine:
App.MouseTrackingComponent = Ember.Component.extend({
classNames: ['mouseTracker'],
mouseEnter: function(){
console.log("Entered")
},
mouseLeave: function(){
console.log("Left");
}
});
You can then use it in your template as follows:
{{ mouse-tracking }}
Full example here

Related

ember.js - render a template in router.js equivalent for controllers

In ember, is there a way to render a template into an outlet from a controller to get the desired effect of:
this.render('some_template', {
into: 'template_name',
outlet: 'template_outlet'
});
Or better yet, call an action in router.js from a controller?
you can put a method in the corresponding route to the controller under the actions hash (Example: posts.index controller and posts.index route) and call it using send
posts.index controller
this.send('exampleAction', record);
posts.index route
actions: {
exampleAction: function(record){
console.log(record);
}
}
Instead of outlet, you can use a component:
<script type="text/x-handlebars" id="components/x-outlet">
{{ partial template }}
</script>
Then, in your controller, you can have a template property that you can pass to the component to display your template dynamically:
App.IndexController = Ember.ArrayController.extend({
template: function(){
return 'this_one';
}.property(),
actions: {
that_one: function(){
this.set('template', 'that_one');
}
}
});
Working example here
Not completely sure what you mean by
Or better yet, call an action in router.js from a controller?
but if you are just trying to transition into a different route, you can use the transitionToRoute() method (see here)

Trigger an action on the change event with Ember.js checkbox input helper?

How can I fire a named action upon changing a checkbox in Ember.js? Any help will be greatly appreciated.
Here is what I have. Checking or unchecking the checkbox has no effect.
Template:
{{input type="checkbox" on="change" action="applyFilter"}}
Controller:
actions: {
applyFilter: function() {
console.log("applyFilter");
}
}
I'd like to post an update to this. In Ember 1.13.3+, you can use the following:
<input type="checkbox"
checked={{isChecked}}
onclick={{action "foo" value="target.checked"}} />
link to source
using an observer seems like the easiest way to watch a checkbox changing
Template
{{input type='checkbox' checked=foo}}
Code
foo:undefined,
watchFoo: function(){
console.log('foo changed');
}.observes('foo')
Example
http://emberjs.jsbin.com/kiyevomo/1/edit
Or you could create your own implementation of the checkbox that sends an action
Custom Checkbox
App.CoolCheck = Ember.Checkbox.extend({
hookup: function(){
var action = this.get('action');
if(action){
this.on('change', this, this.sendHookup);
}
}.on('init'),
sendHookup: function(ev){
var action = this.get('action'),
controller = this.get('controller');
controller.send(action, this.$().prop('checked'));
},
cleanup: function(){
this.off('change', this, this.sendHookup);
}.on('willDestroyElement')
});
Custom View
{{view App.CoolCheck action='cow' checked=foo}}
Example
http://emberjs.jsbin.com/kiyevomo/6/edit
Post Ember version >= 1.13 see Kori John Roys' answer.
This is for Ember versions before 1.13
This is a bug in ember's {{input type=checkbox}} helper.
see https://github.com/emberjs/ember.js/issues/5433
I like the idea of having a stand-in. #Kingpin2k's solution works, but accessing views globally is deprecated and using observers isn't great.
In the linked github ember issue, rwjblue suggests a component version:
App.BetterCheckboxComponent = Ember.Component.extend({
attributeBindings: ['type', 'value', 'checked', 'disabled'],
tagName: 'input',
type: 'checkbox',
checked: false,
disabled: false,
_updateElementValue: function() {
this.set('checked', this.$().prop('checked'));
}.on('didInsertElement'),
change: function(event){
this._updateElementValue();
this.sendAction('action', this.get('value'), this.get('checked'));
},
});
Example usage in a template ('checked' and 'disabled' are optional):
{{better-checkbox name=model.name checked=model.checked value=model.value disabled=model.disabled}}
For those using Ember > 2.x:
{{input
change=(action 'doSomething')
type='checkbox'}}
and the action:
actions: {
doSomething() {
console.warn('Done it!');
}
}
In Ember v1.13 it can be done simply by creating a component named j-check in my occasion(no layout content required):
import Ember from 'ember';
export default Ember.Checkbox.extend({
change(){
this._super(...arguments);
this.get('controller').send(this.get('action'));
}
});
And then you just call it from your template like this:
{{j-check checked=isOnline action="refreshModel" }}

How to get the parent controller for a custom TextField

I have a simple controller
App.UploadController = Ember.Controller.extend({
toUpload: Ember.A([])
});
I have a template backing this w/ a custom text field
<div>
{{view App.UploadFileView name="file" contentBinding="content"}}
</div>
My custom text field in JS is below. The problem I'm having is that in the change event, I need to push an object into the parent controllers "toUpload" array but when I do a get on the parentView.controller it's undefined. How can I get the parent in this scenario?
App.UploadFileView = Ember.TextField.extend({
type: 'file'
change: function() {
var foo = Ember.Object.create();
this.get('parentView.controller').get('toUpload').pushObject(foo);
}
});
The TextField is a component, so the parent controller doesn't exist, you'd need to use sendAction to get things out of it.
Here's my implementation of the upload button that's just a view.
App.UploadFileView = Ember.View.extend({
tagName: 'input',
attributeBindings: ['type'],
type: 'file',
change: function() {
console.log(this.get('controller'));
}
});
http://emberjs.jsbin.com/oQaReMi/1/edit
If you are using an Ember Component (like TextField for example) you would do this like so
App.UploadFileView = Ember.TextField.extend({
change: function() {
console.log(this.get('targetObject'));
}
});
Note- this is in the current version of ember 1.3.x

How do I bind to the active class of a link using the new Ember router?

I'm using Twitter Bootstrap for navigation in my Ember.js app. Bootstrap uses an active class on the li tag that wraps navigation links, rather than setting the active class on the link itself.
Ember.js's new linkTo helper will set an active class on the link but (as far as I can see) doesn't offer any to hook on to that property.
Right now, I'm using this ugly approach:
{{#linkTo "inbox" tagName="li"}}
<a {{bindAttr href="view.href"}}>Inbox</a>
{{/linkTo}}
This will output:
<li class="active" href="/inbox">Inbox</li>
Which is what I want, but is not valid HTML.
I also tried binding to the generated LinkView's active property from the parent view, but if you do that, the parent view will be rendered twice before it is inserted which triggers an error.
Apart from manually recreating the logic used internally by the linkTo helper to assign the active class to the link, is there a better way to achieve this effect?
We definitely need a more public, permanent solution, but something like this should work for now.
The template:
<ul>
{{#view App.NavView}}
{{#linkTo "about"}}About{{/linkTo}}
{{/view}}
{{#view App.NavView}}
{{#linkTo "contacts"}}Contacts{{/linkTo}}
{{/view}}
</ul>
The view definition:
App.NavView = Ember.View.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews.firstObject.active');
}.property()
});
This relies on a couple of constraints:
The nav view contains a single, static child view
You are able to use a view for your <li>s. There's a lot of detail in the docs about how to customize a view's element from its JavaScript definition or from Handlebars.
I have supplied a live JSBin of this working.
Well I took what #alexspeller great idea and converted it to ember-cli:
app/components/link-li.js
export default Em.Component.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews').anyBy('active');
}.property('childViews.#each.active')
});
In my navbar I have:
{{#link-li}}
{{#link-to "squares.index"}}Squares{{/link-to}}
{{/link-li}}
{{#link-li}}
{{#link-to "games.index"}}Games{{/link-to}}
{{/link-li}}
{{#link-li}}
{{#link-to "about"}}About{{/link-to}}
{{/link-li}}
You can also use nested link-to's:
{{#link-to "ccprPracticeSession.info" controller.controllers.ccprPatient.content content tagName='li' href=false eventName='dummy'}}
{{#link-to "ccprPracticeSession.info" controller.controllers.ccprPatient.content content}}Info{{/link-to}}
{{/link-to}}
Building on katz' answer, you can have the active property be recomputed when the nav element's parentView is clicked.
App.NavView = Em.View.extend({
tagName: 'li',
classNameBindings: 'active'.w(),
didInsertElement: function () {
this._super();
var _this = this;
this.get('parentView').on('click', function () {
_this.notifyPropertyChange('active');
});
},
active: function () {
return this.get('childViews.firstObject.active');
}.property()
});
I have just written a component to make this a bit nicer:
App.LinkLiComponent = Em.Component.extend({
tagName: 'li',
classNameBindings: ['active'],
active: function() {
return this.get('childViews').anyBy('active');
}.property('childViews.#each.active')
});
Em.Handlebars.helper('link-li', App.LinkLiComponent);
Usage:
{{#link-li}}
{{#link-to "someRoute"}}Click Me{{/link-to}}
{{/link-li}}
I recreated the logic used internally. The other methods seemed more hackish. This will also make it easier to reuse the logic elsewhere I might not need routing.
Used like this.
{{#view App.LinkView route="app.route" content="item"}}{{item.name}}{{/view}}
App.LinkView = Ember.View.extend({
tagName: 'li',
classNameBindings: ['active'],
active: Ember.computed(function() {
var router = this.get('router'),
route = this.get('route'),
model = this.get('content');
params = [route];
if(model){
params.push(model);
}
return router.isActive.apply(router, params);
}).property('router.url'),
router: Ember.computed(function() {
return this.get('controller').container.lookup('router:main');
}),
click: function(){
var router = this.get('router'),
route = this.get('route'),
model = this.get('content');
params = [route];
if(model){
params.push(model);
}
router.transitionTo.apply(router,params);
}
});
You can skip extending a view and use the following.
{{#linkTo "index" tagName="li"}}<a>Homes</a>{{/linkTo}}
Even without a href Ember.JS will still know how to hook on to the LI elements.
For the same problem here I came with jQuery based solution not sure about performance penalties but it is working out of the box. I reopen Ember.LinkView and extended it.
Ember.LinkView.reopen({
didInsertElement: function(){
var el = this.$();
if(el.hasClass('active')){
el.parent().addClass('active');
}
el.click(function(e){
el.parent().addClass('active').siblings().removeClass('active');
});
}
});
Current answers at time of writing are dated. In later versions of Ember if you are using {{link-to}} it automatically sets 'active' class on the <a> element when the current route matches the target link.
So just write your css with the expectation that the <a> will have active and it should do this out of the box.
Lucky that feature is added. All of the stuff here which was required to solve this "problem" prior is pretty ridiculous.

Correct way to connect a child view in ember.js pre4

I'm trying to figure out the correct way to connect a child view in Ember.js pre4.
I have the following html set as the template in the class App.ContactsShowView :
<div class="container">
<h1>Show Contact</h1>
ID:{{id}}
</div>
Info:
{{outlet infoarea}}
I would like render ContactsShowinfoView into the above outlet infoarea.
App.ContactsShowinfoView = Ember.View.extend({
templateName: 'contact/templates/contactsShowinfoView',
});
Reading the docs it would seem that this should be done via the renderTemplate method in the Route. I've tried multiple variations of the following code:
App.ContactsShowRoute = Ember.Route.extend({
renderTemplate:function() {
this._super();
this.render( "contactsshowinfo", {
outlet:"infoarea"
});
}
});
At best I get no error message and just get the ContactShow view displayed (But no, connect outlet).
Am I missing someting obvious?
You are not using consistent names for the view/template. Try this:
App.ContactsShowInfoView = Ember.View.extend({
templateName: 'contact/templates/contactsShowInfoView',
});
App.ContactsShowRoute = Ember.Route.extend({
renderTemplate:function() {
this._super();
this.render( "contactsShowInfo", {
outlet:"infoarea"
});
}
});