Backbone JS Nested Models with Backbone Relational - backbone.js

I am trying to implement Backbone with backbone-relational.
The Claim model:
define(['underscore', 'backbone', 'backbone-relational', 'models/User'], function(_, Backbone, relational, User) {
var Claim = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasOne,
key: 'a_user',
relatedModel: User
}],
defaults: {
},
initialize: function() {
},
clear: function() {
this.destroy();
this.view.remove();
}
});
return Claim;
});
The User model is a duplicate but with no relations set.
Below is the collection object:
Object
_byCid: Object
_byId: Object
_callbacks: Object
currentPage: 1
firstPage: 1
information: Object
length: 3
models: Array[3]
0: Object
1: Object
_callbacks: Object
_deferProcessing: false
_escapedAttributes: Object
_isInitialized: true
_pending: Object
_permitsUsed: 0
_previousAttributes: Object
_queue: Object
_relations: Array[1]
_silent: Object
attributes: Object
_deleted: false
_new: false
a_user: Object
_callbacks: Object
_escapedAttributes: Object
_isInitialized: true
_pending: Object
_permitsUsed: 0
_previousAttributes: Object
_queue: Object
_relations: Array[0]
_silent: Object
attributes: Object
_deleted: false
_new: false
already_in_save: false
already_in_validation: false
coll_claims: Array[0]
coll_claims_partial: true
created_at: "2012-12-12 09:00:00"
email: "cloud.strife#test.com"
firstname: "Cloud"
id: 2
lastname: "Strife"
modified_at: "2012-12-12 09:00:00"
modified_columns: Array[0]
start_copy: false
title: "Mr"
validation_failures: Array[0]
virtual_columns: Array[0]
__proto__: Object
changed: Object
cid: "c4"
collection: undefined
id: 2
__proto__: Object
already_in_save: false
already_in_validation: false
created_at: "2012-12-12 09:00:00"
fulfilment: "bank"
id: 2
manual: 0
modified_at: "2012-12-12 09:00:00"
modified_columns: Array[0]
promotion_id: 1
purchase_id: 2
start_copy: false
status: "pending"
user_id: 2
validation_failures: Array[0]
virtual_columns: Array[0]
__proto__: Object
changed: Object
cid: "c3"
collection: Object
id: 2
__proto__: Object
2: Object
length: 3
So essentially there are 3 Claim models in the collection and each Claim model has a nested model of User at the attribute key a_user.
The template looks like:
<% _.each( claims, function( item ){ %>
<tr>
<td><%= item.get("id") %></td>
<td><%= item.get("promotion_id") %></td>
<td><%= item.get("a_user").get("firstname") %></td>
<td><%= item.get("purchase_id") %></td>
<td></td>
<td><%= item.get("status") %></td>
<td><%= item.get("created_at") %></td>
</tr>
<% }); %>
However this results in the error:
TypeError: 'null' is not an object (evaluating 'item.get("a_user").get')
If I take it back to just item.get("a_user") it displays outputs [object Object]
I am just learning Backbone so any pointers are appreciated.

You shouldn't be using get within the template -- Underscore templates work with JSON, not Backbone model objects (you pass the model to the template using template(model.toJSON())), so they should be evaluated using basic dot-notation:
<tr>
<td><%= item.id %></td>
<td><%= item.promotion_id %></td>
</tr>

Related

ng-repeat to show output in separate columns

Can anyone direct me how to display values of array in separate columns with ng-repeat? Please note that fields are dynamically generated and I cannot hardcode name of fields like tran.id, or tran.firstname....
thanks
My html is:
<tr ng-repeat="tran in trans">
<td>{{ tran }}</td>
</tr>
My JS code is:
$scope.displayTrans = function(){
$http.get("model/selecttrans.php")
.then(function(response) {
console.log(response.data);
$scope.trans = response.data;
});
}
and my PHP code is:
<?php
$sql = "SELECT * FROM trans";
$stmt = $conn->prepare($sql);
$stmt->execute();
$total = $stmt->rowCount();
if ($total > 0 ) {
while ($row = $stmt->fetchObject()) {
$output[] = $row;
}
} else {
$output = 'No data';
}
echo json_encode($output);
I am getting following output in my console:
[…]
​
0: {…}
​​
"$$hashKey": "object:15"
​​
email: null
​​
firstname: "Aziz"
​​
id: "19"
​​
lastname: "Virani"
​​
password: "12345"
​​
__proto__: Object { … }
​
1: {…}
​​
"$$hashKey": "object:16"
​​
email: "test#test.edu"
​​
firstname: "Test"
​​
id: "32"
​​
lastname: "Result"
​​
password: "test"
​​
__proto__: Object { … }
​
length: 2
​
__proto__: Array []
and following output in my browser:
{"id":"19","lastname":"Virani","password":"12345","firstname":"Aziz","email":null}
{"id":"32","lastname":"Result","password":"test","firstname":"Test","email":"test#test.edu"}
Can any one suggest me how can I display output in separate columns as mentioned below:
id | lastname | password
32 | Result | test
Please ignore validation here like password should be hashed or md5 etc.....
I can easily get data by typing {{ tran.id }} or {{ tran.firstname }} but these fields are dynamically generated and i cannot hardcode fields name....
thank you
Aziz
So I figured out the way to display output....
Earlier I used nested ng-repeat with (key, value) with table row property but here on some other blog i found that nested ng-repeat works with table data so I updated my code and everything works cool....

It possible to show attributes from model to other attributes from model?

