Looping through array of object data within Falcor - angularjs

Let's say I have the following routes:
{
route: "usersById['length']",
get: function(pathSet) {}
},
{
route: "usersById[{integers:ids}]['firstName', 'lastName']",
get: function(pathSet) {}
}
With the following in my angular1 controller:
Model.get(
'usersById.length',
'usersById[0..2]['firstName', 'lastName']'
).then(function(response) {
$scope.$apply(function() {
vm.entities = response.json.usersById;
});
});
The response from the server is going to look something like:
{
jsonGraph: {
usersById: {
"0": {
firstName: 'Jiminy',
lastName: 'Cricket'
},
"1": {
firstName: 'Jafar',
lastName: 'Husain'
},
"length": 123123
}
}
}
In my angular 1 template, I want to loop through the list of users:
<tr ng-repeat="entity in users.entities">
<td>{{entity.firstName}} {{entity.lastName}}</td>
</tr>
The problem is that there aren't just users in the response, firstly it contains length and secondly it seems other meta data is returned by Model's promise, of which looks to be part of the path data: usersById
What is the preferred way of looping through the list of users? Should I doing something like this in my promise?
vm.entities = response.json.usersById.filter(function(value) {
return typeof value === 'object';
});
I'm not seeing any API call for fetching raw values anywhere.

Ok so it seems the correct way to handle this is to create another route: users which just returns usersById references, that way you have an array of just entities, not containing length etc.
I'm guessing having the path data in the array of data was just a bug.
{
users: {...},
usersById: {...}
}
<li ng-repeat="user in users">{{ user.firstName }}</li>

Related

How can I get an item in the redux store by a key?

Suppose I have a reducer defined which returns an array of objects which contain keys like an id or something. What is the a redux way of getting /finding a certain object with a certain id in the array. The array itself can contain several arrays:
{ items:[id:1,...],cases:{...}}
What is the redux way to go to find a record/ node by id?
The perfect redux way to store such a data would be to store them byId and allIds in an object in reducer.
In your case it would be:
{
items: {
byId : {
item1: {
id : 'item1',
details: {}
},
item2: {
id : 'item2',
details: {}
}
},
allIds: [ 'item1', 'item2' ],
},
cases: {
byId : {
case1: {
id : 'case1',
details: {}
},
case2: {
id : 'case2',
details: {}
}
},
allIds: [ 'case1', 'case2' ],
},
}
Ref: http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
This helps in keeping state normalized for both maintaining as well as using data.
This way makes it easier for iterating through all the array and render it or if we need to get any object just by it's id, then it'll be an O(1) operation, instead of iterating every time in complete array.
I'd use a library like lodash:
var fred = _.find(users, function(user) { return user.id === 1001; });
fiddle
It might be worth noting that it is seen as good practice to 'prefer objects over arrays' in the store (especially for large state trees); in this case you'd store your items in an object with (say) id as the key:
{
'1000': { name: 'apple', price: 10 },
'1001': { name: 'banana', price: 40 },
'1002': { name: 'pear', price: 50 },
}
This makes selection easier, however you have to arrange the shape of the state when loading.
there is no special way of doing this with redux. This is a plain JS task. I suppose you use react as well:
function mapStoreToProps(store) {
function findMyInterestingThingy(result, key) {
// assign anything you want to result
return result;
}
return {
myInterestingThingy: Object.keys(store).reduce(findMyInterestingThingy, {})
// you dont really need to use reduce. you can have any logic you want
};
}
export default connect(mapStoreToProps)(MyComponent)
regards

ng-repeat based based on nested items

