Backbone.js + Relational + AMD ... bootstrapping relationals - javascript

I feel sort of lost and overlooking something, but i am not sure how to approach to this and even not very much sure how to ask...
First of all, i am using AMD approach (with curl.js library), which makes this probably more difficult, but i am not giving up on AMD because of this problem.
I have this structure of bootstrap data from the server, stored in 'window.bootstrap' property.
Departments = [
{"Id": 1, "Name": "Early Collections" },
{"Id": 2, "Name": "Collections" }
]
Blocks = [
{"Id": 1, "Code": "K", "Department": 1 },
{"Id": 2, "Code": "A", "Department": 2 }
]
Now i am confused about approach to this. Here is my 'DataModel/Block' module:
define [
'Collection/DepartmentCollection'
'DataModel/Department'
], (DepartmentCollection, Department) ->
Backbone.RelationalModel.extend
relations: [
type: Backbone.HasOne
key: 'Department'
relatedModel: Department
collectionType: DepartmentCollection
]
Module 'DataModel/Department' is just plain RelationalModel without any relations. Also every mentioned Collection here is also plain without anything but reference to Model like this:
define ['DataModel/Department'] , (Department) ->
Backbone.Collection.extend
model: Department
And finally, here goes Bootstrap module, which looks like this:
define [
'DataModel/Department'
'Collection/DepartmentCollection'
'DataModel/Block'
'Collection/BlockCollection'
] , (Department, DepartmentCollection, Block, BlockCollection) ->
model = Backbone.RelationalModel.extend
relations: [
type: Backbone.HasMany
key: 'Departments'
relatedModel: Department
collectionType: DepartmentCollection
,
type: Backbone.HasMany
key: 'Blocks'
relatedModel: Block
collectionType: BlockCollection
]
data = window.bootstrap || {}
boot = new model
boot.get('Departments').reset data.Departments || []
boot.get('Blocks').reset data.Blocks || []
return boot
I would expect from this, that it would find Departments for those Blocks and assign models there, but calling
console.debug ins.get('Blocks').at(0).get('Department')
...gets me undefined.
But this is not the end. I will be having other entities from server with relation to Department too. And i would like to see, it automatically attaches Department from that bootstrap, so i can use it transparently.
I don't know if i had just misunderstood this relational library, or it's not AMD ready. Any help is appreciated.

Potential scoping / name resolution problem? What output do you get for console.debug(window.Block, window.Department)? If you do get the model type, it might help to give the relatedModel as a string, e.g. relatedModel: "Department".

Looks it's solved. Problem was in one line of code...
Backbone.Model.prototype.idAttribute = "Id"
I forgot i am using PascalCase identifiers for object properties. Everything looks ok for now.

Related

Best practice for grouping an array of objects by an association's ID using JavaScript (ES6)?

I'm looking for a best practice on how to group an array by the object's associated ID. The data looks like this:
const data = [
{
"id": 1,
"association": {
"id": 1,
"name": "Association A"
},
"more_data": "...",
...
},
{
"id": 2,
"association": {
"id": 2,
"name": "Association B"
},
...
},
...
]
So the result would return:
{
1: [ { data_hash }, { data_hash }, ...],
2: [ { data_hash }, { data_hash }, ...],
...
}
Where 1 and 2 are the association IDs above. Ruby/Rails has built in methods to do this easily:
Data.group_by(&:association_id).each do |association_id, data|
puts association_id
data.each do |data|
puts data
end
end
Is there something similar or equivalent in the JavaScript/ES6 world, or can anyone offer some insight on how to best handle this? A library, or even just a function that would do the trick?
The other added challenge here is that the association's ID sits in that nested hash.
EDIT: Not opposed to using a library at all. Looking for recommendations for something lightweight.
It looks like lodash is the most lightweight library with a groupBy function:
https://lodash.com/docs/4.17.4#groupBy
The documentation there didn't make it very clear to me, but this SO question does:
using lodash .groupBy. how to add your own keys for grouped output?
And Vanilla version here using array reduce: https://stackoverflow.com/a/39635080/2202674 (thanks Kevin B)

How to get related object in angular JS template?

