Handling relationships in meteor-angular - angularjs

coming from a heavy MySQL and PHP background I can't really get my head over this.
I have a collection of branches with a ManyToOne relation to Restaurants (so there are many branches attached to the same restaurant).
Such relation is defined by the field restaurantId, which stores the id of the object. My branches collection appears as follows:
{
"_id" : "uH4KbNYxw8cnCEqvk",
"address" : "1 High Street",
"loc" : {
"coordinates" : [
0.0,
0.0
],
"type" : "Point"
},
"restaurantId" : "dvxZ2NiA3gbevffsk"
}
And of course the restaurants collection
{
"_id" : "dvxZ2NiA3gbevffsk",
"name" : "Restaurant name"
}
Now, I'm querying all the branches ordered by proximity using $near, and I can't find a clean way to get the name of the parent restaurants while looping over the branches.
Here is what I did - I mean, it works as it is, but I think performance-wise it has a lot to be improved
// controller
angular.module("MyApp").controller("SearchRestaurantsCtrl",
function($scope, $stateParams, $meteor){
$meteor.subscribe('branches');
$meteor.subscribe('restaurants');
$scope.branches = $meteor.collection(function() {
return Branches.find({
loc: {
$near: {
$geometry: {
type: "Point",
coordinates: [ $stateParams.lng, $stateParams.lat]
},
$maxDistance: 300000
}
}
});
});
$scope.getRestaurant = function(restaurantId) {
return Restaurants.findOne({
_id: restaurantId
});
};
}
);
// view
<div ng-repeat="branch in branches">
<h3>{{getRestaurant(branch.restaurantId).name}}</h3>
<address>{{branch.address}}</address>
</div>

I ended up using passing both the collection and filtering using angular:
Controller
angular.module("sushisushi24").controller("SearchRestaurantsCtrl",
function($scope, $stateParams, $meteor){
$scope.branches = $meteor.collection(Branches).subscribe('branchesAndRestaurants');
$scope.restaurants = $meteor.collection(Restaurants);
}
);
Meteor Publish
Meteor.publish('branchesAndRestaurants', function(opts) {
branches = Branches.find();
restaurantIds = branches.map(function(branch) { return branch.restaurantId });
return [
branches,
Restaurants.find({_id: {$in: restaurantIds}})
];
});
View
<div ng-repeat="branch in branches">
<div ng-repeat="restaurant in restaurants | filter: {_id:branch.restaurantId}">
<h3>{{restaurant.name}}</h3>
</div>
<address>{{branch.address}}</address>
</div>

Related

Firebase Ionic Nested Search

