Ember.js: access the current component from helper - ember.js

Looking for any solution (even dirty hacks) to access the current component from a custom helper.
import Ember from 'ember';
export default Ember.Helper.extend({
compute() {
... who is computing me? ...
}
});

Simply pass this to the helper.
Say the code example you mentioned is for format-currency helper, so you can pass the context like {{format-currency value this}}
And in the helper you can access it like:
import Ember from 'ember';
export default Ember.Helper.extend({
compute([value, container]) {
//... who is computing me? ...
// container is computing you
}
});

Related

How to hand off model data to component in Ember 2.x

I have a route in ember which looks like
//fish.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
fishPrices: this.store.query('fish-price', {filter: {type: params.type}}),
type: params.type
});
}
});
My fish.hbs uses model.type to change text on the page. However, I need to hand model.fishPrices off to a component which plots the price of the fish as a function of time:
//fish-price-plot.js
import Ember from 'ember';
/* global Plotly */
export default Ember.Component.extend({
didInsertElement() {
console.log(this.get('model'));
Plotly.plot( 'fish-price-plot', [{
// ...
//Need to access model.fishPrices here
}
});
How do I access the model in this component? I see a lot of information that suggests that I should be able to do something like
var fishPrices = this.get('model.fishPrices');
//do something with fishPrices
but this always ends up undefined.
One way is directly passing it to the component props like this:
// route template in which you want to use your component & model
{{fishprice-plot model=model}}
Have a look at the following twiddle that demo's the first use case.
The other is injecting a service into the component with the required data.
// components/fishprice-plot.js
export default Ember.Component.extend({
fishData: Ember.inject.service()
});
Have a look at this twiddle that demonstrates passing data to a component more comprehensively, and also this part of the guides, as pointed out by #locks in comments.
You can also have a look at this SO link regarding passing properties to your component.

Call Router.generate from a helper

I need to generate the url for given route from a helper.
How to generate url for a route in Ember.js pointed me to use the generate function. And yes it works as i need (Checked the functionality by making application route global). But i am not sure how to call it from inside a helper.
You were in a good direction, so you mainly solved this problem. :) There are two type of helper in Ember a simple function helper and the Class Based Helpers. We will use a Class Based Helper in this case.
As you have seen in your linked example, we need access to the main Router. We can do this with Ember.getOwner(this).lookup('router:main'). (Ember.getOwner() exists from v2.3, before v2.3 use this.container.lookup('router:main'))
For example, you have this map in your router.js:
Router.map(function() {
this.route('about');
this.route('posts', function() {
this.route('post', {path: '/:post_id'});
});
});
And if you create a helper for example with the name of url-for your template could contain these lines:
{{url-for 'about'}}
{{url-for 'posts'}}
{{url-for 'posts.post' 2}}
And your Class Based Helper could be the following:
// app/helpers/url-for.js
import Ember from 'ember';
export default Ember.Helper.extend({
router: Ember.computed(function() {
return Ember.getOwner(this).lookup('router:main');
}),
compute([routeName, ...routeParams]) {
let router = this.get('router');
return Ember.isEmpty(routeParams) ?
router.generate(routeName) : router.generate(routeName, routeParams[0]);
}
});
Demo on Ember Twiddle

Ember transitionToRoute cleanly in a component without sendAction

How can transitionToRoute be called cleanly from within an Ember component?
It works with injecting a controller into the component and calling the controller's transitionToRoute function, however I'd like something a little more elegant if possible.
What it currently looks like inside the component's javascript:
// this.controller is injected in an initializer
this.controller.transitionToRoute("some.target.route.name");
What would be nicer in the component's javascript:
transitionToRoute("some.target.route.name");
One goal is do this without using sendAction as this particular component has a single purpose and should always transition to the same route. There's no need for any other Ember artifacts to be aware of the route this component always transitions to, there's no need for the associated indirection. The responsibility for the target route is owned by this component.
UPDATE Please see the other more recent answers for how to achieve this with less code in newer Ember versions, and vote those up if they work for you - Thanks!
Inject the router into the components and call this.get('router').transitionTo('some.target.route.name').
To inject the router into all components, write an initializer at app/initializers/component-router-injector.js with the following contents:
// app/initializers/component-router-injector.js
export function initialize(application) {
// Injects all Ember components with a router object:
application.inject('component', 'router', 'router:main');
}
export default {
name: 'component-router-injector',
initialize: initialize
};
Sample usage in a component:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
submit: function() {
this.get('router').transitionTo('some.target.route.name');
}
}
});
Jan 22, 2018 update
As of Ember 2.15, phase 1 of the public router service is implemented.
Transition to a route from inside a component:
import { inject as service } from '#ember/service';
export default Ember.Component.extend({
router: service(),
actions: {
someAction() {
this.get('router').transitionTo('index');
}
}
});
Use
router: service()
instead of
router: service('-routing')
import Component from '#ember/component';
import {inject as service} from '#ember/service';
export default Component.extend({
router: service(),
actions: {
onClick(params) {
let route = this.getMyRoute(params);
this.get('router').transitionTo(route);
}
}
});
If you want to use the router only in a specific component or service or controller, you may try this:
Initialize an attribute with the private service -routing. The - because it's not a public API yet.
router: service('-routing'),
And then inside any action method or other function inside the service or component:
this.get('router').transitionTo(routeName, optionalParams);
Note: It'll be transitionToRoute in a controller.
You can use container to get access to any needed part of application. To get application controller :
this.container.lookup('controller:application')
But what about structure of application - components should generate events - so my opinion it's better to use sendAction. Cause in future you can get situation, when you need to filter such behavior ( for example ) or other application-specific logic before transition

Limit to the first 10 elements of a model in the controller

I'm struggling to understand how promises work in the controller. I'd like to display just the first 10 sortedShips in my template but I can't find a way to get slice(0,10) working in my controller.
How can I limit sortedShips or a new property to the first 10 elements only?
app/controllers/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
shipSort: ['name:asc'],
sortedShips: Ember.computed.sort('model.ships', 'shipSort').property('model.ships')
});
Not sure what split() is, but Ember's computed.filter function should do the trick:
import Ember from 'ember';
export default Ember.Controller.extend({
shipSort: ['name:asc'],
// You don't need the .property() here, Ember does that for you
sortedShips: Ember.computed.sort('model.ships', 'shipSort'),
firstTenShips: Ember.computed.filter('sortedShips', function(ship, index) {
return (index < 10);
})
});

didInsertElement from controller?

I have an ember application with a controller header.js and a template header.hbs.
Now I have some javascript I need to execute at document $( document ).ready()
I saw on Ember Views there is didInsertElement but how do I do this from the controller?
// controllers/header.js
import Ember from 'ember';
export default Ember.Controller.extend({
});
// views/header.js
import Ember from 'ember';
export default Ember.Controller.extend({
});
// templates/header.js
test
I read several times it's not good practice to be using Ember Views?
the controller is not inserted (the view is) hence there is no didInsertElement.
If you need something to run once, you can write something like this:
import Ember from 'ember';
export default Ember.Controller.extend({
someName: function () { // <--- this is just some random name
// do your stuff here
}.on('init') // <---- this is the important part
});
A more actual answer (Ember 2.18+) while honouring the same principle as mentioned in user amenthes' answer: it's no longer advised to use Ember's function prototype extensions. Instead you can override the init() method of a controller directly. Do note that you'll have to call this._super(...arguments) to ensure all Ember-related code runs:
import Controller from '#ember/controller';
export default Controller.extend({
init() {
this._super(...arguments); // Don't forget this!
// Write your code here.
}
});