Access Nested Backbone Collection - backbone.js

Below is what i am populating my collection with (FacetModels)
How do I access the AvailableValues[] array
"FacetModels":[
{
"FacetDisplayLabel":null,
"SelectedValues":[],
"AvailableValues":[],
"UnknownResults":0,
"ActionURI":null,
"FacetGroupName":"Category",
"FacetGroupFriendlyId":"SourceCategory",
"FacetGroupOrder":10,
"AllowSuggestions":false
},
This is my view, as you will see all i have access to is the array of FacetModels, I need to be able to pass FacetModels[0].AvailableValues.Name so I can display each category Name
CategoryListItemView = Backbone.View.extend({
tagName: "li",
className: "category",
initialize: function (options) {
this.template = _.template( $("#categorylist_template").html());
},
render: function () {
var category = this.model
console.log(category);
console.log(this.model.toJSON());
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
Display from console
a.Model {cid: "c2", attributes: Object, collection: r, _changing: false, _previousAttributes: Object…}
_changing: false
_events: Object
_pending: false
_previousAttributes: Object
attributes: Object
ActionURI: null
AllowSuggestions: false
AvailableValues: Array[8]
0: Object
ActionURI: "/api/search?firstname=thomas&firstname_variants=true&lastname=smith&region=all&sourcecategory=armed%20forces%20utf0026%20conflict"
Count: 8943
DisplayLabel: "Armed Forces & Conflict"
IsUnknown: false
Name: "Armed Forces & Conflict"
proto: Object
1: Object
2: Object
3: Object
4: Object
5: Object
6: Object
7: Object
length: 8
proto: Array[0]
FacetDisplayLabel: null
FacetGroupFriendlyId: "SourceCategory"
FacetGroupName: "Category"
FacetGroupOrder: 10
SelectedValues: Array[0]
UnknownResults: 0
proto: Object
changed: Object
cid: "c2"
collection: r
proto: Object

Inside your view the javascript array is available through this.model.get('AvailableValues'). If you need Available values to be a Backbone Collection, you can override parse to populate the AvailableValues property with a collection instead of an array.
There are some other SO questions that have examples of this:
backbone-js-fetching-a-more-complex-data-and-using-as-a-collection
how-to-override-backbones-parse-function

Related

Adding property in array. Add the last value in all array

"I'm adding a property on array while in a forEach loop. But when I do the console.log() the added value on each array is always the last value of the foreach loop."
deliveries data has location and I want to pass the location to pickupDetails.
deliveries: [{ //data of deliveries that I want to pass in pickupDetails
0: {Location: Korea},
1: {Location: Japan}
}]
let pickupDetails = this.state.pickupDetails;
//pickupDetails is only one object then It will become two since the deliveries has 2 objects
pickupDetails = {
name: "June"
}
this.state.deliveries.forEach((delivery, index) => {
pickupDetails.location = delivery.location;
console.log(pickupDetails)
})
the result of the console:
pickupDetails = {
name: "June"
location: "Japan" //this should be Korea since the first loop data is korea
}
pickupDetails = {
name: "June"
index: "Japan"
}
I think something missing from that code maybe because its dummy data. alright from my point of view you confused with javascript reference.
deliveries: [{ //data of deliveries that I want to pass in pickupDetails
0: {Location: Korea},
1: {Location: Japan}
}]
let pickupDetails = this.state.pickupDetails;
this.state.deliveries.forEach((delivery, index) => {
let newPickupDetails = JSON.parse(JSON.stringify(pickupDetails));
newPickupDetails.location = delivery.location;
console.log(newPickupDetails);
})
the result of the console:
pickupDetails = {
name: "Juan"
location: "Japan" //this should be Korea since the first loop data is korea
}
pickupDetails = {
name: "June"
index: "Japan"
}
JSON.parse and JSON.stringify we use for create new object instead of use reference. console log effected because it linked by reference so if you make it new object it will do.
Check variable newPickupDetails

Object changes when selected with setState

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)});

angularjs add item to object array

I have a set of array and object that look like this
`
var PaymentContent = "Payments":
[{
"Details": {
"PaymentType": "CreditCard",
"Amount": $scope.total,
"CCNAME": $scope.Form.CreditCard.FullName,
}
}]
Payments: Array[1]
0:Object
Details: Object
Amount: 5.99
CCNAME: null
PaymentType: "CreditCard"`
Now, how can i update that set of objects and array using angularjs?
desired output :
Payments: Array[1]
0:Object
Details: Object
Amount: 5.99
CCNAME: null
PaymentType: "CreditCard"
LastPayment: "04/11/2011"
Notice the lastpayment field.
Here is my code
var paymentDetails = {LastPayment : '04/11/2011', LastSignOn : '04/11/2011'}
fields = angular.extend({}, PaymentContent , paymentDetails);
Thanks!
You are setting an empty object as destination. This new object will receive the properties and values of the other 2 objects...without changing those other 2 source objects
If you want the array object to receive the updates remove the first argument ( the empty object)
angular.extend( fields.Payments[0].Details, paymentDetails);
This will update fields.Payments[0].Details with all of the properties and values in paymentDetails
Demo Fiddle
You can directly write below code:
Payments[0].LastPayment = "04/11/2011";

How to display all attributes of model subclass on first initialization?

Problem
When a child model is initialized for the first time, only defaults of the child are set as attributes.
When a second(and all subsequent) child is being initialized, the attributes of child display defaults of child and it's parent.
Fiddle
var Parent = Backbone.Model.extend({
defaults: {
name: "john",
lname: "smith",
age: 30,
language: "english",
location: "belgium"
}
});
var Child = Parent.extend({
defaults: {
hobby: "doing nothing",
age: 24,
occupation: "student"
},
initialize: function () {
this.constructor.__super__.initialize.apply(this, arguments);
_.defaults(this.defaults, this.constructor.__super__.defaults);
console.log(this.attributes);
}
});
attributes of child initialized for the first time :
var child1 = new Child();
child1.attributes :
hobby: "doing nothing"
age: 24
occupation: "student"
attributes of same Child class, initialized for the second time:
var child2 = new Child();
child2 attributes:
age: 24
hobby: "doing nothing"
language: "english"
lname: "smith"
location: "belgium"
name: "john"
occupation: "student"
Question
Why are not all defaults(child's and parent's) are being set as attributes when a child model is initialized for the first time ?
Because i've to display a Backbone.Collection inside a <ul> and every model's attributes are configurable through a html form inside each <li>. But because of this problem, i can't get to all attributes of the first model in the collection.
You're modifying the Child class's defaults object when the first object is instantiated, during its initialize method. At that point, the Backbone.Model constructor has already used defaults to fill in the attributes for that object, so it will only affect subsequent instantiations.
Take a look at Backbone.Model:
var Model = Backbone.Model = function(attributes, options) {
var defaults;
var attrs = attributes || {};
options || (options = {});
this.cid = _.uniqueId('c');
this.attributes = {};
_.extend(this, _.pick(options, modelOptions));
if (options.parse) attrs = this.parse(attrs, options) || {};
if (defaults = _.result(this, 'defaults')) {
attrs = _.defaults({}, attrs, defaults);
}
this.set(attrs, options);
this.changed = {};
this.initialize.apply(this, arguments);
};
initialize is the very last step, after the defaults have been set, so modifying defaults at that point won't do anything for the current object.
To get it to work how you want, modify defaults after you declare the class, rather than during initialize:
Child.prototype.defaults = _.defaults(Child.prototype.defaults, Parent.prototype.defaults);
Working example

In Backbone Relational how do I silence add:* events on fetch()

When I load in my model using .fetch() backbone Backbone Relational fires off an add:survey_question for each question it adds (which is the same thing that would happen if load something onto my survey_questions collection via .add(). Even worse, the add:survey_questions dont' respect the comparator on my survey_questions collections.
I would like a way to silence the add:survey_questions that happen
Console.log output when listening to all events on the Survey Model (SurveyBuilder) and Question model (embedded inside a SurveyQuestion model)
//START of .fetch() on the Survey Model
SurveyBuilder
["relational:change:survey_questions", child, Array[3], Object]
app.js:25338
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
Question
["add:survey_question", child, Backbone.Collection, Object]
app.js:25575
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
Question
["add:survey_question", child, Backbone.Collection, Object]
app.js:25575
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
Question
["add:survey_question", child, Backbone.Collection, Object]
app.js:25575
SurveyBuilder
["sync", child, Object, Object]
app.js:25338
//START OF Manual .add()
SurveyBuilder
["add:survey_questions", child, child, Object]
app.js:25338
SurveyQuestion
["change:question_number", child, 4, undefined]
app.js:25578
SurveyQuestion
["change", child, undefined]
app.js:25578
And here are my models:
m.Question = m.BaseModel.extend({
urlRoot: '/api/questions',
initialize: function(){
if(this['type']) {
this.set('type', this['type']);
} else if(this.get('type')) {
this.type = this.get('type');
}
m.BaseModel.prototype.initialize.apply(this, arguments);
},
defaults: {
question_text: '',
question_number: 1,
comment_field: true
},
calculateProperties: function(){
return {
'question_id': this.getID()
};
},
subModelTypes: {
'Numeric': 'APP.Models.NumericQuestion',
'MultipleChoice': 'APP.Models.MultipleChoiceQuestion',
'Checkboxes': 'APP.Models.CheckboxesQuestion',
'FreeText': 'APP.Models.FreeTextQuestion',
'TimePeriod': 'APP.Models.TimePeriodQuestions',
'Sorting': 'APP.Models.SortingQuestion',
'Grading': 'APP.Models.GradingQuestion',
'Slider': 'APP.Models.SliderQuestion',
'AgreeDisagree': 'APP.Models.AgreeDisagreeQuestion',
'YesNo': 'APP.Models.YesNoQuestion'
}
});
m.SurveyQuestion = m.BaseModel.extend({
defaults: {
'question_number': 1
},
relations: [{
type: Backbone.HasOne,
key: 'question',
relatedModel: 'APP.Models.Question',
includeInJSON: '_id',
reverseRelation: {
type: Backbone.HasMany,
key: 'survey_question',
includeInJSON: false
}
}]
});
m.Survey = m.BaseModel.extend({
urlRoot: '/api/surveys',
defaults: {
start_date: moment().format('YYYY-MM-DD'),
end_date: moment().add('weeks', 3).format('YYYY-MM-DD'),
title: moment().format('MMMM') + ' Economist Survey'
},
relations: [{
type: Backbone.HasMany,
key: 'survey_questions',
relatedModel: 'APP.Models.SurveyQuestion',
collectionType: 'APP.Collections.SurveyQuestions',
includeInJSON: true,
reverseRelation: {
key: 'survey',
includeInJSON: true
}
}]
});

Resources