I am pretty new to Backbone and just came across this confusing issue. I am trying to fetch models in to my collection in a Express node.js server with the following code :
app.get('/tweet', function(req,res){
res.send([{ name: 'random_name' }, {name: 'diren_gezi'}] );
});
and my backbone code looks like this:
var PostsApp = new (Backbone.View.extend({
Collections: {},
Models: {},
Views: {},
}
start: function(){
var data = {
posts: [
{name:"gorkem"},
{name: "janish"},
{name: "akash"}
]
};
var posts = new PostsApp.Collections.Posts(data.posts);
var postsView = new PostsApp.Views.Posts({collection: posts});
posts.url = "/tweet";
posts.fetch();
console.log(posts.length);
console.log(posts);
}
}))({el : document.body});
I would expect console.log(posts.length) to return 5, because I am adding 3 when I initialize and 2 more when I fetch. Or even if the fetch method erases the collection and re-populates, I would expect posts.length to return 2. However it returns 3, but when I look into the post object from the console I only see two models, the ones coming from the fetch() method. What is the reason fror this ?
It is because , when you do a fetch the collection is reset
So the previous contents are removed and refreshed with the 2 new ones. That is the reason you see a length of 2.
Pass merge: true, to see a length of 5 , where in it effectively merges the collection
posts.fetch({merge: false});
because you already have declared data, and when you do fetch you return 2 more.
var data = {
posts: [
{name:"gorkem"},
{name: "janish"},
{name: "akash"}
Related
I am trying to create multiple posts that belong to a user using Mirage js createList facility. I have created models with corresponding relationships:
models: {
user: Model.extend({
posts: hasMany(),
}),
post: Model.extend({
user: belongsTo()
})
}
In the seeds method, I am trying to create a list of posts and allocate them to a user with this code:
seeds(server) {
let posts = server.createList("post", 2);
server.create("user", {
name: "John",
posts: [posts],
});
}
Unfortunately when I hit this.get("/users"); in http request I receive a mirage error, which I understand but can't fix:
Mirage: You're trying to create a user model and you passed in "model:post(1),model:post(2)" under the posts key, but that key is a HasMany relationship. You must pass in a Collection, PolymorphicCollection, array of Models, or null.
As far as I can tell I am passing an array of Models? How can I fix it, please?
So the problem was:
posts: [posts]
This part returns an array (or collection) already:
let posts = server.createList("post", 2);
So wrapping it in another array [post] is incorrect.
Using server.create("...") we can hook onto it by putting it in array, but server.createList is already returning an array.
Correct syntax is:
seeds(server) {
let posts = server.createList("post", 2);
server.create("user", {
name: "John",
posts: posts,
});
}
or even shorter:
seeds(server) {
let posts = server.createList("post", 2);
server.create("user", {
name: "John",
posts,
});
}
I'm work with NodeJS and Sequelize. I have the following issue:
Read the Settings Table:
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
// Next request
});
I need to save the settings.device (Example) outside of the .then block.
But if I do that like
var device;
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
device = settings.device;
});
It doesn't work.
Already the error output undefined
The output in .then result block with console.log(settings.device); works perfect.
Update
I need it like:
var array = [];
// Get Settings from table
Settings.findOne({where: {
user_id: data
}})
.then(settings => {
// Get token from other Table
Example.findOne({where: {
user_id: data
}})
.then(example => {
// push to array
array.push({
"user_id" : data,
"device":settings.device, // output: settings.device is undefined
"token": example.token
});
});
});
// Send array to client
This is really a question of how to handle multiple resolved values in a Promise chain. You can search for that and see lots of great examples on how to handle it. For example, you could return an array or object in each then handler, or re-assign values to higher-scoped variables (as you're doing with settings). I've used both methods heavily in the past, and the resulting code is obviously inelegant and not fun to write.
However, async/await is readily available in Node and simplifies your code quite a bit:
const array = [];
// Get settings.
const settings = await Settings.findOne({where: {
user_id: data
}});
// Get token.
const example = await Example.findOne({where: {
user_id: data
}});
array.push({
user_id : data,
device: settings.device,
token: example.token
});
Sequelize return the model object you can get value by dataValue
console.log(settings.dataValues.device);
or if you want lean data
Settings.findOne({where: {
user_id: data,
raw:true,
}})
.then(settings => {
device = settings.device;
console.log(device);
});
I have parts of a state managed with the help of Immutable collections.
For example,
const FolderModel = Record({
id: null,
name: '',
items: List()
})
const DocsModel = Record({
folder1: new FolderModel({
id: 1,
name: 'Избранное'
}),
folder2: new FolderModel({
id: 2,
name: 'Отложенное'
}),
folder3: new FolderModel({
id: 3,
name: 'Топ тендеры'
})
})
const initialState = new DocsModel()
I also save my state in a localStorage, so the problem is when retrieve the state from localStorage I don't know how to convert JS object back to a nested collection (e.g Record containing a field that is a List).
I already tried using Immutable.fromJS() method but apparently it doesn't work for Records. Did anybody face the same problem? Please help resolving it
I've had success using the transit-immutable-js lib.
From the docs:
"Transit is a serialisation format which builds on top of JSON to provide a richer set of types. It is extensible, which makes it a good choice for easily providing serialisation and deserialisation capabilities for Immutable's types."
Example usage with Records:
var FooRecord = Immutable.Record({ a: 1, b: 2, }, 'Foo'),
foo = new FooRecord(),
serialize = transit.withRecords([FooRecord]),
json = serialize.toJSON(foo);
console.log(json); //=> json string of your data
I believe you need to serialize your collection with JSON.stringify() before setting it to localStorage. After getting it back from localStorage you can deserialize it with JSON.parse()
I'm in the process of learning FeathersJS and so far it seems like everything I wish Meteor was. Keep up the great work!
Right now I'm working through the Chat App tutorial but have run into some confusion. I don't quite understand what's going on in this section of the tutorial, specifically the populate hook in messages.hooks.js:
'use strict';
const { authenticate } = require('feathers-authentication').hooks;
const { populate } = require('feathers-hooks-common');
const processMessage = require('../../hooks/process-message');
module.exports = {
before: {
all: [ authenticate('jwt') ],
find: [],
get: [],
create: [ processMessage() ],
update: [ processMessage() ],
patch: [ processMessage() ],
remove: []
},
after: {
all: [
// What's the purpose of this ?
populate({
schema: {
include: [{
service: 'users',
nameAs: 'user',
parentField: 'userId',
childField: '_id'
}]
}
})
],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
},
error: {
all: [],
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: []
}
};
Here's process-message.js:
'use strict';
// Use this hook to manipulate incoming or outgoing data.
// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html
module.exports = function() {
return function(hook) {
// The authenticated user
const user = hook.params.user;
// The actual message text
const text = hook.data.text
// Messages can't be longer than 400 characters
.substring(0, 400)
// Do some basic HTML escaping
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
// Override the original data
hook.data = {
text,
// Set the user id
userId: user._id,
// Add the current time via `getTime`
createdAt: new Date().getTime()
};
// Hooks can either return nothing or a promise
// that resolves with the `hook` object for asynchronous operations
return Promise.resolve(hook);
};
};
I understand that before a create, update, or patch is executed on the messages service, the data is sent to processMessage() which sanitizes the data and adds the user ID to it.
Questions:
After processMessage(), is the data immediately written to the database?
After the data is written to the database, the after hooks are executed, correct?
What's the purpose of the populate hook then ?
Thanks :)
TO better understand how hooks and other cool stuff on feathers. It is good to do some basic logs. Anyways here is the flow.
CLIENT -> Before ALL hook -> OTHER BEFORE(create, update, ... ) -> Database -> ERROR hook (note: only if have error on previous steps) -> AFTER ALL hook -> OTHER AFTER(create, update, ...) -> FILTERS -> CLIENT
As for the populate hook, it does the same purpose of populate in your db. Feathers do it for you, instead of you doing the populate query.
Base on your example, you expect that in your schema to have something like this;
{
...,
userId : [{ type: <theType>, ref: 'users' }]
}
And you want to add another field, named user, then populate it with data from users service and match its _id with the userId.
The populate hook is one of the hooks provided by the feathers-hooks-common module. Its function is to provide data after joining the various tables in the database. Since each table is represented by an individual Service you can think of join happening between the service on which the populate hook is being called and another service.
Hence in the following piece of code a schema object is being passed to the populate hook:
populate({
schema: {
include: [{
service: 'users',
nameAs: 'user',
parentField: 'userId',
childField: '_id'
}]
}
})
It is basically telling the hook to include data from the 'users' service.
It is also telling it to name the additional data as 'user'.
It is saying that the parentField for the join (i.e. the field in the service which has the hook (in your case messages service) is 'userId'.
It is saying that the childField for the join (i.e. the field in the 'users' service is '_id'.
When the data is received, it will have all the fields from the messages table and an additional object with key user and key,value pairs from the users table.
I'm using backbone.js
For example, let's suppose we have a "products" model and a "categories" model which have a many-to-many relationship. In one of my views, say I need to retrieve a list of all categories and know whether or not each one is related to the current product model.
Do I set up a "category" collection and have it be a property of my model and somehow give it access to the id of the model so that when it is fetched, it only gets the categories that are related? And then I could fetch all categories and cross examine them to see which ones are related while still having the ones which are not?
I have no idea what the best way to do this would be. I'm used to using an ORM which makes it easy on the server-side.
Check out backbone-relational.
There is a simple & customizable solution for it, although it may not be as robust as backbone-relational.
Backbone.ModelWithRelationship = Backbone.Model.extend({
mappings: {},
set: function(attributes, options) {
_.each(this.mappings, function(constructor, key) {
var RelationshipClass = stringToFunction(constructor);
var model = new RelationshipClass();
/* New relational model */
if (!this.attributes[key]) {
this.attributes[key] = (model instanceof Backbone.Collection) ? model : null;
}
/* Update relational model */
if (attributes[key] && !(attributes[key] instanceof Backbone.Model || attributes[key] instanceof Backbone.Collection)) {
if (model instanceof Backbone.Model) {
this.attributes[key] = model;
this.attributes[key].set(attributes[key], options);
} else if (model instanceof Backbone.Collection) {
this.attributes[key].reset(attributes[key], options);
}
delete attributes[key];
}
}, this);
return Backbone.Model.prototype.set.call(this, attributes, options);
}
});
You can declare the mapping just by creating a subclass of Backbone.ModelWithRelationship.
Models.Post = Backbone.ModelWithRelationship.extend({
mappings: {
'comments': 'Collection.CommentCollection',
'user': 'Models.User'
}
});
http://pathable.github.com/supermodel/ is fantastic. It let's you do stuff like:
Post.has().many('comments', {
collection: Comments,
inverse: 'post'
});
Comment.has().one('post', {
model: Post,
inverse: 'comments'
});
var post = Post.create({
id: 1,
comments: [{id: 2}, {id: 3}, {id: 4}]
});
post.comments().length; // 3
var comment = Comment.create({id: 5, post_id: 1});
post.comments().length; // 4
comment.post() === post; // true :D
Assuming you are using a join table on the backend:
Create a collection and model containing all the rows on your join table and add the following methods to the collection: productsByCategory and categoriesByProduct (using [join collection].where(...).)
Having data in Backbone mirroring your data in the backend seems to help keep things simple and you won't have to do anything complicated when setting URLs.