Object changes when selected with setState - reactjs

I made a question earlier which is mostly confused nonsense
(Mongoose, array in object)
Now I Think I have narrowed it down to the following:
I have a list of drivers which has an Array of cars:
var driverSchema = new mongoose.Schema({
driver: String,
age: String,
cars: [{ type: Schema.Types.ObjectId, ref: 'Car' }]
});
module.exports = mongoose.model('Driver', driverSchema);
A driver-object can be selected to ChoosenDriver like so:
this.setState({choosenDriver:driver})
The problem is that the object Changes when selected. It changes from this:
drivers: array[3]
->0: {}
->1: {}
_v: 0
_id: "4242594395935934"
name: "Roger"
cars: Array[1]
to this:
choosenDriver: {..}
_v:0
_id: "235345353453"
name: "Roger"
cars:
->_proto_ :{..}
0: "4242594395935934",
_id: undefined
cars is no longer an Array when the driver is selected.
Anyone run into something similar maybe?
Update:
I pass the list of drivers to a Child Component like this:
<DriversList drivers={this.props.drivers}
In DriversList i select a driver like this:
(render-func should be enough to show you)
handleDriverClick: function(i) {
this.props.setChoosenDriver(this.props.drivers[i])
},
render: function(){
var self = this;
var drivers = this.props.drivers.map(function(driver,i){
return <li key={driver._id} onClick={self.handleDriverClick.bind(null, i)}> {driver.name} </li>;
});
And in the Parent :
setChoosenDriver: function(driver) {
this.props.setChoosenDriver(driver)
},
And finally in the GrandParent i set the state:
setChoosenDriver: function(driver) {
this.setState({choosenDriver:driver})
},
Update:
getInitialState: function() {
return {
drivers:[]
};
},
componentWillMount: function(){
var self = this;
request
.get(Driversurl)
.end(function(err, res){
self.setState({drivers: res.body});
});
},
Its an Array of drivers wtih objects like:
{"_id":"5607b0747eb3eefc225aed61","name":"Moore","__v":0,"cars":["5607b0747eb3eefc225aed61","5607b07a7eb3eefc225aed62","5606bf4b0e76916c1d1668b4","5607b07a7eb3eefc225aed62"]}

don't execute your request in componentWillMount this is really wrong , execute it in componentDidMount, see this for more details : bind(this) not working on ajax success function
Also I would parse you request result when you receive it, your problem may come from here
self.setState({drivers:JSON.parse(res.body)});

Related

Mongoose doesn't create subdocument from JSON array

I'm trying to write a JSON object that contains both first-level data along with arrays into MongoDB.
What happens instead is all first-level data is stored, but anything contained in an array isn't. When logging the data the server receives, I see the entire object, which leads me to believe there's something wrong with my Mongoose code.
So for example if I send something like this:
issueId: "test1",
issueTitle: "testtest",
rows: [
{order:1,data: [object]},
{order:2,data: [object]},
]
Only the following gets stored:
issueId: "test1",
issueTitle: "testtest",
lastUpdated: Date,
I have the following model for Mongo:
//model.js
var mongoose = require('mongoose');
var model = mongoose.Schema({
issueId : String,
issueTitle : String,
lastUpdated : {type: Date, default : Date.now},
rows : [{
order : Number,
data : [
{
title : String,
text : String,
link : String,
}
]
}]
});
module.exports = mongoose.model('Model', model);
And the routing code, where I believe the problem likely is:
//routes.js
const mongoose = require('mongoose');
const Model = require('./model.js');
...
app.post('/api/data/update', function(req, res) {
let theData = req.body.dataToInsert;
console.log(JSON.stringify(theData,null,4));
Model.findOneAndUpdate(
{issueId : theData.issueId},
{theData},
{upsert: true},
function(err,doc){
if(err) throw err;
console.log(doc);
});
});
As well, here's the part of the Angular controller storing the data. I don't think there's any problem here.
pushToServer = function() {
$http.post('/api/data/update',{
dataToInsert : $scope.dataObject,
}).then(function successCallback(res){
console.log("all good", JSON.stringify(res,null,3));
}, function errorCallback(res){
console.log("arg" + res);
});
}
Look at the first question in the mongoose FAQ:
http://mongoosejs.com/docs/faq.html
Mongoose doesn't create getters/setters for array indexes; without them mongoose never gets notified of the change and so doesn't know to persist the new value. The work-around is to use MongooseArray#set available in Mongoose >= 3.2.0.
// query the document you want to update
// set the individual indexes you want to update
// save the document
doc.array.set(3, 'changed');
doc.save();
EDIT
I think this would work to update all of the rows. I'd be interested to know if it does work.
let rowQueries = [];
theData.rows.forEach(row => {
let query = Model.findOneAndUpdate({
issueId: theData.issueId,
'row._id': row._id
}, {
$set: {
'row.$': row
}
});
rowQueries.push(query.exec());
});
Promise.all(rowQueries).then(updatedDocs => {
// updated
});

AngularJS and JSON returns undefined

Hi I have got a data in LocalStorage as JSON string:
[
{"Date":"28/04/2016","Time":"08:00","Title":"Title 1"},
{"Date":"28/04/2016","Time":"08:30","Title":"Title 2"}
]
And my module.factory looks like:
module.factory('$schedule', function() {
var schedule = {};
var result = JSON.parse(localStorage.getItem('myAgenda'));
schedule.items = [{
title: result.Title,
date: result.Date,
time: result.Time
}];
return schedule;
});
When I am trying to get data it returns undefined. When I try to get a specific object like:
console.log(result[0].Title);
It works fine and shows only the first element. I guess I missing each definition but don't know how to do it. Please help me to get all results in my schedule.items.
And I am passing the result as items into:
module.controller('ScheduleController', function($scope, $schedule) {
$scope.items = $schedule.items;
});
Many thanks.
You are trying to access fields in an array without mentioning wich array element you want to access. If you want to enumerate all agenda entries and add them to your array, it should look something like this:
module.factory('$schedule', function () {
var schedule = [];
var result = JSON.parse(localStorage.getItem('myAgenda'));
result.forEach(function (date) {
schedule.push({
title: date.Title,
date: date.Date,
time: date.Time
})
})
return schedule;
});
You should use .map over array, also add missing } in your last element of array.
var schedule = [];
//assuming result returns an array.
schedule = result.map(function(value){
return {
title: value.Title,
date: value.Date,
time: value.Time
};
})
Not familiar with module.factory, but it looks like result is an array of objects and you're accessing it like a single object when creating schedule.items.
You might have to iterate over the array and create an item per object in result.

