I'd like to be able to modify/validate data before actually saving.
Model
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
date: DS.attr('date', { defaultValue: new Date() }),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
Route
App.PostsNewRoute = Ember.Route.extend({
model: function() {
return this.get('store').createRecord('post');
},
actions: {
doneEditing: function() {
debugger;
this.modelFor('postsNew').save();
this.transitionTo('posts.index');
}
}
});
So, the questions, before the .save() I want to, let's say, validate that the title is not empty or so.
Everything I've tried gets undefined, or [Object object] has no .val() method. I don't know how to get to the values of the model. How can I do that?
And the other thing I have in mind. Is that defaultValue working as intended? I want to set Date() to every new created post. Somehow date is not being recorded since it's not showing.
Thanks.
App.PostsNewRoute = Ember.Route.extend({
model: function() {
return this.get('store').createRecord('post');
},
actions: {
doneEditing: function() {
debugger;
var model = this.modelFor('postsNew');
var title = model.get('title');
model.save();
this.transitionTo('posts.index');
}
}
});
Related
I'm having trouble saving "hasMany" polymorphic records in Ember Data (1.0.0-beta.15). It looks as if Ember Data isn't setting the "type" property of the polymorphic relationship. Relationships in serialized records look like:
"roles": ["1", "2"]
When I expect them to look more like:
"roles":[{
"id": "1",
"type": "professionalRole"
}, {
"id": "2",
"type": "personalRole"
}
];
I see the following error in the console:
TypeError: Cannot read property 'typeKey' of undefined
If the records come back from the server in the expected format, all is well. The error only occurs when Ember Data creates the relationship.
I experience this using the FixtureAdapter, LocalStorageAdapter, and the RESTAdapter. I've read every piece of documentation I can find on the subject, but I cannot see my mistake.
I've created a CodePen to demonstrate the problem, but I'll also paste that code below.
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.FixtureAdapter;
App.Person = DS.Model.extend({
name: DS.attr(),
roles: DS.hasMany('role')
});
App.Role = DS.Model.extend({
title: DS.attr(),
person: DS.belongsTo('person', {
polymorphic: true
})
});
App.ProfessionalRole = App.Role.extend({
rank: DS.attr()
});
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
var person = this.store.createRecord('person', {
name: 'James'
});
var role = this.store.createRecord('professionalRole', {
title: 'Code Reviewer',
rank: 'Expert'
});
var promises = Ember.RSVP.hash({
person: person.save(),
role: role.save()
});
promises.catch(function() {
controller.set('initialSaveResult', 'Failure');
});
promises.then(function(resolved) {
controller.set('initialSaveResult', 'Success!');
var resolvedPerson = resolved.person;
var resolvedRole = resolved.role;
// Either/both of these break it
//resolvedRole.set('person', resolvedPerson);
resolvedPerson.get('roles').addObject(resolvedRole);
var innerPromises = Ember.RSVP.hash({
person: resolvedPerson.save(),
role: resolvedRole.save()
});
innerPromises.catch(function() {
controller.set('secondSaveResult', 'Failure');
});
innerPromises.then(function() {
controller.set('secondSaveResult', 'Success!');
});
});
}
});
App.ApplicationController = Ember.Controller.extend({
initialSaveResult: "Loading...",
secondSaveResult: "Loading..."
});
I'm updating a model using the following controller:
payment_controller.js
App.PaymentNewController = Ember.ObjectController.extend({
needs: ['student'],
isNew: true,
actions: {
createPayment: function() {
var date = new Date(this.get('date'));
if (!date) { return false; }
var amount = this.get('amount');
if (!amount) { return false; }
var studentId = this.get('controllers.student.id');
if (this.isNew)
{
var payment = this.store.createRecord('payment', {
date: date,
amount: amount,
});
}
else
{
var payment = this.get('model');
payment.set('date', date);
payment.set('amount', amount);
}
var self = this;
var onSuccess = function(payment) {
self.set('date', '');
self.set('amount', '');
self.transitionToRoute('student', studentId);
};
var onFail = function(payment) {
};
if (this.isNew)
{
this.store.find('student', studentId).then(function(student){
payment.set('student', student);
payment.save().then(onSuccess, onFail);
});
}
else
{
payment.save().then(onSuccess, onFail);
}
},
}
});
The data returned from the server is the following, and it looks in the correct format expected by ember:
{"payment":{"amount":1111,"date":"2014-09-09T00:00:00.000Z","student":"54024bb9cd52254e181325af","_id":"54024bdacd52254e181325b0"}}
The issue is that after saving the data is correctly saved to the database, but in the store is empty (UPDATE: except the id). As soon as I reload the page the data is shown correctly and is the updated version of the model. This made me think to the fact that the error could lie in the response of the PUT request, but it looks fine to me.
I've been trying many different things with no success. I can provide additional code or info if needed.
UPDATE 1
Here are the involved routes:
App.StudentsRoute = Ember.Route.extend({
model: function() {
return this.store.find('student');
}
});
App.StudentRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('student', params.student_id);
}
});
App.StudentEditRoute = Ember.Route.extend({
setupController: function(controller, model) {
this.controllerFor('student.new').setProperties({isNew:false, content:model});
},
renderTemplate: function() {
this.render('student/new');
}
});
App.PaymentNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('payment');
}
});
App.PaymentEditRoute = Ember.Route.extend({
setupController: function(controller, model) {
this.controllerFor('payment.new').setProperties({isNew:false, content:model});
},
renderTemplate: function() {
this.render('payment/new');
}
});
UPDATE 2
Here are the serializer and the adapter:
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:3000',
});
App.ApplicationSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
primaryKey: '_id',
serializeId: function(id) {
return id.toString();
},
attrs: {
payments: {embedded: 'always'},
lessons: {embedded: 'always'}
}
});
UPDATE 3
Here are the models:
App.Student = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
email: DS.attr('string'),
nationality: DS.attr('string'),
dateOfBirth: DS.attr('date'),
payments: DS.hasMany('payment'),
lessons: DS.hasMany('lesson'),
});
App.Payment = DS.Model.extend({
student: DS.belongsTo('student'),
date: DS.attr('date'),
amount: DS.attr('number'),
});
UPDATE 4
While inspecting the store using the Ember Inspector in Chrome I've noticed that after the update is completed, the only property that contains data in the payment object (in the store) is the id. Both date and amount are empty.
I have a controller for showing item.
Users can put the item in their wish list.
(Item has many users, User has many Items.)
So, when user enter the webpage, I want to show a AddToList or RemoveFromList button to the user based on isAddedToList property.
Below is the code.
User Model:
var User = DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
avatar: DS.attr('string'),
items: DS.hasMany("item", { async: true })
});
export default User;
ItemModel:
var Item = DS.Model.extend({
name: DS.attr("string"),
priceInCent: DS.attr("number"),
salePriceInCent: DS.attr("number"),
brand: DS.belongsTo("brand"),
itemImages: DS.hasMany("itemImage", { async: true }),
users: DS.hasMany("user", { async: true }),
});
export default Item;
ItemRoute:
var ItemRoute = Ember.Route.extend({
model: function(params) {
var userId = this.get("session").get("userId");
return Ember.RSVP.hash({
item: this.store.find('item', params.item_id),
user: this.store.find('user', userId),
});
},
setupController: function(controller, model) {
controller.set('item', model.item);
controller.set('user', model.user);
}
});
export default ItemRoute;
ItemController:
var ItemController = Ember.Controller.extend({
needs: ["current-user", "application"],
currentUser: Ember.computed.alias("controllers.current-user"),
isAddedToList: function() {
var promiseUsers = this.get("item.users"), user = this.get("user");
return promiseUsers.contains(user);
}.property("item"),
actions: {
addToList: function() {
var item = this.get("item"), user = this.get("user");
item.get("users").pushObject(user);
item.set("addedUserIds", [user.get("id")]);
item.save();
},
removeFromList: function() {
var item = this.get("item"), user = this.get("user");
item.get("users").removeObject(user);
item.set("removedUserIds", [user.get("id")]);
item.save();
}
}
});
export default ItemController;
The problem is when I check the length of promiseUsers with
promiseUsers.get("length")
it always returns 0.
but when I try the same with Chrome console, it returns the length properly.
Do I miss something in the route? How to fix the problem?
The problem is you're using your code synchronously, despite it being an asynchronous property.
The first time you attempt to use an async relationship it will begin resolving the relationship, making a callback to the server is necessary. In your case you try to use the users right away, but they are going to be empty the first time, so you're contains will return false. Since you aren't watching the users' collection it will then update, but the computed property won't update since the computed property was just watching item. This is why when you try it from the console it works, because by the time you attempt to use it in the console it's finished resolving the async collection of users.
isAddedToList: function() {
var promiseUsers = this.get("item.users"), user = this.get("user");
return promiseUsers.contains(user);
}.property("user", 'item.users.[]')
I have set up the following scaffolding for my Ember application.
window.App = Ember.Application.create({});
App.Router.map(function () {
this.resource('coaches', function() {
this.resource('coach', {path: "/:person_id"});
});
});
App.ApplicationAdapter = DS.FixtureAdapter.extend({});
App.Person = DS.Model.extend({
fname: DS.attr('string')
,lname: DS.attr('string')
,sport: DS.attr('string')
,bio: DS.attr('string')
,coach: DS.attr('boolean')
,athlete: DS.attr('boolean')
});
App.Person.FIXTURES = [
{
id: 10
,fname: 'Jonny'
,lname: 'Batman'
,sport: 'Couch Luge'
,bio: 'Blah, blah, blah'
,coach: true
,athlete: true
}
,{
id: 11
,fname: 'Jimmy'
,lname: 'Falcon'
,sport: 'Cycling'
,bio: 'Yada, yada, yada'
,coach: false
,athlete: true
}
];
I am trying to set up a route to filter the person model and return only coaches. Just to make sure I can access the data, I have simply used a findAll on the person model.
App.CoachesRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('person');
}
});
Now though, I am trying to implement the filter method detailed on the bottom of the Ember.js Models - FAQ page.
App.CoachesRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return store.filter('coaches', { coach: true }, function(coaches) {
return coaches.get('isCoach');
});
}
});
The coaches route is not working at all with the new route implemented and the old one commented out. I am using the Ember Chrome extension and when using the filter route the console responds with, Error while loading route: Error: No model was found for 'coaches'. Apparently the route is not working, specifically the model. No kidding, right? What am I missing in my filter model route?
Thank you in advance for your help.
The error message is spot on- there is no CoachModel. I think you need to do this:
App.CoachesRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return store.filter('person', { coach: true }, function(coaches) {
return coaches.get('isCoach');
});
}
});
In my project, I have the following three models:
Club Model: represents a sports club. It has one clubMeta and many customCourts.
ClubMeta Model: club related information relevant to the app.
CustomCourt Model: belongs to club. A club should have customCourts only if clubMeta.customCourtsEnabled is true.
Club
var Club = DS.Model.extend({
... bunch of properties ...
clubmeta: DS.belongsTo('App.Clubmeta'),
customCourts: DS.hasMany('App.CustomCourt')
});
module.exports = Club;
Clubmeta
var Clubmeta = DS.Model.extend({
... bunch of properties ...
customCourtsEnabled: DS.attr('boolean'),
club: DS.belongsTo('App.Club')
});
module.exports = Clubmeta;
CustomCourt
var CustomCourt = DS.Model.extend({
name: DS.attr('string'),
glass: DS.attr('boolean'),
indoor: DS.attr('boolean'),
single: DS.attr('boolean'),
club: DS.belongsTo('App.Club')
});
module.exports = CustomCourt;
What I need to do is a template where the club (which is the logged in user) can add customCourts only if clubmeta.customCourtsEnabled is true. As I was told in another SO question, I should be using an ArrayController to handle CustomCourts.
Everything is ok until this point, the problem comes because CustomCourtsController needs to know about club and clubmeta. I have tried this, with some variations to the binding path:
var ClubCourtsController = Ember.ArrayController.extend({
needs: ['currentClub'],
customCourtsEnabledBinding: Ember.Binding.oneWay("App.currentClubController.content.clubmeta.customCourtsEnabled"),
...
});
CurrentClubController
var CurrentClubController = Ember.ObjectController.extend({
init: function() {
this._super();
console.log('Retrieving club ' + App.clubId);
this.set('content', App.Club.find(App.clubId));
}
});
module.exports = CurrentClubController;
But ClubCourtsController.customCourtsEnabled always returns undefined. What is the right way to do this?
ClubCourtsController.customCourtsEnabled always returns undefined. What is the right way to do this?
You're on the right track. For starters the right way to do the binding is via the controllers property:
var ClubCourtsController = Ember.ArrayController.extend({
needs: ['currentClub'],
customCourtsEnabledBinding: Ember.Binding.oneWay("controllers.currentClub.clubmeta.customCourtsEnabled"),
});
Beyond that, it's generally best practice to wire things together in your route's setupController hook instead of a controller init. So even if you've got that binding right, could be other issues causing the customCourtsEnabled property to be undefined. I've posted a working example on jsbin here: http://jsbin.com/ubovil/1/edit
App = Ember.Application.create({
clubId: 1
});
App.Router.map(function() {
this.route("customCourts", { path: "/" });
});
App.Club = Ember.Object.extend({});
App.Club.reopenClass({
find: function(id) {
return App.Club.create({
id: id,
clubmeta: App.ClubMeta.create({
customCourtsEnabled: true
})
});
}
});
App.ClubMeta = Ember.Object.extend({});
App.CurrentClubController = Ember.ObjectController.extend({});
App.ApplicationController = Ember.Controller.extend({
needs: ['currentClub']
});
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('controllers.currentClub.content', App.Club.find(App.clubId));
}
});
App.CustomCourtsController = Ember.ArrayController.extend({
needs: ['currentClub'],
customCourtsEnabledBinding: Ember.Binding.oneWay("controllers.currentClub.clubmeta.customCourtsEnabled")
});
App.CustomCourtsRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', ['Court a', 'Court b', 'Court c']);
}
});
You need to access the controller via the controllers property
ClubCourtsController = Ember.ArrayController.extend({
needs: ['currentClub'],
customCourtsEnabledBinding: Ember.Binding.oneWay("controllers.currentClub.clubmeta.customCourtsEnabled"),
...
});