How to properly use parameters in Ember query? - ember.js

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.

Related

Need to show/hide a button depending on the page

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!

RangeError: Maximum call stack size exceeded when using ember-cli-mirage

I am having issue using ember-cli-mirage. My instance of mirage is configured to use the RESTSerializer and the EmbeddedRecordsMixin for relationships. I am able to get records with relationship without a problem, however when I save the parent record I get the following error coming from the ember-data code:
RangeError: Maximum call stack size exceeded
Weirdly enough, if I just removed the EmbeddedRecordsMixin everything works fine. Is there some restriction or special thing you need to do use the EmbeddedRecordsMixin with mirage
// models/artist.js
import DS from 'ember-data';
const { attr, hasMany, Model } = DS;
export default Model.extend({
name: attr('string'),
genre: attr('string'),
country: attr('string'),
bio: attr(),
albums: hasMany('album', {
async: false
}),
songs: hasMany('song', {
async: false
})
});
// serializers/artist.js
import DS from 'ember-data';
const { RESTSerializer } = DS;
export default RESTSerializer.extend(EmbeddedRecordsMixin, {
attrs: {
albums: {
embedded: 'always'
},
songs: {
embedded: 'always'
}
}
});
// mirage/models/artist.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
albums: hasMany(),
songs: hasMany()
});
// mirage/factories/artist.js
import { Factory, faker } from 'ember-cli-mirage';
export default Factory.extend({
name() {
return faker.name.findName();
},
genre() {
return faker.hacker.noun();
},
country() {
return faker.address.country();
},
bio() {
return faker.lorem.sentence();
},
afterCreate(artist, server) {
server.createList('album', 3, { artist });
server.createList('song', 3, { artist });
}
});
// mirage/serializers/artist.js
import { RestSerializer } from 'ember-cli-mirage';;
export default RestSerializer.extend({
embed: true,
include: ['songs', 'albums']
});

ember-cli-mirage factory only includes one child

I have my mirage setup with two models and factories like so
mirage/models/user.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
posts: hasMany()
});
mirage/models/post.js
import { Model, belongsTo } from 'ember-cli-mirage';
export default Model.extend({
user: belongsTo()
});
mirage/factory/user.js
import { Factory, faker } from 'ember-cli-mirage';
export default Factory.extend({
name(i) {
return `Person ${i}`;
},
afterCreate(user, server){
server.create('post', 10, { user });
}
});
mirage/factories/post.js
import { Factory, association } from 'ember-cli-mirage';
export default Factory.extend({
title(i) {
return `Show ${i}`;
},
description(){
return faker.lorem.paragraph();
},
user: association()
});
mirage/serializers/user.js
import { RestSerializer } from 'ember-cli-mirage';
export default RestSerializer.extend({
include: ['posts']
});
mirage/scenarios/default.js
export default function(server) {
server.createList('user', 10);
}
In my model hook I make a request to this.get('store').findAll('user') and then I loop over it in my template. The problem I have is that this get request only includes 1 post per user. What I would have expected is to get 10 posts created per user.
What am I missing here?

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 .save() only saving one attribute

I am trying to create and save a model in Ember but only the first entry in my form is saving leaving the others blank.
Before saving all of the models attributes are correct. (shown with logs)
Any help greatly appreciated!
My Model is:
import DS from 'ember-data';
export default DS.Model.extend({
endpoint: DS.attr('string'),
playerVersion: DS.attr('string'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date')
});
My Route is:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.createRecord('account');
}
});
My Controller is:
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
save: function() {
var _this = this;
var model = this.get('model');
console.log(model.get('endpoint')); //Shows correct endpoint
console.log(model.get('playerVersion')); //Shows correct playerVersion
model.save().then(function(account) {
console.log(model.get('endpoint')); //Shows correct endpoint
console.log(model.get('playerVersion')); //Shows nothing
_this.transitionToRoute('accounts.index');
});
return false;
}
}
});
UPDATE - Was some settings needed on the custom Serializer needed.
export default JsonApiSerializer.extend({
keyForAttribute: function(key) {
//return Ember.String.dasherize(key);
return Ember.String.camelize(key);
},
keyForRelationship: function(key) {
//return Ember.String.dasherize(key);
return Ember.String.camelize(key);
},
keyForSnapshot: function(snapshot) {
//return Ember.String.dasherize(snapshot.typeKey);
return Ember.String.camelize(snapshot.typeKey);
},
});