iterating over collection in backbone.js - javascript

var User = Backbone.Model.extend({
defaults : {
"students" : [
{
name : "abc",
grade: "A",
school: "xyz"
}],
"interest":
{
music : "yes"
}
}
});
var UserList = Backbone.Collection.extend({
model : User,
});
var obj = new UserList({"students" : [{name:"Rax", grade: "B", school : "javiers"}, {name:"John", grade: "C", school : "loreto"}], "interest" : {music: "yes"}});
for(var i = 0; i < obj.students.length; i++) {
console.log(obj.students.at(i).get("name"));
console.log(obj.students.at(i).get("grade"));
console.log(obj.students.at(i).get("school"));
}
//i want to iterate over each students detail(name, grade and school) ..but this is giving me error " obj.students is undefined " ..why this error is coming?

First of all, there are several syntax errors, so look into console and fix these. Also, something is conceptually wrong with the object you pass to collection.
Collection is a "user list", the model is one user. So, perhaps, you should change the defaults data description. It doesn't make sense to make students an array for a user.
Also, when you create an instance of Collection, you then create a model by using create method, and not by just passing in the hash. You could instatinate the Model that way, but not the Collection.
So when you want to create a user, you pass only one "student" in, perhaps something like this (you also have to put "url" key in your Collection, otherwise it won't work):
var User = Backbone.Model.extend({
defaults : {
"students" : [{
name : "abc",
grade: "A",
school: "xyz"
}],
"interest":{
music : "yes"
}
}
});
var UserList = Backbone.Collection.extend({
model : User,
// `url` is required, otherwise Backbone will throw an error
url : '/something'
});
var userList = new UserList();
var bob = userList.create({
students : [ {
name:'bob',
grade : 'a',
school : '123'
} ],
interest : { music : 'no' }
});
console.log( bob.get('students')[0].grade );
http://jsbin.com/elusey – here, hit "edit" and take a look at the code and in the console. You should see "a" as an output.
You could actually simplify the model to this (as I told, the data structure described above doesn't really make sense for a single user):
var User = Backbone.Model.extend({
defaults : {
name : "abc",
grade : "A",
school : "xyz"
interests : ["music"]
}
});
UPD. Actually, you don't have to use create collection method in order to add models to your collection. When you create an instance of your collection you can pass in an array of models, something like this (note the difference, in your code you pass an object to a collection, but you actually need to pass an array, even if with one object in it):
var userList = new UserList([{
name:'bob',
grade : 'A',
school : '123',
interests : [ "music" ]
},{
name:'george',
grade : 'B',
school : '321',
interests : [ "drugs" ]
}]);
You can find the working example that is using this way of adding models (don't forget to look into console) here: http://jsbin.com/elusey/2/edit

Are you sure a user 'has many' students? That doesn't make much sense. However, if that is the case, you should add a definition for a student model and collection of students. Than, your user model should contain a student collection, and some logic in the set() method to transform an array of students to a students collection. If you want to handle collections/models nested under a model more gracefully, take a look at Backbone-relational.

Related

Mongoose: How to extract arrays in subdocuments to a single array in the parent document

An Express server using Mongoose returns the following JSON from the database in one of its routes:
{
"gradeLevel": 12,
"classes": [
{
"className": "A",
"students": [
"student1", "student2","student3"
]
},
{
"className": "B",
"students": [
"student4", "student5","student6"
]
},
{
"className": "C",
"students": [
"student7", "student8","student9"
]
}
]
}
In the client, I'd like to be able to for example efficiently check if a student is in any of the class subdocuments in the parent document. The following are the solutions I've conceived, but am not sure which is best practice:
When a student is pushed to class subdocument, push the student to the parent's students field as well.
Add a new field in the model's toJSON transformation to compile all the students.
Use vanilla Javascript in the client to extract all students
I was at this problem too. But to tackle this you need a separate collection called allStudents for example where the schema of it is as follows
{ nameOfStudent: 'String' className: 'String' }
then aggregate it accordingly with subdoucment. So that wherever you push a new student into allStudents with the className mentioned it will be pushed to the students subdocument of the respective className.
Querying with dot notation, like
.find({"classes.students":"student8"})
will check the students field of each object in the classes array, returning the documents containing the specific student in any class.
Thank you for the answers given. Just sharing another solution using vanilla Javascript that might be of use to those in similar situations:
checkStudent = (batch, student) => {
let result = null
const inClass = (cls) => {
if (cls.students.includes(student)) {
result = cls
return true
}
return false
}
batch.classes.some(inClass)
return result
}

Live filtering of json

This Meteor client app "mostly smartPhone usage" needs to accept input from user in the form of typed text and filter json data of about 500 bottom nodes, giving back the branches of the tree where the bottom node text contains the user input text.
{
"people": {
"honour": [
[
"family"
],
[
"friends"
]
],
"respect": [
[
"one another"
]
]
},
"animals": {
"eat": [
[
"row food"
]
]
}
}
When the user inputs 'a', the code needs to give the tree where the occurrence exists:
people, honour, family.
people, respect, one another
When the user types 'o', output should be:
people, respect, one another.
animals, eat, row food.
When the user types 'oo', output should be:
animals, eat, row food.
when the user types 'f', output should be:
people, honour, family.
people, honour, friends.
animals, eat, row food.
My options are:
Converting the json to javascript object and write the seach/find/match logic with few loops.
Use defiantjs which I never used before and have to learn.
Import the json to mongodb and filter the database.
Whatever else you suggest.
Which would be best fit for fast results and easy of maintenance? Thanks
OK this question was an excuse for me to create a generic Object method Object.prototype.paths() to get all the paths within an object. In objects there are values many paths. Some values might be the same at the end of different paths. We will generate an object with the original object's values as properties and these properties' values are going to be the paths. Each value might have several paths so an array of strings array where each strings array will contain a single path to that value.
So once we have this tool to map the object values and paths, it becomes very easy to get your result.
Object.prototype.paths = function(root = [], result = {}) {
var ok = Object.keys(this);
return ok.reduce((res,key) => { var path = root.concat(key);
typeof this[key] === "object" &&
this[key] !== null ? this[key].paths(path,res)
: res[this[key]] == 0 || res[this[key]] ? res[this[key]].push(path)
: res[this[key]] = [path];
return res;
},result);
};
var data = {"people":{"honour":[["family"],["friends"]],"respect":[["one another"],["friends"]]},"animals":{"eat":[["row food"]]}},
paths = data.paths(),
values = Object.keys(paths),
keystr = document.getElementById("keystr");
getPaths = function(str){
var valuesOfInterest = values.filter(f => f.includes(str));
return valuesOfInterest.reduce((p,c) => p.concat({[c]: paths[c]}),[]);
};
keystr.oninput = function(e){
console.log(JSON.stringify(getPaths(e.target.value),null,2))
}
<input id="keystr" placeholder = "enter some characters" value = ""/>
So when you press "o" you will get the following
[
{
"one another": [
[
"people",
"respect",
"0",
"0"
]
]
},
{
"row food": [
[
"animals",
"eat",
"0",
"0"
]
]
}
]
Which means:
The outer array has 2 object items. This means that in the original
object there are two values with "o" character/s in it. "one another" and "row food",
"one another" has only one path ["people", "respect", "0", "0"]. If "one another" was listed at multiple places like "friends" listed both under "respect" and "honour" then this array would contain two sub arrays with paths. Type "fr" and see it for yourself.
A few words of warning: We should be cautious when playing with the Object prototype. Our modification should have the descriptor enumerable = false or it will list in the for in loops and for instance jQuery will not work. (this is how silly jQuery is, since apparently they are not making a hasOwnProperty check in their for in loops) Some good reads are here and here So we have to add this Object method with Object.defineProperty() to make it enumerable = false;. But for the sake of simplicity and to stay in the scope of the question i haven't included that part in the code.
I think a few loops would work just fine. In the example below, as you type in the input, the results matching your search are logged to the console.
$("#search").on("input", function() {
var result = [];
var search = this.value;
if (search.length) {
$.each(data, function(key1, value1) {
//key1: people, animals
$.each(value1, function(key2, value2) {
//key2: honor, respect, eat
$.each(value2, function(i, leaf) {
if (leaf.length && leaf[0].indexOf(search) >= 0) {
//found a match, push it onto the result
var obj = {};
obj[key1] = {};
obj[key1][key2] = leaf;
result.push(obj);
}
});
});
});
}
console.log(result);
});
var data = {
"people": {
"honour": [
[
"family"
],
[
"friends"
]
],
"respect": [
[
"one another"
]
]
},
"animals": {
"eat": [
[
"row food"
]
]
}
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input id="search" />
Use this package for meteor is awesome https://atmospherejs.com/matteodem/easy-search

How to search by object with nested object in AngularJS

I want to filter my orders collection by search object. want to show only matching order in view.
I have orders array collection like:
$scope.orders = [{
"_id" : "56461e2b7caaf49345076709",
"customer" : {"_id": "76461e2b7caaf49345076a19b", "name": "cr1"},
"seller" : {"_id": "96461e2b7caaf49345076a18b", "name": "sl1"},
"address" : "Squire Park",
"qt" : 5
},
{
"_id" : "56461e2b7caaf49345076708",
"customer" : {"_id": "76461e2b7caaf49345076a19b1", "name": "cr2"},
"seller" : {"_id": "96461e2b7caaf49345076a18c1", "name": "sl2"},
"address" : "Squire Park1",
"qt" : 6
},
..................
];
and my search object like:
$scope.search = {
"qt": 5,
"customer" : {"_id": "76461e2b7caaf49345076a19b1"},
"seller" : {"_id": "96461e2b7caaf49345076a18c1"}
};
view:
<tr data-ng-repeat="order in orders | myFilter:search">
<td>{{order._id}}</td>
<td>{{order.customer.name}}</td>
</tr>
What should be my myFilter function?
Thanks advance.
A rather simple solution would be attaching a filter function to the scope
$scope.orderFilter = function(order) {
// implementation depends on how you want the search to behave
// for example, if the order should match every property of the searchObj
var pass = true;
for (var property in $scope.search) {
if ($scope.search.hasOwnProperty(property)) {
if($scope.search[property] != order[property]) {
pass = false;
}
}
}
return pass;
// .. though i wouldnt recommend this implementation
// you´d be better off by implementing a more specific matching
}
And in the view
<tr data-ng-repeat="order in orders | filter:orderFilter">
<td>{{order._id}}</td>
<td>{{order.customer.name}}</td>
</tr>
If you want the filter-expression you are using to work, you have to register a custom filter on your module. This would be done like so:
angular.module('app').filter('myFilter', function() {
return function(orders, searchObj) {
var filteredOrders = [];
orders.forEach(function(order) {
// evaluate if you want to have the order show up based on the searchOjb
// .. if so, add it to the array like so:
filteredOrders.push(order);
});
return filteredOrders;
}
});
NOTE
If you change a property of you search object, your filter function wont be called again, because angular filters arent listening for changes of nested properties. The filter will only be called if you change the whole search object.

Finding nested array data with elemMatch in MongoDB/Meteor

collection structure as such:
{"_id" : "abc",
"potentialUsers" : [{"userID" : "def"},
{"userID" : "ghi"}]
},
{"_id" : "123",
"potentialUsers" : [{"userID" : "456"},
{"userID" : "789"}]
},
I want to query if user abc has the user def in their potential users array. My current query (client) is
getPendingLiftRequests: function() {
return collection.find({}, {potentialUsers: {$elemMatch: {userID: Meteor.user().services.facebook.id}}});
}
On the server I publish all of that collection to the user, and then just selectively show it based on the view of the client. However, when I try to use an elemMatch on the array, it just shows all of the records, when it should only show 1.
You don't need $elemMatch here. This should work:
var fId = Meteor.user().services.facebook.id;
return collection.find({'potentialUsers.userID': fid});
Have look at the documentation for minimongo in Meteor:
http://docs.meteor.com/#/full/find
You have to use the fields option to get the desired result.
getPendingLiftRequests: function() {
return collection.find({}, { fields: { potentialUsers: {$elemMatch: {userID: Meteor.user().services.facebook.id}}}});
}

Using objects to store a list of names

I was wondering how to use an object to store a list of different names and access them by simply using the key.
Do I have to use embedded object like this.
var f =
{
0 : { name : "John" },
1 : { name : "Phillip" }
};
console.log(f[1].name);
Do not over-complicate things. Why don't you just try a simple array?
var f = [
{ name : "John" },
{ name : "Phillip" }
];
console.log(f[1].name);
Why not just an array, which is indexed identically? Do you actually need a name: attribute for some reason?
var names = [ 'John', 'Phillip' ];
Instead of names[0].name, which is pretty redundant, you'd just use names[0]...
He wants to access them by key:
var people = {
John:{ age:33},
Bob :{ age:42}
};
Now you can truly access them by key:
console.log(people["John"].age);
or this way (although odd):
console.log(people.John.age);
No looping is necessary, you are using the power of javascripts associative syntax to index directly into your data structure. No need to refer to some arbitrary integer index either.
This definitely works but typically you'd use a more appropriate property to index than just a name like perhaps an employee id.
You can use like this
Var names = [
{ 'name' : 'ashwin', age: 18 },
{'name' : 'jhon', 'age' : 20 }
];
console.log ( names[0].name );

Categories

Resources