I'm trying to make a search function.
When a user selects a product, a list of products should be shown belonging to some storage place.
For so far, I can only show the array Products with all the items in it. But when I want to go deeper and loop over each items (to search items that match with the selected item), I got an error:
Error: No index defined for Products
This is my code:
controllers.js:
.controller('InventoryCtrl', function($scope, Category, Inventory) {
$scope.categories = Category;
$scope.searchInventory = function(category){
var word = category.Category;
//console.log(word);
var ref = new Firebase('https://vivid-heat-2430.firebaseio.com/');
var invenRef = ref.child('Inventories');
var prodRef = invenRef.child('Products');
invenRef.orderByChild("Products").on("child_added", function(snapshot) {
var data = snapshot.val();
var store = data.Inventory;
var prod = data.Products;
console.log(prod);
snapshot.forEach(function(childSnapshot) {
var test = childSnapshot.val();
console.log(test);
//var key = childSnapshot.key();
//console.log(key);
});
});
};
})
I’ve already defined my index. But I still got this error.
I also tried it like this in my controller:
$scope.searchInventory = function(category){
var word = category.Category;
var ref = new Firebase('https://vivid-heat-2430.firebaseio.com/');
var invenRef = ref.child('Inventories');
var prodRef = invenRef.child('Products');
invenRef.orderByChild("Products").equalTo(word).on("child_added", function(snapshot){
console.log(snapshot.key());
});
};
})
Here I got no errors, But I can't log anything to see.
This is my firebase structure:
Database structure
Can you please point out what I did wrong? I'm still new to this and trying to learn from my mistakes.
Thanks in advance!
EDIT
My rules.json
{
"rules": {
".read": true,
".write": true,
"Inventories": {
".indexOn": "Products"
}
}
}
JSON file of db structure:
{
"Categorie" : {
"-K5a3iGlgi7PS0m3O4y8" : {
"Category" : "IQ 33cl BOX",
"Optional" : false,
"Size" : "24"
},
"-K5a3vNZRc2Cur9964Xs" : {
"Category" : "CL 33cl BOX",
"Optional" : true,
"Size" : "24"
},
"-K5a40Q79SCqWqMbqWQu" : {
"Category" : "IQ 33cl CASE",
"Optional" : true,
"Size" : "24"
},
"-K5a464FON4qdnqE9rgf" : {
"Category" : "CL 33cl CASE",
"Optional" : false,
"Size" : "24"
},
"-K5a4TAzHE8cRGPbNeij" : {
"Category" : "Empty CASES",
"Optional" : false,
"Size" : "24"
}
},
"Inventories" : {
"17-12-2015Storage 1" : {
"Date" : 1450366396979,
"Inventory" : "Storage 1",
"Products" : {
"CL 33cl BOX" : {
"boxes" : 0,
"full" : 11,
"half" : 13
},
"IQ 33cl BOX" : {
"boxes" : 0,
"full" : 60,
"half" : 0
}
}
},
"17-12-2015Storage Deb" : {
"Date" : 1450367128198,
"Inventory" : "Storage Deb",
"Products" : {
"IQ 33cl CASE" : {
"boxes" : 0,
"full" : 2,
"half" : 14
}
}
}
},
"Storages" : {
"-K5a4esu-1Na1hkKMP47" : { "name" : "Storage 1" },
"-K5a4ihb9L5z6qSqQxAx" : { "name" : "Storage Deb" },
"-K5a4l9odWuPUJJuN8OR" : { "name" : "Storage Bart" },
"-K5a4nsc47N3k_hMVl2h" : { "name" : "Storage Debosz" }
}
}
index.html
<div style="max-height: 300px" ng-controller="InventoryCtrl">
<ion-list>
<ion-radio ng-repeat="category in categories" class="item-accordion" ng-model="checked.check" value="{{category.Category}}" ng-click="searchInventory(category)">
<p>{{category.Category}}</p>
</ion-radio>
<br>
</ion-list>
</div>
Your structure (according to the link)
Inventories
some_node_name_1
Inventory: xxxx
Products:
product_name: "product a"
product_price: 30
some_node_name_2
Inventory: xxxx
Products:
product_name: "product b"
product_price: 50
and your query path
var ref = new Firebase('https://vivid-heat-2430.firebaseio.com/');
var invenRef = ref.child('Inventories');
var prodRef = invenRef.child('Products');
(note prodRef doesn't appear to be used)
So how about:
var ref = new Firebase('https://vivid-heat-2430.firebaseio.com/Inventories');
ref.orderByChild("Products/product_name").on("child_added", function(snapshot) {
});
And the .indexOn rule should be at the same level as where you do the query (product_name).
Edit with more info:
A nodes key is indexed automatically so you don't need to add .index for the node Products (it's already done). However, you would add a index for a child of Products for example:
{
"rules": {
"Inventories": {
"Products": {
".indexOn": ["product_name"]
}
}
}
}
or (I believe this is another option)
{
"rules": {
"Inventories": {
".indexOn": ["Products/product_name"]
}
}
}
I've solved the problem by using Object.keys() to get the name of the products.
I also used a for loop to search for the matching product.
My controller looks like this now:
.controller('InventoryCtrl', function($scope, $firebaseObject, Category, Inventory) {
$scope.categories = Category;
$scope.searchInventory = function(category){
var word = category.Category;
var ref = new Firebase('https://vivid-heat-2430.firebaseio.com/');
var invenRef = ref.child('Inventories');
var prodRef = invenRef.child('Products');
var test22 = [];
invenRef.orderByChild("Products").on("child_added", function(snapshot) {
var data = snapshot.val();
var store = data.Inventory;
var test = Object.keys(prod);
for( var i = 0; i < test.length; i++){
if (test[i] == word) {
test22.push(data.Inventory);
};
};
$scope.show = test22;
});
};
})