Sorting a nested array from model in Ember?

So I have a model in Ember that is generating a hash with three objects. One of the objects is an array of objects with another array inside each object. I need to sort this innermost array, but I am having trouble doing so.
Here are my models.
App.Person = DS.Model.extend ({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
age: DS.attr('string'),
gender: DS.attr('string'),
innerMostArray: DS.hasMany('innerMostObject')
});
App.innerMostObject = DS.Model.extend ({
person_id: DS.belongsTo('person'),
attr1: DS.attr('string'),
attr2: DS.attr('string')
});
Here is my Route
App.NestedArrayRoute = Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
object1: this.store.find('object1', params.object1_id),
people: this.store.all('person'),
object3: this.store.all('object3')
});
},
afterModel: function(model, transition) {
model.people.forEach(function(item, index, enumerable){
var innerMostArray = item.get('innerMostArray');
var sortedArray = innerMostArray.sortBy('attr1', 'attr2');
});
model.people.update();
}
});
I know that I am nowhere near doing this right but I just don't know how to sort this nested array. I've seen examples of array controllers, but I don't know how to use one to sort this nested array. If anyone could give an example of how to do this it would be very helpful. Thank you.
I agree with Kalmans answer, but I suggest you do this sorting with built-in methods to save you trouble:
App.Person = DS.Model.extend({
name: DS.attr('string'),
fruits: DS.hasMany('fruit', {async: true}),
fruitSorting: ['title', 'color'],
sortedFruits: Ember.computed.sort('fruits', 'fruitSorting')
});
I forked his example here: http://emberjs.jsbin.com/manutu/1/edit?html,js,output
One way to do this is to create a computed property on the model as follows:
App.Person = DS.Model.extend({
name: DS.attr('string'),
fruits: DS.hasMany('fruit', { async: true }),
sortedFruits: function(){
var fruits = this.get('fruits');
return fruits.sortBy('title', 'color');
}.property('fruits.#each.title', 'fruits.#each.color')
});
Working example here

Merge local Backbone collection with server