Based on the data below, and assuming there are more cars and more firebase user id's associated with specific cars. How would I ng-repeat only the car data that matches the current user. Example: if I was signed in and my Firebase.uid was simplelogin:26, how would I ng-repeat the car data that had my Firebase.uid within it? So in this case it would only display the Honda.
I have read that the way you organize Firebase data is really important so I'm not sure if this is formatted ideally, either way I would like to know if this is possible the way it's currently formatted. I still learning all this and can't seem to figure this out. Any insight would be great, thank you!
html
<h1>MyCars</h1>
<ul>
<li ng-repeat="car in cars">
<p>Make: {{ car.type }}</p>
<p>Year: {{ car.year }}</p>
<p>color: {{ car.color }}</p>
</li>
</ul>
JSON
"cars": {
"-JRHTHaIs-jNPLXOQivY": {
"type": "Honda",
"year": "2008",
"color":"red",
"simplelogin:26": {
"name":"ted"
},
"simplelogin:32": {
"name":"ted"
}
},
"-JRHTHaKuITFIhnj02kE": {
"type": "Chevy",
"year": "2006",
"color":"white",
"simplelogin:16": {
"name":"ted"
}
}
}
This is not an ideal data structure. If cars indeed have a 1:1 relationship with users, as the data suggests, then one should simply store them by user and then query that specific user id:
{
"cars": {
"ted": {
...
}
}
}
Now querying cars by user is extremely simple:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
$scope.cars = $firebaseArray(ref.child('cars/<USER ID>'));
If cars cannot be split by user, because they have an n:1 relationship, then a query can provide the same functionality (make sure you index them on the server):
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var query = ref.child('cars').orderByChild('name').equalTo('ted');
$scope.cars = $firebaseArray(query);
If you want to have an n:n relationship, then indexing users to cars is more appropriate:
"cars": {
"-JRHTHaIs-jNPLXOQivY": {
"type": "Honda",
"year": "2008",
"color":"red"
},
...
},
"owners": {
"ted": {
"-JRHTHaIs-jNPLXOQivY": true,
...
}
}
Fetching cars for a given user is now a bit more difficult, but still not unreasonable:
angular.factory('CachedCarList', function() {
// a simple cache of Firebase objects looked up by key
// in this case, a list of cars that have an n:n relationship to users
var carsRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/cars");
var carsLoaded = {};
return {
get: function(carId) {
if( !carsLoaded.hasOwnProperty(carId) ) {
carsLoaded[cardId] = $firebaseObject(carsRef.child(carId));
}
return carsLoaded[carId];
},
destroy: function(carId) {
angular.forEach(carsLoaded, function(car) {
car.$destroy();
});
carsLoaded = {};
}
}
});
angular.factory('CarList', function($firebaseArray, CachedCarList) {
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var CarsIndexed = $firebaseArray.$extend({
'$$added': function(snapshot) {
// when something is added to the index, synchronize the actual car data
// we use the $loaded promise because returning it here will make AngularFire
// wait for that data to load before triggering added events and Angular's compiler
return CachedCarList.get(snapshot.key()).$loaded();
},
'$$updated': function(snapshot) {
return false; // our cars update themselves, nothing to do here
}
});
return function(userId) {
// when a list of cars is requested for a specific user, we return an CarsIndexed
// than synchronizes on the index, and then loads specific cars by referencing their
// data individually
return new CarsIndexed(ref.child('owners/'+userId));
}
});
And firebase-util's NormalizedCollection can help make this process much simpler:
angular.factory('CarList', function($firebaseArray) {
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
return function(userId) {
var nc new Firebase.util.NormalizedCollection(
ref.child('owners/' + userId),
ref.child('cars')
).select('cars.type', 'cars.year', 'cars.color')
return $firebaseArray(nc.ref());
}
});
The Firebase Angular guide covers many topics like this one, and also introduces a bindings library to handle syncing remote/local data on your behalf.
Also, many topics such as data structures, indexing many-to-one or many-to-many relationships, et al are covered in the Firebase docs. I'd highly recommend reading the guide front to back before going any further.
You can iterate over the properties on an object in ng-repeat using the syntax ng-repeat="(key, value) in data"
<ul>
<li ng-repeat="(key, value) in cars">
<p>Make: {{ value.type }}</p>
<p>Year: {{ value.year }}</p>
<p>color: {{ value.color }}</p>
</li>
</ul>

$q.all(promises)and structure of promises object to collect the returned data

