I'm new to Ember.js and Firebase and I've managed to add a post and display it onto the screen but I'm stuck on being able to delete it, will anyone be able to help me?
////////////////////////////////////////////////////////////
// CONTROLLER
////////////////////////////////////////////////////////////
App.PostsController = Ember.ArrayController.extend({
init: function() {
this.set('post', Ember.Object.create());
},
sortProperties: ['timestamp'],
sortAscending: false, // sorts post by timestamp
actions: {
publishPost: function() {
var newPost = this.store.createRecord('post', {
title: this.get('post.title'),
body: this.get('post.body'),
timestamp: new Date()
});
newPost.save();
},
removePost: function(post) {
var post = this.get('post');
Promise.cast(post.get('post.title')).then(function(posts) {
posts.removeObject(post);
post.destroyRecord();
post.save();
});
}
}
});
////////////////////////////////////////////////////////////
// POSTS
////////////////////////////////////////////////////////////
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('post');
}
});
I've managed to fix it the updated code it below:
removePost: function(the_id)
{
console.log("Look, the record class is actually a promise! :D");
console.log(this.get('store').find('post', the_id));
console.log(this.get('store').find('post', the_id).toString());
this.get('store').find('post', the_id).then(function(rec) {
rec.destroyRecord();
});
}
I wasn't passing the id through in the index file, which weren't allowing me to delete the post. A rewrite of the removePost function also helped.
Related
Sorry if this question is too naive,but I am getting confused a lot on rendering views in Ember.
I have a 'Person' route. I am able to do CRUD operations on it.
router.js
this.route('person', function() {
this.route('index', { path: '' });
});
controllers/person/index.js
actions: {
createPerson: function() {
var person = this.get('store').createRecord('person');
this.set('person', person);
this.set('editPersonPane', true);
},
editPerson: function(person) {
this.set('person', person);
this.set('editPersonPane', true);
},
closeEditPerson: function() {
this.get('person').rollback();
this.set('editPersonPane', false);
},
savePerson: function(person) {
var _this = this;
person.save().then(function() {
_this.set('editPersonPane', false);
Ember.get(_this, 'flashMessages').success('person.flash.personUpdateSuccessful');
}, function() {
Ember.get(_this, 'flashMessages').danger('apiFailure');
});
},
deletePerson: function(person) {
var _this = this;
person.destroyRecord().then(function() {
_this.set('editPersonPane', false);
Ember.get(_this, 'flashMessages').success('person.flash.personDeleteSuccessful');
}, function() {
Ember.get(_this, 'flashMessages').danger('apiFailure');
});
}
}
What I want to do now is when I want to create a new person, a form slides in to create it. After filling up the form, I want the list view of persons to be updated immediately, without refreshing the page. Right now, I have been able to add the form and when I add a new person, I get a successful flash message but it's not updated in the view immediately. I have to refresh the page.
It might have to do something with observers but I am still not sure how.
Reloading a saved object will allow you to avoid having to refresh the page:
savePerson: function(person) {
var _this = this;
person.save().then(function(saved) {
saved.reload();
_this.set('editPersonPane', false);
Ember.get(_this, 'flashMessages').success('person.flash.personUpdateSuccessful');
}, function() {
Ember.get(_this, 'flashMessages').danger('apiFailure');
});
}
Also, it's worth noting that if you destructure and use ES6 syntax, you can clean up your code a bit as follows:
//controllers/person/index.js
//at the top of the file
import Ember from 'ember';
const { get, set } = Ember;
//other code
actions: {
//other actions
savePerson(person): {
person.save().then((saved) => {
saved.reload();
set(this, 'editPersonPane', false);
get(this, 'flashMessages').success('person.flash.personUpdateSuccessful');
}, () {
get(this, 'flashMessages').danger('apiFailure');
});
}
}
Which route is displaying your persons list?
Wouldn't something like this work better, so you can display the list and then edit a person within the persons.hbs outlet?
this.route('persons', function() {
this.route('person', { path: 'id' });
});
I am trying to set data from two models (that has hasMany & belongsTo relationship) and save them to firebase.
'list' data ends up being saved to firebase but not user data.
I think I'm doing something wrong at step 3. I'd appreciate your help!
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.find('list');
},
actions: {
createList: function() {
var newListTitle = this.controllerFor('lists').get('newListTitle');
var username = this.get('session.user.displayName');
alert(this.get('session.user.displayName'));
if (Ember.isBlank(newListTitle)) { return false; }
//1
var list = this.store.createRecord('list', {
title: newListTitle,
user: username,
});
//2
this.controllerFor('lists').set('newListTitle', '');
var _this = this;
//3
list.save().then(function(list) {
user.get('lists').addObject(list);
user.save();
_this.transitionTo('lists.show', list); //4
});
}
}
});
Restructured your adding logic as well as user defined models, also modified your route, which could look like this in Edit and View mode. Meaning you can have more than one item returned from "model".
// Update models
App.List = DS.Model.extend({
value: DS.attr('string')
});
App.User = DS.Model.extend({
name: DS.attr('string')
});
App.UserLists = DS.Model.extend({
user: DS.belongsTo('user'),
list: DS.belongsTo('list')
});
export default Ember.Route.extend({
LIST:SHOW ROUTE
model: function(params) {
var store = this.get('store');
var userPromise = store.find('user', params.id);
return Ember.RSVP.hash({
user: userPromise,
userList : userPromise.then(function(user) {
return store.find(userList, { WhereUserIdIs : user.get('id') })
});
});
},
actions: {
createList: function() {
var self = this;
var failure = function(reason) {
// handle stuff
};
var list = this.store.createRecord('list', {
title: this.get('title'),
});
var user = this.get('user');
var usersList = store.createRecord('userList', {
'user': user,
'list': list
});
list.save().then(function(list) {
user.save().then(function() {
userList.save().then(function() {
self.transitionTo('lists.show', list.get('id'));
}, failure);
}, failure);
}, failure);
}
});
Following this post, the scenario is the same outlined here, i.e. a list of posts, each having zero or more comments.
Suppose one has the following router.
App.Router.map(function() {
this.resource('posts', function() {
this.resource('post', { path: '/:post_id' }, function() {
this.resource('comments', function() {
this.route('create');
});
});
});
});
The model for a post is the following one.
App.Post = DS.Model.extend({
title: DS.attr('string'),
comments: DS.hasMany('comment', { async: true })
});
A post, for example, is the following one.
{
"post": {
"id": 1,
"title": "Rails is omakase",
"links": { "comments": "/posts/1/comments" }
}
}
This way, I am able to load the comments of this post using the following route.
App.CommentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('post').get('comments');
}
});
The question now is: how can I create the CommentsCreateRoute, so that the new comment is actually posted to /posts/1/comments and not to /comments?
So far I have the following.
App.CommentsCreateRoute = Ember.Route.extend({
model: function() {
var newComment = this.store.createRecord('comment');
newComment.set('title', 'test comment');
return newComment;
}
});
App.CommentsCreateController = Ember.ObjectController.extend({
actions: {
saveEditing: function(){
var newComment = this.get('model');
newComment.save().then(function(comment) {
/* OK saved */
}, function(response) {
/* show errors */
});
}
}
});
Does this REST scheme make sense?
GET /posts/{post_id}/comments
PUT /posts/{post_id}/comments/{comment_id}
POST /posts/{post_id}/comments
or should I use the following?
GET /posts/{post_id}/comments
PUT /comments/{comment_id}
POST /comments
or any other? Which would be the most "standard"?
I have a basic Rest API and when I visit my newPost page, there is a form I can fill in with 'title' and 'text' fields as well as a submit button. After I create the record, I save it with the controller and transitionToRoute('posts').
When I view the 'posts' page, the input I just entered is displayed twice. I checked the Ember inspector and it looks like the record is put into the data store twice, once as I click the create button and one after the page refreshes. When I refresh the browser again, the duplicate is removed.
How do I get rid of the duplicate record in the store? Cheers for any suggestions.
EDIT: I believe I have solved my issue by using a filter to remove posts that hadn't been persisted yet.
Updated Routes:
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
},
setupController: function() {
var posts = this.store.filter('post', function (post) {
return !post.get('isDirty');
});
this.controllerFor('posts').set('model', posts);
}
});
App.NewPostRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('post');
}
});
Routes:
/* Routes */
App.Router.map(function() {
this.resource('posts');
this.resource('newPost');
});
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
},
setupController: function() {
var posts = this.store.filter('post', function (post) {
return !post.get('isDirty');
});
this.controllerFor('posts').set('model', posts);
}
});
App.NewPostRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('post');
}
});
NewPostController:
actions:
createPost: function() {
...
var post = this.get('store').createRecord('post', {
title: title,
text: text
});
this.set('title', '');
this.set('text', '');
post.save();
this.transitionToRoute('posts');
...
newRecord: function() {
this.set('content', App.Post);
}
Thanks!
I have two models:
App.User = DS.Model.create({
comments: DS.hasMany('App.Comment')
});
App.Comment = DS.Model.create({
user: DS.belongsTo('App.User')
});
When a user is deleted, it also will delete all its comments on the backend, so I should delete them from the client-side identity map.
I'm listing all the comments on the system from another place, so after deleting a user it would just crash.
Is there any way to specify this kind of dependency on the association? Thanks!
I use a mixin when I want to implement this behaviour. My models are defined as follows:
App.Post = DS.Model.extend(App.DeletesDependentRelationships, {
dependentRelationships: ['comments'],
comments: DS.hasMany('App.Comment'),
author: DS.belongsTo('App.User')
});
App.User = DS.Model.extend();
App.Comment = DS.Model.extend({
post: DS.belongsTo('App.Post')
});
The mixin itself:
App.DeletesDependentRelationships = Ember.Mixin.create({
// an array of relationship names to delete
dependentRelationships: null,
// set to 'delete' or 'unload' depending on whether or not you want
// to actually send the deletions to the server
deleteMethod: 'unload',
deleteRecord: function() {
var transaction = this.get('store').transaction();
transaction.add(this);
this.deleteDependentRelationships(transaction);
this._super();
},
deleteDependentRelationships: function(transaction) {
var self = this;
var klass = Ember.get(this.constructor.toString());
var fields = Ember.get(klass, 'fields');
this.get('dependentRelationships').forEach(function(name) {
var relationshipType = fields.get(name);
switch(relationshipType) {
case 'belongsTo': return self.deleteBelongsToRelationship(name, transaction);
case 'hasMany': return self.deleteHasManyRelationship(name, transaction);
}
});
},
deleteBelongsToRelationship: function(name, transaction) {
var record = this.get(name);
if (record) this.deleteOrUnloadRecord(record, transaction);
},
deleteHasManyRelationship: function(key, transaction) {
var self = this;
// deleting from a RecordArray doesn't play well with forEach,
// so convert to a normal array first
this.get(key).toArray().forEach(function(record) {
self.deleteOrUnloadRecord(record, transaction);
});
},
deleteOrUnloadRecord: function(record, transaction) {
var deleteMethod = this.get('deleteMethod');
if (deleteMethod === 'delete') {
transaction.add(record);
record.deleteRecord();
}
else if (deleteMethod === 'unload') {
var store = this.get('store');
store.unloadRecord(record);
}
}
});
Note that you can specify via deleteMethod whether or not you want to send the DELETE requests to your API. If your back-end is configured to delete dependent records automatically, then you will want to use the default.
Here's a jsfiddle that shows it in action.
A quick-and-dirty way would be to add the following to your user model
destroyRecord: ->
#get('comments').invoke('unloadRecord')
#_super()
I adapted the answer of #ahmacleod to work with ember-cli 2.13.1 and ember-data 2.13.0. I had an issue with nested relationships and the fact that after deleting an entity from the database its id was reused. This lead to conflicts with remnants in the ember-data model.
import Ember from 'ember';
export default Ember.Mixin.create({
dependentRelationships: null,
destroyRecord: function() {
this.deleteDependentRelationships();
return this._super()
.then(function (model) {
model.unloadRecord();
return model;
});
},
unloadRecord: function() {
this.deleteDependentRelationships();
this._super();
},
deleteDependentRelationships: function() {
var self = this;
var fields = Ember.get(this.constructor, 'fields');
this.get('dependentRelationships').forEach(function(name) {
self.deleteRelationship(name);
});
},
deleteRelationship (name) {
var self = this;
self.get(name).then(function (records) {
if (!records) {
return;
}
var reset = [];
if (!Ember.isArray(records)) {
records = [records];
reset = null;
}
records.forEach(function(record) {
if (record) {
record.unloadRecord();
}
});
self.set(name, reset);
});
},
});
Eventually, I had to set the relationship to [] (hasMany) or null (belongsTo). Else I would have run into the following error message:
Assertion Failed: You cannot update the id index of an InternalModel once set. Attempted to update <id>.
Maybe this is helpful for somebody else.