How can I change this mixin, that I call in a controller, so that I am able to run tests.
import Ember from 'ember';
export default
Ember.Mixin.create({
scrollViewTo: function (navigateToId) {
Ember.run.scheduleOnce("afterRender", function () {
Ember.$('html,body').animate({scrollTop: Ember.$(navigateToId).offset().top}, 1000);
});
}
});
Any code in my test that results in scrollViewTo beign called result in this:
at http://localhost:7357/assets/test-loader.js:14:29: Cannot read property 'top' of undefined
I suspect I need to move all this logic to events in my view?
Related
Component action triggers perfectly.
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
searchField: function() {
console.log('searchField');
this.sendAction('searchField');
}
}
});
Route does not get triggered.
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
searchField: function() {
console.log('ROUTE');
}
}
});
Handlebars
{{input key-up='searchField' searchField=(action "searchField")}}
I have spent so much time with this I am starting to lose interest with Ember.js, as I have also tried according to the documentation, but I get the same result.
http://emberjs.com/api/classes/Ember.Component.html#method_sendAction
sendAction will not reach to route.
You have two options,
define searchField function in controller and from there you can route functionthis.send('searchField')
To directly call from component to route, there is addon ember-route-action-helper for this.
ember install ember-route-action-helper
Refer answer for more info.
To play around - Sample Twiddle
Let'a assume a simple EmberJS component:
//my-app/components/my-component.js
export default Ember.Component.extend({
classNames: ['cursor-pointer'],
doSth(){
// whatever, state of the component does not change
},
clickListener: Ember.on('click', function(){
this.doSth();
})
});
Now I would like to use integration tests to find out whether clicking the component reaches (delegates to) the doSth() method.
moduleForComponent('my-component', 'Integration | Component | stores parser previewer', {
integration: true
});
test('should call doSth() on click', function (assert) {
/* Line 3 - the place I tried to set mocked doSth() up */
this.render(hbs`{{my-component}}`);
this.$('.cursor-pointer').click(); // triggers clickListener() correctly
});
The problem is that I cannot substitute method doSth() with my mock.
Thus I never run into assert.ok() statement.
I have tried to insert following statements onto the line 3 in the test:
// idea 1
this.set('doSth', function(){
assert.ok(true);
});
// idea 2
this.doSth = function(){
assert.ok(true);
};
None of the 2 approaches (idea 1, idea 2) worked. this.subject() is also unavailable since it's an integration test, not a unit test.
Update:
Imagine doSth() be a function like the openUrlInANewTab() shown below that does not influence the component's state, nor the associated controller's nor route's state.
Example:
//my-app/components/my-component.js
...
import openUrlInANewTab from 'my-app/utils/new-tab-opener';
...
export default Ember.Component.extend({
...
doSth(url){
openUrlInANewTab(url);
}
while
// my-app/utils/new-tab-opener.js
export default function openUrlInANewTab(){
anchor = document.createElement("a");
...
anchor.click();
...
}
You can move doSth to service custom-service that you inject to your my-component. Then in test you can EASILY test this by injecting fake service.
tests/integration/components/my-component-test.js:
test('should call doSth() on click', function(assert) {
const customStub = Ember.Service.extend({
doSth() {
// whatever, state of the component does not change
console.log('Stubbed doSth fired');
assert.ok(true, 'Stubbed doSth fired');
}
});
this.register('service:custom-service', customStub);
this.inject.service('custom-service', { as: 'customService' });
this.render(hbs`{{my-component}}`);
this.$('.cursor-pointer').click(); // triggers clickListener() correctly
});
components/my-component.js:
import Ember from 'ember';
export default Ember.Component.extend({
customService: Ember.inject.service('custom-service'),
classNames: ['cursor-pointer'],
clickListener: Ember.on('click', function(){
this.get('customService').doSth();
console.log('Click listener');
})
});
Working demo.
Ember Guides.
As an alternative to what Daniel had proposed you might consider using a closure action in case of handling click within your component. What I mean is instead of delegating to one of your methods (what you had done and could not test) or to a service (what Daniel suggested) just trigger an action passed to your component. This way also provides you the opportunity to mock in your test. Moreover, this approach results in a more reactive component; since you inject the behavior to your component from the owner at runtime; which enables you to reuse your custom component within different contexts (different parent components/templates) easily. See the twiddle for an example of what I mean.
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
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.
}
});
I am using Ember's Need Api to call a method of a controller in another controller. I am able to get the instance of the controller but when I am calling it method it returns me this error TypeError: Object [object Object] has no method.
This is how I am calling it:
Cards.CardsIndexController = Ember.Controller.extend({
needs: 'account_info',
actions: {
accountInfoStart:function(){
console.log(this.get('controllers.account_info').test()); // error here
}
}
});
This is the controller whose function I want to call
Cards.AccountInfoController = Ember.Controller.extend({
actions:{
test: function(){
alert(1);
}
}
});
How can I solve it?
test is not technically a method, but an action or event. Use the send method instead:
this.get('controllers.account_info').send('test', arg1, arg2);
As per Ember documentation; create a property that lazily looks up another controller in the container. This can only be used when defining another controller.
legacy ember application example:
App.PostController = Ember.Controller.extend({
accountInfo: Ember.inject.controller()
this.get('accountInfo').send('test')
});
modern ember application example:
// in an ember app created with ember-cli
// below snippet would be the app/controllers/post.js file
import Ember from 'ember';
export default Ember.Controller.extend({
appController: Ember.inject.controller('application')
});
You can find more documentation about Ember.inject here
From the Updated Ember Documentation :
import { inject } from '#ember/controller';
export default Ember.Controller.extend({
appController: inject('application')
});
For further reference, you can find out by this link https://guides.emberjs.com/release/applications/dependency-injection/#toc_ad-hoc-injections