I am using Angularjs $q.all(promises) to make multiple REST call and then collecting the data once promise is successful. I have following following.
If "promises is simple array then it works Plunker
var promises = [
Users.query().$promise,
Repositories.query().$promise
];
If "promises" is simple object then also it works Plunker
var promises = {
users: Users.query().$promise,
repos: Repositories.query().$promise
};
If "promises" is nested object then it is not working. For my requirement I need nested object to remember the input parameters. Plunker
var promises = {
users: {"phx":Users.query().$promise},
repos: {"phx":Repositories.query().$promise}
};
These plunkr are just to simulate my problem. However I want this approach in real project for following requirement.
I have list of 12 product
Each product has "details", "benefits" and "offers" data
I have separate REST API services for "details", "benefits" and "offers" having :productID as parameter
I am making call in following order
a. Loop for each cards
b. For each card, make a REST API call for "details", "benefits" and "offers"
c. Add #b steps into "promises" object
d. call
$q.all(promises).then(function(results) {
// Here need logic to compile the result back to product
// and corresponding "details", "benefits" and "offers" mapping
}
and get the data back
Following is json structure I needed to collect my response.
{
"prod1": {
"benefits": {},
"offers": {},
"pages": {
"productPage": {}
}
}
},
"common": {
"benefits": {},
"pages": {
"commonBenefit": {}
},
"others": {}
}
How can I achieve this?
If you really need it, you can wrap the nest with $q.all like this:
var promises = {
users: $q.all({"phx": Users.query().$promise}),
repos: $q.all({"phx": Repositories.query().$promise})
};
plnkr.co

Meteor, MongoDB get part of array through subscription

I have a question about how to just get a certain element of an array using MongoDB and MeteorJS. I have the following schema for the user document:
bankList:[
{
id: "34567890987654345678",
name: "xfgchjbkn",
type: "credit"
},
{
id: "09876543456789098767"
name: "65789876t8",
type: "debit"
}
]
I first subscribe to only part of the fields in the array, specifically I gather a list of all the ids. Then I have an edit screen that should subscribe to all of the fields for a specific element in the array with a matching id. I do not want to expose the rest of the array just the single element. Currently, I use the following to first gather a list of just the ids:
Meteor.users.find({_id: this.userId},
{fields:{'bankList.id': 1}});
And the following publication-subscription method to get just a specific element's information:
Publication:
Meteor.publish("userBankAdvanced", function(bankId){
check(bankId,String);
if(this.userId){
return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {'bankList.$': 1});
}else{
this.ready();
}
});
Subscription:
this.route('edit_account', {
path: '/edit/account/',
waitOn: function(){
if(Session.get("bankId")){
return Meteor.subscribe('userBankAdvanced',Session.get("bankId"));
}
return null;
},
data: function(){
if(Session.get("bankId")){
return Meteor.users.findOne();
}
return null;
},
onBeforeAction: function(){
beforeHooks.isRevise(Session.get("bankId"));
}
});
The subscription method returns all of the elements of the array with all of the information.
I want, for example, just this (not the entire list with all of the information):
bankList:[
{
id: "34567890987654345678",
name: "xfgchjbkn",
type: "credit"
}]
It looks like you're just missing the "fields" specifier in your "userBankAdvanced" publish function. I wrote a test in meteorpad using your example and it seems to work fine. The bank id is hardcoded for simplicity there.
So instead of
return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {'bankList.$': 1});
try using
return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {fields: {'bankList.$': 1}});
No luck, in meteor the "fields" option works only one level deep. In other words there's no builtin way to include/exclude subdocument fields.
But not all is lost. You can always do it manually
Meteor.publish("userBankAdvanced", function (bankId) {
var self = this;
var handle = Meteor.users.find({
_id: self.userId, "bankList.id": bankId
}).observeChanges({
added: function (id, fields) {
self.added("users", id, filter(fields, bankId));
},
changed: function (id, fields) {
self.changed("users", id, filter(fields, bankId));
},
removed: function (id) {
self.removed("users", id);
},
});
self.ready();
self.onStop(function () {
handle.stop();
});
});
function filter(fields, bankId) {
if (_.has(fields, 'bankList') {
fields.bankList = _.filter(fields.bankList, function (bank) {
return bank.id === bankId;
});
}
return fields;
}
EDIT I updated the above code to match the question requirements. It turns out though that the Carlos answer is correct as well and it's of course much more simple, so I recommend using that one.

Breeze: getting collection of results that are wrapped in meta data object

I am attempting to use Breeze with AngularJS and a Web API back end which returns data in the following format:
API response format:
{
Count: 123,
Items: [
{
CustomerID: 1,
FirstName: "John",
Surname: "Smith",
// etc..
},
{
CustomerID: 2,
FirstName: "Bill",
Surname: "Jones",
// etc..
},
{
// 23 more Customer records...
}
],
NextPageLink: "http://localhost/web/api/customers?$skip=25"
}
I have manually set up the Customer entity in the metaDataStore following the example on the Breeze.js website http://www.breezejs.com/documentation/metadata-hand-depth:
function addCustomer() {
addType({
shortName: "Customer",
defaultResourceName: "customers",
dataProperties: {
CustomerID: { type: ID, isPartOfKey: true },
FirstName: { max: 50 },
Surname: { max: 50 },
// a bunch more properties
}
});
}
My code to query the "customers" endpoint looks like this:
function getCustomers(){
var customers = manager.getEntities('Customer');
return breeze.EntityQuery.from('customers')
.using(manager).execute()
.then(function(data){
return data.results; // breakpoint here is reached
});
}
The query successfully executes, as when I put a breakpoint where indicated, the data object is there but the data.results property is an array containing what looks like a single Customer entity that is empty. See screenshot:
I suspect that this is due to the fact the the back end is returning the collection as the Items property of a wrapper object.
My question then is: assuming I am unable to change the response from the back end API, how do I tell Breeze that my Customer entities are actually contained in the results.Items property?
You can build your own custom JsonResultsAdapter to do this.
See http://www.breezejs.com/documentation/mapping-json for more details.
We also have a sample that shows a custom adapter in action with a 3rd party back end.
Hope this helps.

Resources