How to select objects that have a certain atribute in a json file in an Angular JS project?

I have a link to an API which lists movies, each movie has title,id etc...
API: http://private-5d90c-kevinhiller.apiary-mock.com/angular_challenge/horror_movies
It also has an offers array inside which contains a 'provider_id' property.
I need to show how many movies are available at which provider.
But I dont know how, the best I came up with was:
{{movie.offers[0].provider_id}}
But that only shows the first provider on each movie.
What I want is to extract how many movies each provider has.
My Html:
<div ng-controller="ProviderController as provider">
<div ng-repeat="movie in provider.movies">
{{movie.offers[0].provider_id}}
</div>
</div>
My Main.js:
app.controller('ProviderController', function($http) {
var provider = this;
provider.movies = [];
$http({
url: 'some/path/horror_movies',
method: "GET",
}).success(function(data) {
provider.movies = data;
});
});
Also this is the structire of the json file:
[{
"id": 140524,
"title": "Dracula Untold",
"offers": [{
"provider_id": 2,
"retail_price": 14.99,
//...
}, {
"provider_id": 2,
"retail_price": 14.99,
}, {
"provider_id": 7,
"retail_price": 14.99,
}, ]
},
{
"id": 138993,
"title": "The Purge: Anarchy",
"offers": [
// ...
]
},
]
Any ideas ? Thanks.
You will need to change the structure of your database to have providers at the top level. A function achieving something like this should iterate over every movies offers, something like this:
function getProviders (movies) {
var providers = [],
movie = {},
offer = {};
for (var i = 0, max = movies.length; i < max; i += 1) {
movie = movies[i];
for (var j = 0; j < movies[i].offers.length; j += 1) {
offer = movie.offers[j];
if (typeof providers[offer.provider_id] === 'undefined') {
providers[offer.provider_id] = { movies: [] };
}
providers[offer.provider_id].movies.push(movie);
}
}
return providers;
}
I started a fiddle here: http://jsfiddle.net/d29tu8fg/1/
Before adding a movie to a provider, you might also want to check, if that movie already exists. You only need to do that though, if – like in your data – a movie can have two different offers from the same provider.
Add the providers to the $scope and in your view show the length:
<div ng-repeat="provider in providers track by $index">
{{provider.movies.length}}
</div>

how to check the result returned by filter in null or not and if it is null need to call a function in the controller

