Ember-cli-pagination unable to print the remote paginated value in screen - ember.js

I use cli-pagination for pagination. It is working fine but i am unable to set the page value = 0 instead it is always 1. Then while trying to print the value in screen it always print the id the model and other values are always empty. Please find the code sample below.
model
export default DS.Model.extend({
id: DS.attr(),
name: DS.attr(),
status: DS.attr()
});
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ["page", "perPage"],
page: 0,
perPage: 2,
//some actions
});
Route
import Ember from 'ember';
import RouteMixin from 'ember-cli-pagination/remote/route-mixin';
export default Ember.Route.extend(RouteMixin, {
model: function(params) {
params.paramMapping = {
perPage: "size"
};
return this.findPaged('book',params);
},
setupController(controller, model) {
controller.set('model', model);
controller.set('books', model);
}
});
Template
<div class="row">
<div class="col-md-2">
<h3>
Books
</h3>
</div>
</div>
<br/>
{{#each model as |book|}}
{{! your app's display logic}}
<h2>{{book.id}}</h2>
<h2>{{book.name}}</h2>
<h2>{{book.status}}</h2>
<h2>entered paged content</h2>
{{/each}}
{{page-numbers content=model}}
In the above code I am getting value for book.id but book.name? &book.status` is always empty even though getting value from backend.
Also by default page attribute is 1 but I want to set it to 0 because my backend API will accept page number 0.

Related

EmberError: Nothing handled the action

I have been struggling with making my Ember Application work with Firebase.
I went through all the posts here on Stackoverflow about the similar matter but I did not find the answer to my problem. So here it is:
Whenever I try to put data into input fields and submit them with a button i get the console error:
EmberError
code : undefined
description : undefined
fileName : undefined
lineNumber : undefined
message :
"Nothing handled the action 'createBook'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble."
My model:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
picture: DS.attr('string'),
buyer: DS.attr('string'),
bought: DS.attr('boolean', { defaultValue: false }),
createdAt: DS.attr('date', { defaultValue() { return new Date(); } })
});
And my Controller:
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
createBook: function(){
var newBook = this.store.createRecord('book', {
title: this.get('title'),
author: this.get('author'),
picture: this.get('picture'),
buyer: this.get('buyer'),
bought: this.set(false),
createdAt: new Date().getTime()
});
newBook.save();
//reset values after create
this.setProperties({'title' : '',
'author' : '',
'picture' : '',
'buyer' : ''
});
}
}
});
The template:
{{outlet}}
<div style ="margin-left:130px;">
<h1> blabla </h1>
{{input type="text" value=title placeholder="Add Title"}}<br>
{{input type="text" value=author placeholder="Add author"}}<br>
{{input type="text" value=picture placeholder="Add picture"}}<br>
{{input type="text" value=buyer placeholder="buyer"}}<br>
</div>
<button class="btn btn-default" {{action "createBook" }}> Create</button>
{{#each model as |book|}}
<ul>
<li>{{book.title}}</li>
</ul>
{{/each}}
The connection between the Firebase and Ember is set up 100 % properly.
For now the rules on firebase have been set to true for both read and write.
The only problem is that it does not post the data to Firebase.
Thanks #Lux for your advice.
There were several things wrong with my code.
I created model and controller called book, but route called books.
I did not know that it will have an effect on my model and controller.
So i ended up with:
app/controllers/book.js
app/model/book.js
app/routes/book.js
app/templates/book.hbs
This was not enough. I also had to edit content of my controller
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
createBook: function(){
var newBook = this.store.createRecord('book', {
title: this.get('title'),
author: this.get('author'),
picture: this.get('picture'),
buyer: this.get('buyer')
});
newBook.save();
//reset values after create
this.setProperties({'title' : '',
'author' : '',
'picture' : '',
'buyer' : ''
});
}
}
});
As you can see I have removed the lines that were setting the default values of bought and createdAt. It was enough just to set them inside the model itself.

Filtering an array in ember

Ok so I'm fairly new to programing, I know how to run a filter on a JSON Array but I cant seem to figure it out when I'm pulling the data from firebase and viewing it in an Ember app.
this is my route.js code:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('user');
}
});
This is my template.hbs code the href="#!" is the generic from materialize for the dropdown buttons:
<div class="list-wrapper col-xs-10 col-xs-offset-1">
<div class="col-xs-12 button-div">
{{#md-btn-dropdown text='Filter' class="col-xs-2" belowOrigin=true}}
<li>Female</li>
<li>Male</li>
<li>All</li>
{{/md-btn-dropdown}}
</div>
{{#each model as |info|}}
<div class="col-xs-3 user-card">
<div class="card-info">
<ul>
<li>Last Name- {{info.lastName}}</li>
<li>First Name- {{info.firstName}}</li>
<li>Gender- {{info.gender}}</li>
<li>{{info.email}} </li>
</ul>
</div>
</div>
{{/each}}
</div>
{{outlet}}
This is my controller.js code which I no is all wrong:
import Ember from 'ember';
export default Ember.Controller.extend({
customFilter: function(gender) {
return function(el) {
var r = el.user;
return r.gender === gender;
};
}
});
and this is my model:
import DS from 'ember-data';
import Ember from 'ember';
export default DS.Model.extend({
lastName: DS.attr('string'),
firstName: DS.attr('string'),
gender: DS.attr('string'),
email: DS.attr('string')
});
I've searched high and low and I'm sure I'm just missing something basic and stupid. What I want is for the dropdown menu to be able to filter and display only female, male or all. Again I'm new to this stuff so I apologize if this is a pretty basic thing. Thank You
What your missing is an action that updates your controller when an item in the dropdown is actually selected.
Some helpful reading:
Triggering changes with actions
Computed Properties
Here's how to put actions in your dropdown component
{{#md-btn-dropdown text='Filter' class="col-xs-2" belowOrigin=true}}
<li><a {{action "filterUpdated" "female"}}>Female</a></li>
<li><a {{action "filterUpdated" "male"}}>Male</a></li>
<li><a {{action "filterUpdated"}}>All</a></li>
{{/md-btn-dropdown}}
In your controller you then need to handle this action like so:
import Ember from 'ember';
export default Ember.Controller.extend({
// the people property is an alias of the model object
// which essentially makes people a synonym for model
// read more http://emberjs.com/api/classes/Ember.computed.html#method_alias
people: Ember.computed.alias('model'),
// holds the currently selected gender, e.g., "female". A null value indicates there is no filter.
currentFilter: null,
/*
filteredPeople is a computed array containing people models.
The array is recomputed every time the model changes or the currentFilter changes,
see the .property() bit at the end.
read more: http://emberjs.com/api/classes/Ember.computed.html#method_filter
*/
filteredPeople: Ember.computed.filter('people', function(person/*, index, array*/) {
// this function is passed each item in the model array, i.e., the person argument
// there's no need to use the index nor array arguments, so I've commented them out
if(this.get('currentFilter') === null) {
// when the filter is null, we don't filter any items from the array
return true;
} else {
// otherwise only return true if the gender matches the current filter
return person.gender === this.get('currentFilter');
}
}).property('people', 'currentFilter'),
actions: {
filterUpdated: function (value) {
if (Ember.isEmpty(value)) {
// an empty value means we should clear the filter
this.set('currentFilter', null);
}
else {
this.set('currentFilter', value);
}
}
}
});
Finally, edit your template to change
{{#each model as |info|}}
to
{{#each filteredPeople as |info|}}
Also at a meta level, don't apologize for asking questions! Everyone is new at something at somepoint, and often asking is the best way to learn. That's what stackoverflow is all about :)
Something like this would work:
gender: 'All',
filteredModel: Ember.computed.filter('model', function(person) {
return person.gender === this.get('gender');
}).property('gender'),
this assumes that it starts on all, and then when the dropdown changes the value of gender, then the filteredModel will get updated. You can then in your hbs file change the result to:
{{#each filteredModel as |info|}}

ember firebase pass models to action handler

I have ember data models hooked with firebase, characters and spells. I can create new models and save them to firebase. Now I wanted to add spells to character. I defined that character has many spells:
export default DS.Model.extend({
chClass: DS.attr(),
chName: DS.attr(),
chImage: DS.attr(),
chSpells: DS.hasMany('spell', {async: true}),
});
In my hbs I listed spells in <select> element, there is also input fields and add button.
Add new character <br>
name {{input value=mchName }}<br>
class {{input value=mchClass }}<br>
image {{input value=mchImage }}<br>
<br>
Choose Spells:<br>
<select name="spellslist" multiple>
{{#each spells as |spell index|}}
<option value="{{index}}">{{spell.spName}}</option>
{{/each}}
</select>
<button {{action 'addChar' spells}}>add</button><br>
So when user types in character name, level and picks some spells I want to call addChar action function on add button and pass this data.
export default Ember.Controller.extend({
mchName:'',
mchClass:'',
mchImage:'',
store: Ember.inject.service(),
actions: {
addChar: function(spells) {
var newChar = this.store.createRecord('character');
newChar.set("chName", this.mchName);
newChar.set("chClass", this.mchClass);
newChar.set("chImage", this.mchImage);
newChar.get("chSpells").addObject(?????? how to get spell here ?????);
newChar.save();
I know how to pass string from inputs, but I dont know how to pass selected spells to this function, its killing me.
I'm assuming that you (as admin) are going to populate the spells table. Now ... assuming that a character can have many spells and a spell can have many characters, here's how one can approach this (note that I'm using a controller ... you should ideally be doing this in a component):
Character model is simplified:
//app/models/character.js
import DS from 'ember-data';
export default DS.Model.extend({
chName: DS.attr(),
chSpells: DS.hasMany('spell', {async: true})
});
Spells model is also simplified for this example:
//app/models/spell.js
import DS from 'ember-data';
export default DS.Model.extend({
spName: DS.attr(),
spellChar: DS.hasMany('character', {async: true})
});
We need an include helper for the multiline select. Review this article for details:
//app/helpers/include.js
import Ember from 'ember';
export function include(params/*, hash*/) {
const [items, value] = params;
return items.indexOf(value) > -1;
}
export default Ember.Helper.helper(include);
Here's the application route:
app/routes/application.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
var spells = this.store.findAll('spell');
return spells;
}
});
And the application controller:
//app/controllers/application.js
import Ember from 'ember';
export default Ember.Controller.extend({
selectedSpellIds: [],
actions: {
selectSpell(event){
const selectedSpellIds = Ember.$(event.target).val();
this.set('selectedSpellIds', selectedSpellIds || []);
},
addChar: function(){
var charName = this.get('mchName');
var _this = this;
var spells = this.get('selectedSpellIds');
var spellObjArray = spells.map(function(spellId){
return _this.store.peekRecord('spell', spellId );
});
var charToSave = this.store.createRecord('character', {
chName: charName,
chSpells: spellObjArray
});
charToSave.save();
},
}
});
And the application template:
//app/templates/application.hbs
Add new character <br>
name {{input value=mchName }}<br>
<br>
Choose Spells:<br>
<select multiple onchange={{action "selectSpell"}}>
{{#each model as |spell|}}
<option value={{spell.id}} selected={{include selectedSpellIds spell.id}}>{{spell.spName}}</option>
{{/each}}
</select>
<button {{action 'addChar'}}>add</button><br>

Disable button in Ember with return value of function

I've been trying to create an Ember Component with the latest version on Ember. Here is the part of the component I'm having trouble with though:
//app/templates/components/page-r.hbs
<div class="pager-container">
{{#each range as |page|}}
<button type="button" disabled={{isCurrentPage(page)}}>{{page}}</button>
{{/each}}
</div>
//app/components/page-r.js
import Ember from 'ember';
const PageR = Ember.Component.extend({
settings: Ember.computed('params.[]', function() {
return this.get('params')[0];
}),
range: [4,5,6],
currentPage: 1,
isCurrentPage(pageIndex) {
return this.currentPage === pageIndex;
},
});
PageR.reopenClass({
positionalParams: 'settings'
});
export default PageR;
There is more to the component than that, but my question is this:
Is it possible to make the disabled attribute update based off of the result of an action like I'm trying to do? If so, how?
The above code doesn't even compile, but I've tried a number of variations and haven't gotten any of them to work. I can get the disabled property to work based off a pure boolean value, but can't figure out how to get this to execute the function and utilize the boolean value that is returned from it to fuel the disabled attribute. Any help would be greatly appreciated!
You need to use another component to create the computed property.
// app/templates/components/page-r.hbs
<div class="pager-container">
{{#each range as |page|}}
{{page-button page=page currentPage=currentPage}}
{{/each}}
</div>
// app/templates/components/page-button.hbs
<button type="button" disabled={{isCurrentPage}}>{{page}}</button>
// app/components/page-r.js
import Ember from 'ember';
const { computed } = Ember;
const PageR = Ember.Component.extend({
settings: computed('params.[]', function() {
return this.get('params')[0];
}),
range: [4,5,6],
currentPage: 1
});
PageR.reopenClass({
positionalParams: 'settings'
});
export default PageR;
// app/components/page-button.js
import Ember from "ember";
const { computed } = Ember;
export default Ember.Component.extend({
tagName: 'span',
isCurrentPage: computed('currentPage', 'page', function() {
return this.get('currentPage') === this.get('page');
}),
});

Use ember-simple-validaiton to validate property on hasMany relation

this question is about particular plugin - ember-cli-simple-validation.
Lets assume that there is two models
User = DS.Model.extend({
name: DS.attr()
emails: DS.hasMany('email')
})
Email = DS.Model.extend({
address: DS.attr()
emails: DS.belongsTo('user')
})
and user dynamically create emails:
<button {{action 'createEmail'}}>Add Email</button>
{{#each model.emails as |email index|}}
{{input value=email.address}}
{{/each}}
is it possible to use ember-simple-validaiton in this situation to validate presence of address on each of the emails?
I tried to go with validateEach but getting an Error while processing route: profile self.get(...).forEach is not a function TypeError: self.get(...).forEach is not a function error on the https://github.com/toranb/ember-cli-simple-validation/blob/master/addon/mixins/validate.js#L66
controller:
import Ember from 'ember';
import {ValidationMixin, validateEach} from "ember-cli-simple-validation/mixins/validate";
var isLegit = function(address) {
return address && address.length > 3;
};
export default Ember.Controller.extend(ValidationMixin, {
emailAddressValidation: validateEach("model.emails", isLegit),
actions: {
createChildren: function(type) {
this.store.createRecord(type, {profile: this.get('model')});
},
});
template:
{{#each model.emails as |email index|}}
{{input value=email.address}}
{{#validation-error-field submitted=submitted field="address" model=mode.emails index=index validation="basic"}}invalid address{{/validation-error-field}}
{{/each}}
model:
import Ember from 'ember';
import DS from 'ember-data';
export default DS.Model.extend{
address: DS.attr(''),
kind: DS.attr(''),
contactable: DS.belongsTo('contactable', { polymorphic: true, inverse: 'emails', async: false }),
kindIsPrimed: false,
kindChanged: Ember.observer('kind', function () {
this.set('kindIsPrimed', true);
}),
addressIsPrimed: false,
addressChanged: Ember.observer('address', function () {
this.set('addressIsPrimed', true);
})
});
On the surface (without running the code) you might alter your hbs from this
{{#each model.emails as |email index|}}
{{input value=email.address}}
{{#validation-error-field submitted=submitted field="address" model=mode.emails index=index validation="basic"}}invalid address{{/validation-error-field}}
{{/each}}
to instead ...
{{#each model.emails as |email index|}}
{{input value=email.address}}
{{#validation-error-field submitted=submitted field="address" model=email index=index validation="basic"}}invalid address{{/validation-error-field}}
{{/each}}
In the validateEach scenario (for array validation) you need to set the model and index on each iteration. The model should still be the individual model (not the array itself).
Another example that's tested in the addon for reference
https://github.com/toranb/ember-cli-simple-validation/blob/master/tests/dummy/app/templates/many.hbs