It should be noted that I'm very new to Angular.
I have the following JSON I get from Django Rest Framework:
api/movie/1
{
"id": 1,
"name": "Mr & Mrs Smith",
"actors": [
1,
2,
]
}
api/actor/1
{
"id": 1,
"name": "Angelina Jolie",
}
api/actor/2
{
"id": 2,
"name": "Brad Pitt",
}
I made a movie detail page following the Angular tutorial, using $resource.
This is the movie-detail.component.js
angular.
module('movieDetail').
component('movieDetail', {
templateUrl: 'static/partials/movie-detail/movie-detail.template.html',
controller: [ '$routeParams', 'Movie',
function MovieDetailController($routeParams, Movie) {
var self = this;
self.movie= Movie.get({movieId: $routeParams.movieId})
}
]
});
In the movie HTML template, I have access to the "actors" id via "$ctrl.movie.actors". But I can't figure out if there is a way to use this id to ask the server for the 'actor' object. Something like :
Actor.get({id}) to incorporate them in the movie details template.
TL;DR
What I can do now :
Actors:
<ul>
<ling-repeat="actor in $ctrl.movie.actors"> {{actor}} </li>
</ul>
result:
Actors
1
2
And I want something along the lines of :
Actors:
<ul>
<ling-repeat="actor in $ctrl.movie.actors"> {{actor.name}} </li>
</ul>
result:
Actors
Angelina Jolie
Brad Pitt
How can this be achieved ?
Thank you for any replies!
First of All you are using Angularjs 2 and Angularjs 2 is way different then Angularjs 1.*.
In this case its more javascript object manipulation rather Angularjs tricks
you can try below ways to achieve this
option1:
if you are able to edit your rest call in Django, change your code to return the movie and actor details in one js like this
{
"id": 1,
"name": "Mr & Mrs Smith",
"actors": [
1: {"id": 1,"name": "Angelina Jolie"},
2: {"id": 1,"name": "Angelina Jolie"},
]
}
And try to ng-repeat for you actor object under movie. or you can separate both the calls as your requirement.
option2:
You need to do a lazy loading at the time of loading. once your movie object is loaded, you need to call actor rest url to get all the actor object required and append them in the existing object if you are unable to make a single rest call.then in the template you can show actor detail as above.

Filtering Rows to show on Table

I have a JSON file with multiple 'rows', how can i just show the rows i need?
In my case, i need to show the Name and Score, also, i need the IP to show up when i click a user's name, and do not show any UselessData rows.
(I do not require code for all that, i just need to get the general idea)
JSON File
{
"records": [
{
"name": "AAAA",
"score": 100,
"IP" : "0.0.0.0",
"UselessData" : "Whatever"
},
{
"name": "BBBB",
"score": 50,
"IP" : "1.1.1.1",
"UselessData" : "Whatever"
},
[...]
],
}
I've been searching through de Documentation and a bit of the Source Code, but did not see anything like it, i may have just skipped it though, because it seems like a basic setting, but i just cannot figure it out.
If I understand correctly, you're just needing an array of object with three properties. I think, what you can do is just to map through your records and return object with the data you just need. Something like this
x.records.map(function(record) {
return {name: record.name, score: record.score, IP: record.IP};
});

JSON database module for Ruby/Sinatra? A JS lowDB alternative for Ruby/Sinatra?

I have to deal with a huge json like this acting as live datasource, is loaded every 5 min from a url..
sports: [
{
id: 200,
title: "Horse Racing",
meetings: [ ],
is_virtual: false,
events: [...],
pos: 83
},
{
id: 600,
title: "Tennis",
meetings: [ ],
is_virtual: false,
events: [
{
id: 301804310,
is_virtual: false,
outcomes: [
{
id: 32779738900,
description: "Brown/Pliskova",
},
{
id: 32779738900,
description: "Brown/Pliskova",
}]
}]
}]
And need to write methods like
getAllSports() returning an array object with all sports
getSport(sport_id) returning the object with this sport id
getAllEvents(Sport) returning all events list object of this sprot
getEvent(Sport, event_id) returning events that matches with given event_id
getOutcomes(Event, outcomes) ... and so on
Is there is a library that parses the json and already have methods some methods to help me to do this kind of stuff? example: obj.find(sport_id)...
In JS you have LowDB https://github.com/typicode/lowdb for this, any similar in Ruby/Sinatra? Or any approach suggestion? Im not using Rails.
Thanks in advice
You could always use Ruby's built in JSON library. You would be able to do something like
json_string = '{"name": "my name", "age": 5}'
object = JSON.parse(json_string)
object["name"] => "my name"
You can then use regular ruby hash / array functions on the returned object. In your case, you could do something along the lines of
def getSport(json_object, id)
json_object["sports"].select { | h | h["id"] == id }.first
end
Which, assuming you have already parsed the JSON and passed the resulting value into that function, would return the sport that had the given ID.

MongoDb: How to get a field (sub document) from a document?