The result of list returned by the filter test_id needs to be checked.If the list is null need to call a function in the controller.
<div class="test_list">
<ul>
<p> testID :{{test_id}}</p>-->
<li ng-repeat="test in test_list | filter: test_id">
{{test}}
</li>
</ul>
</div>
Thanks in advance.
Add a watch over the list variable in your controller and filter it programatically and call the necessary function if found null.
$scope.$watch("test_id", function(new_test_id) {
//inject $filter service in your main controller
var filtered_test_val=$filter('filter')($scope.test_val, new_test_id, false);
if(angular.isUndefined(filtered_test_val) || filtered_test_val== null)
{
//call your function here.
}
}
You just need a custome filterfor this :-)
<div ng-app="myapp">
<div ng-controller="test">
<input type="text" ng-model="search">
<div ng-repeat="data in myData | filterData:search">
{{ data.ID }}
{{ data.Message }}
</div>
</div>
</div>
angular.module('myapp',[])
.controller('test',function($scope){
$scope.myData = [
{
"ID" : "001",
"Message" : "test test test test"
},
{
"ID" : "002",
"Message" : "test test test test"
},
{
"ID" : "003",
"Message" : "test test test test"
},
{
"ID" : "004",
"Message" : "test test test test"
},
{
"ID" : "005",
"Message" : " "
},
{
"ID" : "006",
"Message" : "test test test test"
},
{
"ID" : "007",
"Message" : "test test test test"
},
{
"ID" : "007",
"Message" : null
}
];
})
.filter('filterData',function(){
return function(data,search) {
if(!search || search.length<3){
return data;
}
var dataToBePushed = [];
for(var i=0;i<data.length;i++){
if(data[i].ID==search){
dataToBePushed.push(data[i]);
}
}
if(dataToBePushed.length==0){
alert("My function");
}else{
return dataToBePushed;
}
}
});
PS:- I checked the search after length is 3 but it may help you to clear the idea and you can manupilate it according to your requirement.
Fiddle
You can make use of the built-in filter filter in your own custom defined filter.
.filter('checknull', function($filter) {
return function(x, test) {
var result = $filter("filter")(x, test);
if (result.length <= 0)
myFunc();
return result;
}
});
Fiddle Here

how can I add groups under an active/inactive child?

