Need to show/hide a button depending on the page - ember.js

I am trying to hide back button on site-header that takes me to dashboard. I am using pod structure that is something like this:
pod
component
site-header
template.hbs
component.js
main
dashboard
In the component.js I used computed to get current route
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { computed } from '#ember/object';
export default Component.extend({
router: service (),
dashboard:computed('currentRouteName',function(){
if(this.get('currentRouteName') === 'main.dashboard.index'){
return true;
}
return false;
})
})
In template.hbs I used the following code to check the link.
{{#unless dashboard}}
{{#link-to "main.dashboard" class="back-btn"}}{{t "goBackToDashboard"}}{{/link-to}}
{{/unless}}
Still it is the same by tweaking the if/else conditions also I either get the button on all pages or on none.
Any help will be appreciated.
app/route.js:
import EmberRouter from '#ember/routing/router';
import config from './config/environment';
import { inject } from '#ember/service';
import $ from 'jquery';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL,
ajax: inject('ajax'),
});
Router.map(function () {
this.route('login', { path: 'login' });
this.route('main', { path: '/' }, function () {
this.route('dashboard', { path: '' }, function () {});
this.route("review", { path: "/review/:docId" }, function() { // eslint-disable-line
this.route("edit", { path: "/edit/:bitId" }); // eslint-disable-line
this.route('window_edit');
});
}

You mention that the computed property is in the component.js, and you are doing this.get('currentRouteName'), but that property does not exist in components.
I believe you need to use the router service in your component.
I'm assuming you are using pre-Octane syntax, so it should look something like this:
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { computed } from '#ember/object';
export default Component.extend({
router: service(),
dashboard: computed('router.currentRouteName',function() {
if (this.get('router.currentRouteName') === 'main.dashboard.index') {
return true;
}
return false;
})
});
I don't remember which version RouterService was first available, but I hope this helps!

Related

How to call a route function in Ember js controller function test?

I have an ember js controller function onRefreshClicked which call a function inside the route refreshData.
I would like to run a unit/integration test for this controller's function, but then it cannot find the link to the route's function and error out.
How do I do this in ember v3.2.x?
addon/controllers/scan-monitor.js
import { action } from '#ember/object';
export default class ScanMonitorController extends Controller {
#action
onRefreshClicked(clickEvent) {
debugger;
clickEvent.preventDefault();
// Some extra logic
this.send('refreshData'); // ----> ERROR OUT!
}
}
addon/routes/scan-monitor.js
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
import { action } from '#ember/object';
export default class ScanMonitorRoute extends Route {
#service store;
#action
refreshData() {
debugger;
this.refresh();
}
}
tests/unit/controllers/scan-monitor-test.js
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Controller | scan-monitor', function (hooks) {
setupTest(hooks);
let scanMonitorController;
hooks.beforeEach(function () {
scanMonitorController = this.owner.lookup('controller:scan-monitor');
});
test('controllers.scan-monitor.onRefreshClicked', function (assert) {
debugger;
// Assigning or not assigning the route doesn't make any difference.
// scanMonitorController.route = this.owner.lookup('route:scan-monitor');
scanMonitorController.onRefreshClicked(new MouseEvent('click', {}));
assert.ok(scanMonitorController);
});
});
The error
TypeError: Cannot read property 'trigger' of undefined
at Router.send (http://localhost:7357/assets/vendor.js:31600:28)
at ScanMonitorController.send (http://localhost:7357/assets/vendor.js:34358:16)
at ScanMonitorController.onRefreshClicked (http://localhost:7357/assets/vendor.js:173217:12)
at Object.<anonymous> (http://localhost:7357/assets/tests.js:527:29)
My favorite technique is to define methods on the route's model data, like this:
async model() {
return {
myMethod: () => {}
};
}
Then, in your controller you can invoke via this.model.myMethod()

How to properly use parameters in Ember query?

I have been trying to implement pagination (I've tried both ember-cli-pagination and ember-simple-pagination) for my application but I've had a lot of issues. So I decided to try custom pagination and noticed that I cannot pass parameters into my query. For instance, when visiting my api at: http://jsonplaceholder.typicode.com/posts?_start=0&_limit=10, start and limit both work properly. When calling it in my route, it seems to ignore that entirely and just give me all entries. I would appreciate all insight into what I am doing wrong or how to properly implement pagination in this case.
app/adapters/post.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
host:'https://jsonplaceholder.typicode.com',
pathForType(){
return 'posts';
}
});
app/models/post.js
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({
user:DS.belongsTo('user'),
title:DS.attr('string'),
body:DS.attr('string'),
});
app/routes/post.js
import Route from '#ember/routing/route';
import { set } from '#ember/object';
import { hash } from 'rsvp';
export default Route.extend({
model() {
return hash({
post: this.store.query('post', {
start: 0,
limit: 10
}),
user: this.store.findAll('user')
});
},
setupController(controller, model) {
this._super(...arguments);
set(controller, 'posts', model.post);
set(controller, 'users', model.user);
}
});
You need define the query params in both side Route and Controller.
app/routes/post.js
import Route from '#ember/routing/route';
import { set } from '#ember/object';
import { hash } from 'rsvp';
export default Route.extend({
queryParams = {
start: {
refreshModel: true
},
limit: {
refreshModel: true
}
},
model() {
return hash({
post: this.store.query('post', {
start: 0,
limit: 10
}),
user: this.store.findAll('user')
});
},
setupController(controller, model) {
this._super(...arguments);
set(controller, 'posts', model.post);
set(controller, 'users', model.user);
}
});
And inside app/controllers/post.js
import Controller from '#ember/controller';
export default class ArticlesController extends Controller {
queryParams = ['start', 'limit'];
start = 1;
limit = 5;
}
Ember by default does not call model when query params are changed. We tell it to do so any time start/limit changes, through refreshModel: true.

How to pass errors in template from route?

I do 'server side validation'. In route in method 'catch' get errors from server. And I want pass this errors in template.
How to pass errors in template from route?
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
model() {
return this.store.createRecord('project');
},
actions: {
save(project) {
var router = this;
var errors = router.controllerFor('project.new').get('errors')
project.save().then(function(project){
router.transitionTo('projects.show', project);
}).catch(function(resp) {
// how to pass this errors in template ????
console.log(resp.errors);
});
}
},
});
From router.js
this.route('projects', function() {
this.route('new');
this.route('show', { path: '/:project_id' });
});
From Component
import Ember from 'ember';
import DS from 'ember-data'
export default Ember.Component.extend({
actions: {
save() {
this.project.set('colors', colors);
this.sendAction('save');
}
},
...
});
Closure Actions! (Assuming a recente version of - Ember 1.13+). Closure actions can have a return value, unlike regular actions.
On your template you do:
{{my-component mySave=(action 'save')}}
And in your component you do
import Ember from 'ember';
import DS from 'ember-data'
export default Ember.Component.extend({
actions: {
save() {
this.project.set('colors', colors);
let result = this.attrs.mySave();
//do something with result
}
},
...
});
And then in your controller:
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Controller.extend({
actions: {
save(project) {
var router = this;
var errors = router.controllerFor('project.new').get('errors')
project.save().then(function(project){
router.transitionTo('projects.show', project);
}).catch(function(resp) {
return resp.errors;
});
}
},
});
I would also recommend this article on Closure Actions which is very helpful.
EDIT: I initially replied with the action being on the route (as in your example) but #Kitler correctly reminded that closure actions communicate with the controller or another component. I don't know if that's an option for the OP?

Ember component not updating in integration test when injected service is updated

I have a side-bar component which relies on side-bar service which is injected into it via initializer.
the component then has a computed property title which is tied to the same property on the service:
title: function () {
return this.get('sideBarService.title');
}.property('sideBarService.title'),
This works in the app itself but I cannot get the component to update in an integration test when the service is upated.
Here is my non working integration test:
import Ember from 'ember';
import startApp from '../helpers/start-app';
import hbs from 'htmlbars-inline-precompile';
import { moduleForComponent, test } from 'ember-qunit';
var application, container, sideBarService;
moduleForComponent('side-bar', 'Integration | side-bar',{
integration: true,
beforeEach: function() {
application = startApp();
container = application.__container__;
sideBarService = container.lookup('service:side-bar');
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
test('it displays the correct title', function(assert) {
assert.expect(1);
Ember.run(function () {
sideBarService.set('title', 'Hello');
});
this.render(hbs`
{{side-bar}}
`);
var content = this.$('.side-bar-content .title').text().trim();
var serviceTitle = sideBarService.get('title');
// fails
assert.deepEqual(content, serviceTitle);
});
Interestingly, if I debug in the test and grab the component with the console and then grab the sideBarService off of the component, it is aware of the updated title value and even the value title on the component itself seems to be updated but the dom never gets updated:
//debugged in browser console
var sb = container.lookup('component:side-bar')
undefined
sb.get('title')
"Hello"
sb.get('sideBarService.title')
"Hello"
this.$('.title').text().trim()
""
Is this a run loop issue? If so what do I need to do to set it off?
edit: In regards to Toran's comment. Does this look right?
var done = assert.async();
var content = this.$('.side-bar-content .title').text().trim();
var serviceTitle = sideBarService.get('title');
setTimeout(function() {
assert.deepEqual(content, serviceTitle);
done();
});
I would probably go about fixing this by avoiding the injection in the initializer and instead using the Ember.inject.service helper.
// component
import Ember from 'ember'
const { Component, inject, computed } = Ember;
const { service } = inject;
const { alias } = computed;
export default Component.extend({
sideBarService: service('side-bar'),
title: alias('sideBarService.title')
});
Then in your test, you can pass the service when you use the component.
import Ember from 'ember';
import startApp from '../helpers/start-app';
import hbs from 'htmlbars-inline-precompile';
import { moduleForComponent, test } from 'ember-qunit';
var application, container, sideBarService;
moduleForComponent('side-bar', 'Integration | side-bar',{
integration: true,
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
test('it displays the correct title', function(assert) {
assert.expect(1);
this.set('sideBarService', Ember.Object.create({
title: 'hello'
}));
this.render(hbs`
{{side-bar sideBarService=sideBarService}}
`);
var title = this.$('.side-bar-content .title').text().trim();
assert.equal(title, 'hello'); // Hopefully passes
});

Testing ember using ember-cli and factory guy

I'm running into a problem where the Ember application I'm testing doesn't seem to be noticing the models that I'm creating with FactoryGuy. Here's my test file:
import Ember from 'ember';
import startApp from '../helpers/start-app';
import FactoryGuy from 'factory-guy';
import { testMixin as FactoryGuyTestMixin} from 'factory-guy';
import carsFactory from "../fixtures/car";
var application, testHelper, store, make;
var TestHelper = Ember.Object.createWithMixins(FactoryGuyTestMixin);
module('Acceptance: Cars', {
setup: function() {
application = startApp();
testHelper = TestHelper.setup(application);
store = testHelper.getStore();
testHelper.make('car');
},
teardown: function() {
Ember.run(function() { testHelper.teardown(); });
Ember.run(application, 'destroy');
}
});
test('visiting /cars', function() {
equal(store.all('car').get('content.length'), 1);
visit('/cars');
andThen(function() {
equal(currentPath(), 'cars');
var li = find('li');
equal(li.length, 2);
});
});
The first and second equal assertions will succeed, but the last one will fail. Here's what my template looks like:
<ul>
{{#each car in model}}
<li>{{car.label}}</li>
{{/each}}
</ul>
And my route:
import Ember from 'ember';
export default Ember.Route.extend({
model: function () {
this.store.find('car');
}
});
What am I missing in getting the Ember app's store to get properly populated by the FactoryGuy's make method?
Edit: I also have tried adding the following line at the top of the test method and in the setup function, and it still isn't working correctly.
testHelper.handleFindMany('car', 1);
EmberDataFactoryGuy is now an ember addon, so if you are using that then the test would look like this:
import Ember from 'ember';
import startApp from '../helpers/start-app';
import { make } from 'ember-data-factory-guy';
import TestHelper from 'ember-data-factory-guy/factory-guy-test-helper';
var App;
module('Acceptance: Cars', {
setup: function() {
Ember.run(function () {
App = startApp();
TestHelper.setup();
});
},
teardown: function() {
Ember.run(function() {
TestHelper.teardown();
App.destroy();
});
}
});
test('visiting /cars', function() {
TestHelper.handleFindAll('car', 2);
visit('/cars');
andThen(function() {
equal(currentPath(), 'cars');
var li = find('li');
equal(li.length, 2);
});
});
There is a sample acceptance test just like this one in the ember-data-factory-guy repo here ( looks pretty much just like this one though ):
https://github.com/danielspaniel/ember-data-factory-guy/blob/master/tests/acceptance/users-view-test.js
Anyway, there is no more hassle of setting the store, or creating TestHelper, it's all done for you, and setup automatically when you start the application.