Consider this example collection:
{
"_id:"0,
"firstname":"Tom",
"children" : {
"childA":{
"toys":{
'toy 1':'batman',
'toy 2':'car',
'toy 3':'train',
}
"movies": {
'movie 1': "Ironman"
'movie 2': "Deathwish"
}
},
"childB":{
"toys":{
'toy 1':'doll',
'toy 2':'bike',
'toy 3':'xbox',
}
"movies": {
'movie 1': "Frozen"
'movie 2': "Barbie"
}
}
}
}
Now I would like to retrieve ONLY the movies from a particular document.
I have tried something like this:
movies = users.find_one({'_id': 0}, {'_id': 0, 'children.ChildA.movies': 1})
However, I get the whole field structure from 'children' down to 'movies' and it's content. How do I just do a query and retrieve only the content of 'movies'?
To be specific I want to end up with this:
{
'movie 1': "Frozen"
'movie 2': "Barbie"
}
The problem here is your current data structure is not really great for querying. This is mostly because you are using "keys" to actually represent "data points", and while it might initially seem to be a logical idea it is actually a very bad practice.
So rather than do something like assign "childA" and "childB" as keys of an object or "sub-document", you are better off assigning these are "values" to a generic key name in a structure like this:
{
"_id:"0,
"firstname":"Tom",
"children" : [
{
"name": "childA",
"toys": [
"batman",
"car",
"train"
],
"movies": [
"Ironman"
"Deathwish"
]
},
{
"name": "childB",
"toys": [
"doll",
"bike",
"xbox",
],
"movies": [
"Frozen",
"Barbie"
]
}
]
}
Not the best as there are nested arrays, which can be a potential problem but there are workarounds to this as well ( but later ), but the main point here is this is a lot better than defining the data in "keys". And the main problem with "keys" that are not consistently named is that MongoDB does not generally allow any way to "wildcard" these names, so you are stuck with naming and "absolute path" in order to access elements as in:
children -> childA -> toys
children -> childB -> toys
And that in a nutshell is bad, and compared to this:
"children.toys"
From the sample prepared above, then I would say that is a whole lot better approach to organizing your data.
Even so, just getting back something such as a "unique list of movies" is out of scope for standard .find() type queries in MongoDB. This actually requires something more of "document manipulation" and is well supported in the aggregation framework for MongoDB. This has extensive capabilities for manipulation that is not present in the query methods, and as a per document response with the above structure then you can do this:
db.collection.aggregate([
# De-normalize the array content first
{ "$unwind": "$children" },
# De-normalize the content from the inner array as well
{ "$unwind": "$children.movies" },
# Group back, well optionally, but just the "movies" per document
{ "$group": {
"_id": "$_id",
"movies": { "$addToSet": "$children.movies" }
}}
])
So now the "list" response in the document only contains the "unique" movies, which corresponds more to what you are asking. Alternately you could just $push instead and make a "non-unique" list. But stupidly that is actually the same as this:
db.collection.find({},{ "_id": False, "children.movies": True })
As a "collection wide" concept, then you could simplify this a lot by simply using the .distinct() method. Which basically forms a list of "distinct" keys based on the input you provide. This playes with arrays really well:
db.collection.distinct("children.toys")
And that is essentially a collection wide analysis of all the "distinct" occurrences for each"toys" value in the collection, and returned as a simple "array".
But as for you existing structure, it deserves a solution to explain, but you really must understand that the explanation is horrible. The problem here is that the "native" and optimized methods available to general queries and aggregation methods are not available at all and the only option available is JavaScript based processing. Which even though a little better through "v8" engine integration, is still really a complete slouch when compared side by side with native code methods.
So from the "original" form that you have, ( JavaScript form, functions have to be so easy to translate") :
db.collection.mapReduce(
// Mapper
function() {
var id this._id;
children = this.children;
Object.keys(children).forEach(function(child) {
Object.keys(child).forEach(function(childKey) {
Object.keys(childKey).forEach(function(toy) {
emit(
id, { "toys": [children[childkey]["toys"][toy]] }
);
});
});
});
},
// Reducer
function(key,values) {
var output = { "toys": [] };
values.forEach(function(value) {
value.toys.forEach(function(toy) {
if ( ouput.toys.indexOf( toy ) == -1 )
output.toys.push( toy );
});
});
},
{
"out": { "inline": 1 }
}
)
So JavaScript evaluation is the "horrible" approach as this is much slower in execution, and you see the "traversing" code that needs to be implemented. Bad news for performance, so don't do it. Change the structure instead.
As a final part, you could model this differently to avoid the "nested array" concept. And understand that the only real problem with a "nested array" is that "updating" a nested element is really impossible without reading in the whole document and modifying it.
So $push and $pull methods work fine. But using a "positional" $ operator just does not work as the "outer" array index is always the "first" matched element. So if this really was a problem for you then you could do something like this, for example:
{
"_id:"0,
"firstname":"Tom",
"childtoys" : [
{
"name": "childA",
"toy": "batman"
}.
{
"name": "childA",
"toy": "car"
},
{
"name": "childA",
"toy": "train"
},
{
"name": "childB",
"toy": "doll"
},
{
"name": "childB",
"toy": "bike"
},
{
"name": "childB",
"toy": "xbox"
}
],
"childMovies": [
{
"name": "childA"
"movie": "Ironman"
},
{
"name": "childA",
"movie": "Deathwish"
},
{
"name": "childB",
"movie": "Frozen"
},
{
"name": "childB",
"movie": "Barbie"
}
]
}
That would be one way to avoid the problem with nested updates if you did indeed need to "update" items on a regular basis rather than just $push and $pull items to the "toys" and "movies" arrays.
But the overall message here is to design your data around the access patterns you actually use. MongoDB does generally not like things with a "strict path" in the terms of being able to query or otherwise flexibly issue updates.
Projections in MongoDB make use of '1' and '0' , not 'True'/'False'.
Moreover ensure that the fields are specified in the right cases(uppercase/lowercase)
The query should be as below:
db.users.findOne({'_id': 0}, {'_id': 0, 'children.childA.movies': 1})
Which will result in :
{
"children" : {
"childA" : {
"movies" : {
"movie 1" : "Ironman",
"movie 2" : "Deathwish"
}
}
}
}

Categories

Resources