I have successfully been able to send data to the firebase server however I am having trouble getting the data to send to the appropriate data subset I have created. When I send data made in the html form, It sends organized by ID number. I need it to be sent as a child to the 'groups' category in firebase.
here is a Plnkr with the server and $add working. Any suggestions I would really appreciate!
http://plnkr.co/edit/LZ24sRoSJjuCHQnEGzQz?p=linter
.controller('MainCtrl', ['$scope', 'groupsService', function( $scope, groupsService, $firebase ) {
$scope.newGroup = {
name: '',
status: ''
};
$scope.addGroup = function(newGroup) {
groupsService.addGroup(newGroup);
$scope.newGroup = {
name: '',
status: ''
};
};
$scope.updateGroup = function (id) {
groupsService.updateGroup(id);
};
$scope.removeGroup = function(id) {
groupsService.removeGroup(id);
};
}])
.factory('groupsService', ['$firebase', 'FIREBASE_URI',
function ($firebase, FIREBASE_URI) {
var ref = new Firebase(FIREBASE_URI);
var groups = $firebase(ref).$asArray();
var getGroups = function(){
return groups;
};
var addGroup = function (newGroup) {
console.log(newGroup)
groups.$add(newGroup);
};
var updateGroup = function (id){
groups.$save(id);
};
var removeGroup = function (id) {
groups.$remove(id);
};
return {
getGroups: getGroups,
addGroup: addGroup,
updateGroup: updateGroup,
removeGroup: removeGroup,
}
}]);
Thanks for responding! What I am trying to do is add dummy data (name and status) to the groups category like this:
{
Groups:[
"-JcFXid1A2G8EM7A_kwc" : {
"name" : "hi",
"status": "inactive"
},
"-JcFZP5FNtL4Yj6nja_7" : {
"name" : "hi"
"status": "inactive"
},
"-JcFtGoZL7J-CCIjTYcL" : {
"name" : "dfgdfg",
"status": "inactive"
}
]
}
would it make more sense to have them organized by active or inactive? I am afraid to nest too far in firebase...
like
{
Groups:[
"Active":[
"-JcFXid1A2G8EM7A_kwc" : {
"name" : "hi",
}
],
"Inactive":[
"-JcFZP5FNtL4Yj6nja_7" : {
"name" : "hi"
},
"-JcFtGoZL7J-CCIjTYcL" : {
"name" : "dfgdfg"
}
]
]
}
This isn't an answer to your question yet, because I first need to understand what you're trying to accomplish (and I can't fit this amount of information in a comment).
In your view you have a form that binds to the group's name and status:
<form role="form" ng-controller="MainCtrl" ng-submit="addGroup(newGroup)">
<div class="form-group">
<label for="groupName">Group Name</label>
<input type="text" class="form-control" id="groupName" ng-model="newGroup.name">
</div>
<div class="form-group">
<label for="groupStatus">Group Status</label>
<select class="form-control" ng-model="newGroup.status">
<option value="inactive">Inactive</option>
<option value="active">Active</option>
</select>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
In your GroupsService you essentially add a group like this:
var ref = new Firebase(FIREBASE_URI);
var groups = $firebase(ref).$asArray();
groups.$add(newGroup);
Which adds the group to the collection at that URL.
Which leads to this data structure:
{
"-JcFXid1A2G8EM7A_kwc" : {
"name" : "hi",
"status" : "inactive"
},
"-JcFZP5FNtL4Yj6nja_7" : {
"name" : "hi",
"status" : "active"
},
"-JcFtGoZL7J-CCIjTYcL" : {
"name" : "dfgdfg",
"status" : "active"
}
}
But if I understand you correctly you to want the data to be stored like this:
{
"inactive": [
"-JcFXid1A2G8EM7A_kwc" : {
"name" : "hi"
}
],
"active": [
"-JcFZP5FNtL4Yj6nja_7" : {
"name" : "hi"
},
"-JcFtGoZL7J-CCIjTYcL" : {
"name" : "dfgdfg",
}
]
}
Is this indeed what you're looking to do?
Are you ever going to display active and inactive groups combined in a list? This is important to know, since it is quite easy to filter a list in Angular, but I wouldn't know how to merge two lists.

AngulaJS filter on array of object

I have this JSON object.
var UsersList=[
{
"User" : {
"IdUser" : "admin",
"FirstName" : "mirco",
"LastName" : "sabatino"
}
}, {
"User" : {
"IdUser" : "coordinator",
"FirstName" : "coordinator",
"LastName" : ""
}
}, {
"User" : {
"IdUser" : "test",
"FirstName" : "publisher",
"LastName" : "Diaz"
}
}, {
"User" : {
"IdUser" : "work",
"FirstName" : "ingester",
"LastName" : "Brown"
}
}
] ;
I want filter in ng-repeat for LastName value.
<div class="col-md-6" ng-repeat="users in UsersList | filter: {User.LastName : filterSearchLetter}">
filterSearchLetter value in my controler is populated by
$scope.filterLetterUser=function(letter){
$scope.filterSearchLetter=letter;
console.log($scope.filterSearchLetter);
};
But this don't work.
See my plnkr for the solution http://plnkr.co/edit/l27xUQ?p=preview. I also have implemented your required UI behavior using ng-change. Hope it helps.
For detailed explanation I offer you Todd Motto's blog - http://toddmotto.com/everything-about-custom-filters-in-angular-js/
To invoke a filter, the syntax is the filter function name followed by the parameters.
To define a filter you need to do (This is copy of Todd Motto's code modified for your data)
todos.filter('startsWithLetter', function () {
return function (items, letter) {
var filtered = [];
var letterMatch = new RegExp(letter, 'i');
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (letterMatch.test(item.User.LastName.substring(0, 1))) {
filtered.push(item);
}
}
return filtered;
};
});
This is not a valid object: {User.LastName : filterSearchLetter} It should actually throw an exception saying something like
Uncaught SyntaxError: Unexpected token .
What you probably want to try instead is: {"User": {"LastName" : filterSearchLetter} }
To iterate over an array of Objects see:
angularjs - ng-repeat: access key and value from JSON array object
You may have to convert to valid JS object using JS_Obj=JSON.parse(JSON_Obj);
Maybe something like this:
//iterate through array
<ul ng-repeat="users in UsersList">
//iterate through Object
<li ng-repeat=(key, value) in users | filter: {User.LastName : filterSearchLetter}>
{{key}} : {{value}}
</li>
</ul>

Resources