Hi i have a view with model attributes :
name: "P",
surname: "a",
sorters: ["name","surname"] // dynamical array ( parameter to show )
in template :
<% for(i=0 ;i<sorters.length(); i++ ){ %>
<h2><%= sorters[0] %></h2> // its <%= 'name' %> with quotes
<% } %>
as result i got
name,
surname
i need get
P,
a
As result i get values from Sorters[array] not model values:
Some examples
1.
name: "P",
surname: "a",
sorters: ["name"]
P
2.
name: "P",
surname: "a",
sorters: ["surname","name"]
a, P
With this code in template i dont have a values from models but string text from my array and my view instead of attributes from model show labels
Based on the fact that sorters returns a value which appears to be equal to another field in the model, you wish to dynamically return that field.
There's a few ways to do this, probably the best is to provide the variable option when creating the template (docs). See example below:
var model = new Backbone.Model({
name: "P",
surname: "a",
sorters: ["name","surname"]
});
var tmp = _.template($('#template').html(), {variable: 'data'});
$('#result').html(tmp(model.attributes));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<script id="template" type="text/template">
<% for(i=0; i < data.sorters.length; i++ ){ %>
<h2><%= data[data.sorters[i]] %></h2>
<% } %>
</script>
<div id="result"/>
Also, better to use each rather than a for loop:
<% _.each(data.sorters, function(sorter) { %>
<h2><%= data[sorter] %></h2>
<% }) %>

Storing and Returning an ObjectId in MongoDB (MEAN Stack)

I'm new to the MEAN stack and am having trouble with how to correctly link an ObjectId with MongoDB from my javascript. I am also trying to then get the name that corresponds to that ObjectId to show in my view.
The code is meant to make appointments in a schedule, where users are tied to each appointment, then the view shows information about each appointment (ie. each user's name).
Here is my Schema for the Schedule:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ScheduleSchema = new Schema({
description: String,
category: [String],
location: String,
startDate: { type: Date, default: Date.now },
endDate: { type: Date, default: Date.now },
participants: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
type: Schema.Types.ObjectId,
ref: 'User'
},
timestamp: { type: Date, default: Date.now },
active: Boolean
});
module.exports = mongoose.model('Schedule', ScheduleSchema);
And the Schema for the Users:
var UserSchema = new Schema({
name: String,
email: { type: String, lowercase: true },
role: {
type: String,
default: 'user'
},
hashedPassword: String,
provider: String,
salt: String
});
There's a lot more to that ^ file but I think the schema is the only relevant part.
And here is my controller code that passes fake testing info with 2 real user objectids (participants):
// Use our rest api to post a new meeting
$scope.addMeeting = function() {
$http.post('/api/schedules', { description: "Testing schedule adding", participants: ["547f03befccbd4f93874b547", "5481dcbf5dad7f735a766ad9"], category: "1", location: "Small Conference Room", startDate: new Date(), endDate: new Date(), timestamp: new Date(), active: true });
};
I've gotten that to post fine, but when I get all the appointments from the schedule and put them on the view, (maybe obviously) the ObjectId is displayed rather than the name, so this:
<table style="width:100%;">
<tr>
<td class="left" ng-class="'head'">Time</td>
<td ng-class="'head'">Attendees</td>
<td ng-class="'head'">Description</td>
<td class="right" ng-class="'head'">Location</td>
</tr>
<tr ng-repeat="meeting in meetings">
<td class="left" ng-class-even="'even'">{{ meeting.startDate | date:"h:mma" }} - {{ meeting.endDate | date:"h:mma" }}</td>
<td ng-class-even="'even'"><span ng-repeat="person in meeting.participants">{{ person }}<span ng-hide="$last">, </span></span></td>
<td ng-class-even="'even'">{{ meeting.description }}</td>
<td class="right" ng-class-even="'even'">{{ meeting.location }}</td>
</tr>
</table>
Turn into this: (I had an image posted but I don't have enough reputation points, so all the picture showed was the ObjectIds of the Users underneath the 'Participants' column instead of the names)
And I'm wondering how you're supposed to properly link things to be able to show the name instead of those ObjectIds.
Sorry for the novel, hope I put enough to make it clear.
As you are using Mongoose, use populate to load relations. In MongoDB you are storing user._id in schedule.participants, so when you list your schedules, or query for exact Schedule make sure you populate schedule.participants array.
http://mongoosejs.com/docs/populate.html
Example
Schedule
.findOne({ title: 'Once upon a timex.' })
.populate('participants')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
})
Also angular code should bind to name or email of user as needed.
{{ person.name }}

Access Nested Backbone Collection

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

How to create a generic table template for BackboneJS use

So my template thus far:
<script id="table-row" type="x-handlebars-template">
<tr>
{{#each this}}
<td>{{.}}</td>
{{/each}}
</tr>
</script>
And my render code:
that.collection.each(function(model){
var template = Handlebars.compile( $('#table-row').html() );
that.table.append(template(model.toJSON()));
});
Any my dummy data collection:
this.collection = new Backbone.Collection([
{name: "Tim", age: 5},
{name: "Ben", age: 26},
{name: "Rob", age: 55}
]);
Now, how can I create a template that can output as many <td>s as there are object keys in my Model? ie. I have 'name' and 'age' now, but what I add 'height', I have to specifically update this template, how can I create a generic table row template?
I think you'll need a custom Handlebars helper to iterate over object keys. I haven't used it but something like this should do the trick.
Here's the code from that link:
// HELPER: #key_value
//
// Usage: {{#key_value obj}} Key: {{key}} // Value: {{value}} {{/key_value}}
//
// Iterate over an object, setting 'key' and 'value' for each property in
// the object.
Handlebars.registerHelper("key_value", function(obj, fn) {
var buffer = "",
key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
buffer += fn({key: key, value: obj[key]});
}
}
return buffer;
});

Resources