I'm still trying to figure out how ember works and I want to have more info on managing multiple checkboxes in ember..
here's what I tried to do: http://jsbin.com/datojebu/2/edit
as you can see all checkboxes get selected and the checked function doesn't get called
what's the correct way of doing this?
Check this now. http://jsbin.com/datojebu/3/edit
{{#each genre in genres}}
{{input type="checkbox" name=genre.id checked=genre.isChecked}} {{genre.nom}}
{{/each}}
you have to add genre.isChecked else same isChecked will be binded to all checkboxes.
BTW if you want to have controller for each item, you can add ItemController in the array controller. Here is another example.
/* controllers */
App.AnimesController = Ember.ArrayController.extend({
itemController: 'anime'
});
Okay further to your additional questions, I've basically finished your app for you:
http://jsbin.com/datojebu/11/edit
App = Ember.Application.create();
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: '/api',
namespace: 'fr'
});
/* router */
App.Router.map(function() {
this.resource('animes');
});
App.AnimesRoute = Ember.Route.extend({
model: function() {
return this.store.find('anime');
},
setupController: function(controller, model) {
this._super();
this.store.find('genre').then(function(genres) {
controller.set('genres', genres);
});
controller.set('model', model);
}
});
/* models */
var model = DS.Model,
attr = DS.attr,
hasMany = DS.hasMany;
App.Genre = model.extend({
animes: hasMany('anime', {async: true}),
nom: attr('string')
});
App.Anime = model.extend({
nom: attr('string'),
parution: attr('number'),
synopsis: attr('string'),
likes: attr('number'),
auteur: attr('string'),
genres: hasMany('genre', {async: true})
});
/* controllers */
App.AnimesController = Em.ArrayController.extend({
genres: Em.A([]),
selectedGenreIds: Em.A([]), // a set of ids
selectedGenres: function() {
var genres = this.get('genres'),
selectedGenreIds = this.get('selectedGenreIds');
return genres.filter(function(genre) {
return selectedGenreIds.contains(genre.get('id'));
});
}.property('selectedGenreIds.#each', 'genres.#each'),
selectedAnimes: function() {
var allAnimes = this.get('model'),
selectedGenres = this.get('selectedGenres'),
filteredAnimes;
// for an anime to be shown, it has to have at
// least one of its genres selected
filteredAnimes = allAnimes.filter(function(anime) {
return anime.get('genres').any(function(animeGenre) {
return selectedGenres.contains(animeGenre);
});
});
return filteredAnimes;
}.property('model.#each', 'selectedGenres.#each', 'genres.#each')
});
App.GenreController = Em.ObjectController.extend({
needs: ['animes'],
isChecked: function(key, value) {
if(arguments.length > 1) {
// setter
var selectedGenreIds = this.get('controllers.animes.selectedGenreIds'),
thisId = this.get('id');
if(!selectedGenreIds.contains(thisId) && value) {
selectedGenreIds.addObject(thisId);
} else {
selectedGenreIds.removeObject(thisId);
}
}
// always return the value for the getter and the setter
return value;
}.property('controllers.animes.selectedGenreIds')
});
/* mockjax */
var animes = [
{
id: 1,
nom: 'Blah',
parution: 2014,
genres: [1, 3],
synopsis: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore, eveniet, ab pariatur omnis dolor sunt alias atque voluptate neque reiciendis maiores impedit quibusdam perferendis optio ratione expedita adipisci et. Cupiditate!',
likes: 206,
auteur: 'Moi :p'
}
],
genres = [
{
id: 1,
nom: 'action',
animes: []
},
{
id: 2,
nom: 'magie',
animes: [1]
},
{
id: 3,
nom: 'amour et amitier',
animes: []
},
{
id: 4,
nom: 'aventures',
animes: [1]
}
];
$.mockjax({
url: '/api/fr/animes',
responseTime: 750,
responseText: {
'animes': animes
}
});
$.mockjax({
url: '/api/fr/genres',
responseTime: 750,
responseText: {
'genres': genres
}
});
You need to do as CodeJack said...
Once you've done that, you use bindings to "know" which one is checked. That is to say you don't need to know it yourself, you just need to bind the correct values to the right spot.
Anyway, this jsbin should alleviate your issues... notice the console gets set with the value and triggered at the correct tiems/places.
http://jsbin.com/datojebu/6/edit
App = Ember.Application.create();
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: '/api',
namespace: 'fr'
});
/* router */
App.Router.map(function() {
this.resource('animes');
});
App.AnimesRoute = Ember.Route.extend({
model: function() {
return this.store.find('anime');
},
setupController: function(controller, model) {
this._super(controller, model);
this.store.find('genre').then(function(genres){
controller.set('genres', genres);
});
}
});
/* models */
var model = DS.Model,
attr = DS.attr,
hasMany = DS.hasMany;
App.Genre = model.extend({
animes: hasMany('anime', {async: true}),
nom: attr('string')
});
App.Anime = model.extend({
nom: attr('string'),
parution: attr('number'),
synopsis: attr('string'),
likes: attr('number'),
auteur: attr('string'),
genres: hasMany('genre', {async: true})
});
/* controllers */
App.GenreController = Em.ObjectController.extend({
isChecked: function(key, value) {
if(arguments.length > 1) {
// setter
console.log('huzzah' + this.get('id') + ' val: ' + value);
}
// always return the value for the getter and the setter
return this.get('model.isChecked');
}.property(),
actions: {
click: function() {
console.log("hi");
}
}
});
/* mockjax */
var animes = [
{
id: 1,
nom: 'Blah',
parution: 2014,
genres: [1, 3],
synopsis: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore, eveniet, ab pariatur omnis dolor sunt alias atque voluptate neque reiciendis maiores impedit quibusdam perferendis optio ratione expedita adipisci et. Cupiditate!',
likes: 206,
auteur: 'Moi :p'
}
],
genres = [
{
id: 1,
nom: 'action',
animes: []
},
{
id: 2,
nom: 'magie',
animes: [1]
},
{
id: 3,
nom: 'amour et amitier',
animes: []
},
{
id: 4,
nom: 'aventures',
animes: [1]
}
];
$.mockjax({
url: '/api/fr/animes',
responseTime: 750,
responseText: {
'animes': animes
}
});
$.mockjax({
url: '/api/fr/genres',
responseTime: 750,
responseText: {
'genres': genres
}
});
Related
I am new to Ember.js and I have an issue I can't understand.
When I directly go to index.html#/subjects/1, I have the following data loaded:
subject (1):
Id: 1 - Title: My subject
message (3):
Id: 1 - Html content: undefined - Creation date: undefined
Id: 2 - Html content: undefined - Creation date: undefined
Id: 3 - Html content: undefined - Creation date: undefined
user (2):
Id: 1 - Name: undefined - Email: undefined - ...
Id: 2 - Name: undefined - Email: undefined - ...
Id: 3 - Name: undefined - Email: undefined - ...
And I also have an error (Uncaught TypeError: Cannot read property 'get' of null) on the following line :
return (this.get('sender').get('id') == 1);
But when I go on a page that loads all my fixture data and then go to index.html#/subjects/1, everything works fine.
To help you, this is my app.js file of Ember:
App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Router.map(function() {
// ...
this.resource('subjects', function () {
this.resource('subject', {path: ':subject_id'});
});
});
// Some routes ...
App.SubjectRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('subject', params.subject_id);
}
});
// Some controllers ...
App.Subject = DS.Model.extend({
title: DS.attr('string'),
messages: DS.hasMany('message', { async: true }),
users: DS.hasMany('user', { async: true }),
messagesCount: function () {
return this.get('messages.length');
}.property('messages'),
unreadMessagesCount: function () {
return Math.floor(this.get('messages.length')/2);
}.property('messages'),
callsCount: function () {
return Math.floor((Math.random() * this.get('messages.length')));
}.property('messages'),
textListOfUsers: function () {
var res = '';
var users = this.get('users').toArray();
test = this.get('users');
for (i = 0; i < users.length; i++) {
var name = users[i].get('name');
res = res.concat(users[i].get('name'), ' & ');
};
res = res.substring(0, res.length-3);
return res;
}.property('users.#each.name'),
textListOfOtherUsers: function () {
var res = '';
var users = this.get('users').toArray();
test = this.get('users');
for (i = 0; i < users.length; i++) {
var name = users[i].get('name');
if (users[i].get('id') != 1)
res = res.concat(users[i].get('name'), ' & ');
};
res = res.substring(0, res.length-3);
return res;
}.property('users.#each.name')
});
App.Message = DS.Model.extend({
htmlContent: DS.attr('string'),
creationDate: DS.attr('string'),
subject: DS.belongsTo('subject'),
sender: DS.belongsTo('user'),
isFromConnectedUser: function () {
return (this.get('sender').get('id') == 1);
}.property('sender.id') // NOTE: Not sure about the syntax
});
App.User = DS.Model.extend({
name : DS.attr(),
email : DS.attr(),
bio : DS.attr(),
avatarUrl : DS.attr(),
creationDate: DS.attr(),
subjects: DS.hasMany('subject', { async: true }),
messages: DS.hasMany('message', { async: true })
});
App.Subject.FIXTURES = [{
id: 1,
title: 'My subject',
messages: [ 1,2,3 ],
users: [1,2]
}, {
id: 2,
title: 'Hello world',
messages: [ 4 ],
users: [1,2]
// Other data...
}];
App.Message.FIXTURES = [{
id: 1,
htmlContent: 'Message 1',
creationDate: '2015-06-16',
subject: 1,
sender: 1
}, {
id: 2,
htmlContent: 'Message 2',
creationDate: '2015-06-16',
subject: 1,
sender: 2
// Other data...
}];
App.User.FIXTURES = [{
id: 1,
name: 'Alice Bernard',
email: 'alice#bernard.com',
bio: 'Lorem ispum dolor sit amet in voluptate fugiat nulla pariatur.',
avatarUrl: 'images/default-user-image.png',
creationDate: '2015-06-16'
}, {
id: 2,
name: 'John Doe',
email: 'john#doe.com',
bio: 'Lorem ispum dolor sit amet in voluptate fugiat nulla pariatur.',
avatarUrl: 'images/default-user-image.png',
creationDate: '2015-06-16'
// Other data...
}];
Thank you for your help.
I have a router that returns data from the data model.
I want to use this data to bind it to a widget in a view.
Model:
myApp.Unit = DS.Model.extend({
active: DS.attr('boolean'),
allowdecimals: DS.attr('boolean'),
name: DS.attr('string'),
count: DS.attr('number'),
});
Router:
myApp.EunitsRoute = Ember.Route.extend({
model: function() {
return this.store.find('unit');
},
setupController: function(controller, model) {
this._super(controller, model);
controller.set('units', model);
},
actions: { ...
In the view I expect an object formated as follows:
[ {"id": 1,"active": true,"allowdecimals": true,"name": "Pint","count": 8},
{"id": 2,"active": true,"allowdecimals": true,"name": "Each","count": 8},
...]
What I am getting now in the view is an object:<DS.RecordArray:ember400>
View:
var source10 = {
datatype: "array",
datafields: [
{ name: 'id' },
{ name: 'name' },
{ name: 'allowdecimals' },
{ name: 'active' },
{ name: 'count' }
],
localdata: controller.get('units')
};
var dataAdapter10 = new $.jqx.dataAdapter(source10);
$("#eunits_grid").jqxGrid({
pageable: true,
theme: 'energyblue',
autorowheight : true,
rowsheight : 50,
pagesize: 10,
source: dataAdapter10,
....
Template:
<script type="text/x-handlebars" data-template-name="eunits">
<div class="col-md-12">
<h3 class="page-header">Edit Units</h3>
{{#if adding}}
{{view AddnewunitView}}
{{/if}}
{{view UnitsView id='eunits_grid'}}
</div>
</script>
I am trying to load an ember table using Ember-Data and Fixtures. I can see the rows generated in the table , however they are empty. Note that it does generate the correct number of rows, it just does not show data. If I use an array instead, everything works properly. Also, if I remove the table and just do {{#each}}, data shows just fine too.
Any ideas would be welcome!
Thanks
here is the html:
<div class="table-container">
{{table-component
hasFooter=false
columnsBinding="columns"
contentBinding="content"}}
</div>
And here is the controller:
App.CustomersController = Ember.ArrayController.extend({
numRows: 1,
columns: function() {
var nameColumn, typeColumn, phoneColumn, identifierColumn;
nameColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 150,
textAlign: 'text-align-left',
headerCellName: 'Name',
getCellContent: function (row) {
return row.name;
}
});
typeColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 150,
textAlign: 'text-align-left',
headerCellName: 'Type',
getCellContent: function (row) {
return row.type;
}
});
phoneColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 100,
textAlign: 'text-align-left',
headerCellName: 'Phone',
getCellContent: function (row) {
return row.phone;
}
});
identifierColumn = Ember.Table.ColumnDefinition.create({
columnWidth: 150,
textAlign: 'text-align-left',
headerCellName: 'ID',
getCellContent: function (row) {
return row.identifier;
}
});
console.log('in columns func');
console.log(this.get('content'));
return [nameColumn, typeColumn, phoneColumn, identifierColumn];
}.property(),
content: function() {
/*var temp= [
{
id: 1,
name: 'Seller',
type: 'Supermarket',
phone: '1-800-Sell',
identifier: '12345'
},
{
id: 2,
name: 'Sell',
type: 'Supermarket',
phone: '1-800-Sell2',
identifier: '12356'
}];
return temp;*/
return this.store.toArray
}.property('numRows')
});
(note the commented array that works is also included above)
And the model:
App.Customers = DS.Model.extend({
name: DS.attr('string'),
type: DS.attr('string'),
phone: DS.attr('string'),
identifier: DS.attr('string')
});
When you use an array, the objects are turned into real objects as opposed to being Ember.Object. You should be able to use:
getCellContent: function (row) {
return row.get('name');
}
To select the data on each row properly.
I'm trying to render mediaitems in my post template, but I'm getting this nasty console error:
Uncaught TypeError: Object photo has no method '_create'
These are my models & fixture data:
/**************************
* Models
**************************/
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.FixtureAdapter'
});
App.Mediaitem = DS.Model.extend({
type: DS.attr('string'),
url: DS.attr('string'),
post: DS.belongsTo('App.Post')
});
App.Post = DS.Model.extend({
type: DS.attr('string'),
title: DS.attr('string'),
summary: DS.attr('string'),
body: DS.attr('string'),
date: DS.attr('date'),
mediaitems: DS.hasMany('App.Mediaitem', {embedded:true})
});
App.Post.FIXTURES = [
{
id:"post-one",
type:"news",
title:"First Post",
summary:"Ipsum Lorem",
date:"2013-02-07T16:44:57",
mediaitems:[{
id:593,
post_id:"post-one",
type:'photo',
url:'http://www.google.com'
},
{
id:789,
post_id:"post-one",
type:'photo',
url:'http://www.google.com'
}]
},
{
id:"post-two",
type:"gallery",
title:"Second Post",
summary:"Lorem ipsum",
date:"2013-02-07T16:44:57",
mediaitems:[{
id:342,
post_id:"post-two",
type:'photo',
url:'http://www.google.com'
},
{
id:231,
post_id:"post-two",
type:'photo',
url:'http://www.google.com'
}]
}
];
This is my template code:
<script type="text/x-handlebars" data-template-name="post">
<div class="detail">
{{#linkTo posts}}close{{/linkTo}}<br/>
<h2>{{id}} - {{title}}</h2>
<br/>
{{#each mediaitem in mediaitems}}
print something
{{/each}}
</div>
</script>
Can someone help me out?
The FIXTURE adapter does not support embedded relationships, at least not in rev 11.
You need each model to have its own FIXTURE definition with the record and the relationships to have the id/s of the proper child/parent.
App.Post.FIXTURES = [
{
id:"post-one",
type:"news",
title:"First Post",
summary:"Ipsum Lorem",
date:"2013-02-07T16:44:57",
mediaitems:['593','789']
},
{
id:"post-two",
type:"gallery",
title:"Second Post",
summary:"Lorem ipsum",
date:"2013-02-07T16:44:57",
mediaitems:['342','231']
}];
App.Mediaitems.FIXTURES = [{
id:342,
post_id:"post-two",
type:'photo',
url:'http://www.google.com'
},
{
id:231,
post_id:"post-two",
type:'photo',
url:'http://www.google.com'
},
{
id:593,
post_id:"post-one",
type:'photo',
url:'http://www.google.com'
},
{
id:789,
post_id:"post-one",
type:'photo',
url:'http://www.google.com'
}];
I get this assertion when run the code below:
Emptying a view in the inBuffer state is not allowed and should not
happen under normal circumstances. Most likely there is a bug in your
application. This may be due to excessive property change
notifications.
Link to demo:
http://plnkr.co/edit/s3bUw4JFrJvsL690QUMi
var App = Ember.Application.create({
Store: DS.Store.extend({
revision: 4,
adapter: DS.FixtureAdapter.create()
}),
Router: Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: "/",
connectOutlets: function(router){
var person;
person = App.Person.find(657);
person.addObserver("isLoaded", function() {
return router.get('router.applicationController').connectOutlet("things", person.get("things"));
});
}
})
})
}),
ApplicationController: Em.Controller.extend(),
ApplicationView: Em.View.extend({
template: Em.Handlebars.compile("{{outlet}}")
}),
ThingsController: Em.ArrayController.extend({
thingTypes: (function() {
return App.ThingType.find();
}).property()
}),
ThingsView: Em.View.extend({
template: Em.Handlebars.compile([
'{{#each controller.thingTypes}}',
'{{this.name}}',
'{{/each}}',
'{{#each controller.content}}',
'{{this.title}}',
'{{/each}}'].join(""))
}),
//MODELS
Person: DS.Model.extend({
things: DS.hasMany('App.Thing', {
embedded: true
})
}),
Thing: DS.Model.extend({
description: DS.attr('string'),
thingType: DS.belongsTo("App.ThingType", {
embedded: true
}),
title: (function() {
return this.get("thingType.name");
}).property("description")
}),
ThingType: DS.Model.extend({
name: DS.attr("string")
})
});
App.Person.FIXTURES = [
{
id: 657,
things: [
{
id: 1,
description: "Some text",
thing_type: {
id: 1,
name: "type 1"
}
}, {
id: 2,
description: "Some text",
thing_type: {
id: 2,
name: "type 2"
}
}
]
}
];
App.ThingType.FIXTURES = [
{
id: 1,
name: "type 1"
}, {
id: 2,
name: "type 2"
}, {
id: 3,
name: "type 3"
}
];
Why is this happening?
I was having the same error while trying to load a list of dropdown values from fixtures. What resolved it was overriding queryFixtures on the fixture adapter:
App.FixtureAdapter = DS.FixtureAdapter.extend
latency: 200
queryFixtures: (records, query, type) ->
records.filter (record) ->
for key of query
continue unless query.hasOwnProperty(key)
value = query[key]
return false if record[key] isnt value
true
I probably wouldn't have figured it out had I not set the latency first. Then the error was a bit more descriptive.
a bit late I guess... but I got it to work here:
http://plnkr.co/edit/hDCT4Qy1h5aE6GjM76qp
Didn't change the logic but where its called
I modified your router like this:
Router: Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: "/",
connectOutlets: function(router) {
var person;
router.set('router.applicationController.currentPerson', App.Person.find(657));
}
})
})
})
And created an ApplicationController:
ApplicationController: Em.Controller.extend({
currentPerson: null,
currentPersonLoaded: function() {
this.connectOutlet("things", this.get("currentPerson.things"));
}.observes("currentPerson.isLoaded"),
})
I dont know if this is the output you wished but the bug vanished!