I have a local Backbone collection:
var collection = new Backbone.Collection([ { greeting: "hi" }, { greeting: "bye" } ]);
I understand that when I run collection.fetch, Backbone will run collection.set on the results. I need to merge in the response from the server, however. Say the response is:
[ { id: "2", greeting: "hi", name: "Bob" } ]
I would like the resulting collection, after the merge, to be:
[ { id: "2", greeting: "hi", name: "Bob" }, { greeting: "bye" } ]
I understand Backbone already attempts to do some merging here, but if I set the example response above, no merge happens and a new model gets added instead. I assume this is because it merges by id, and here we do not have any ids (in the local collection). In this case, greeting is my unique identifier / key.
The reason I am trying to do this is because I have a local collection and I simply want to see what already exists from that collection (using the key greeting) and merge any findings in.
My solution:
feeds.fetch({
add: false,
remove: false,
merge: false,
data: params,
success: function (feeds, response) {
// Merge any matches
_.each(response.results, function (result) {
_.each(feeds.models, function (feed) {
// We have to `parse` the result before setting it, as Model#set does
// not automatically run `parse` (Collection#set does).
result = feed.parse(result)
if (feed.get('rssUrl') === result.rssUrl) feed.set(result)
})
})
cb(feeds)
}
})
You can tell backbone to use a different key for the id attribute on your model:
GreetingModel = Backbone.Model.extend({
idAttribute: "greeting"
});
GreetingCollection = Backbone.Collection.extend({
model: GreetingModel
});
http://backbonejs.org/#Model-idAttribute
Edit: I suppose you could use two separate collections for local and server side.
var localCollection = new Backbone.Collection([ { greeting: "hi" }, { greeting: "bye" } ]);
ServerCollection = Backbone.Collection.extend({
url: "/api/"
...
});
var serverCollection = new ServerCollection({});
serverCollection.on("reset", function() {
localCollection.each(function(localModel) {
var greeting = localModel.get("greeting");
serverModel = serverCollection.findWhere({greeting: greeting});
if(serverModel) {
localModel.set(serverModel.attributes);
}
});
});
serverCollection.fetch();

Getting models in nested collections in Backbonejs

I'm working in a collection that contains a model with collections of "itself". For example:
[{
id: 1
name: "John",
children: [
{
id: 32
name: "Peter",
children: []
},
{
id: 54
name: "Mary",
children: [
{
id:12,
name: "Kevin"
}
]
},
]
}]
Let say that I want to get the Kevin "user" by its Id. But all that I have is the "first collection". How can I do that?? And about setting a user within a collection? Another thing: Its possible to get all the Kevin "parents" from him? Like Mary and John?
Does anyone has come to a issue like that?
Thanks a LOT
Well I've made a recursive function on the User's Collection that seems to solved the problem for now ( the best of this is that I can use for retrieve a "deep" model and change it.). Something like that ( if someone has any suggestions, be free to give it a opinion ):
findUserById: function(id) {
var self = new Backbone.Collection(this.toJSON());
return thisCollection(id, this);
function thisCollection(id, collection, array) {
var innerSelf = collection || this;
var thisArray = array || [];
for(var i = innerSelf.models.length; i--;) {
if(innerSelf.models[i].get('id') == id) {
return [innerSelf.models[i]].concat([thisArray]);
}else {
if(innerSelf.models[i].get('children').length > 0) {
thisArray.push(innerSelf.models[i]);
return thisCollection(id, innerSelf.models[i].get('children'), thisArray);
}else {
innerSelf.remove(innerSelf.models[i]);
return thisCollection(id, self, []);
}
}
}
}
}
Basically I return an array with 2 items. The first is the record that I'm looking for and the second is an array with the parents of this user.
Underscore (which is a Backbone dependency, so you already have it) is great for this sort of thing; if you use its "map" function (which Backbone provides as a method on Collection) with its find function, you can do the following:
findPersonInPeopleCollection: function(nameWeAreLookingFor) {
function findChildren(person) {
if (!person.children) return [person];
var children = _.map(person.children, function(child) {
foundPeople.push(findChildren(child);
})
return _.flatten(children);
}
var allPeople = _.flatten(myCollectionOfPeople.map(findChildren));
return _(allPeople).find(function(person) {
return person.get('name') == nameWeAreLookingFor;
}
}
If you wanted to store the parents initially you could either add logic to your "Person" model class's initialize function, eg.
var Person = Backbone.Model.extend({
initialize: function() {
_.each(this.get('children'), function(child) {
child.parent = this;
}, this);
}
});
You could also do something similar by overriding your collection's add method, or adding an event handler to it that triggers after people get added.

Resources