Displaying two tables within a loop - angularjs

I'm pretty new to AngularJS.
I'm using it with firebase.
I have a set of data like this one:
"userId1" : {
"name" : "Bobby",
"comments" : {
"-J_v90hh5sbJ2acmX4nq" : true
}
}
and a comment list
"-J_v90hh5sbJ2acmX4nq" : {
"creation_date" : 1415110466568,
"creator_id" : "userId1",
"desc" : "dsqdsq",
"type" : "suggest"
}
So what I want to do is to display all the comments of our friend Bobby.
.controller('CommentsCtrl', function($scope, $firebase){
var creator_id = 'userId1'; // Test purpose
var userCommentsRef = new Firebase("https://awesome.firebaseio.com/users/"+creator_id+'/comments');
var commentsRef = new Firebase("https://awesome.firebaseio.com/comments");
var userCommentsSync = $firebase(userCommentsRef);
var commentsSync = $firebase(commentsRef);
var userComments = userCommentsSync.$asObject();
var comments = commentsSync.$asObject();
$scope.userComments = userComments;
$scope.comments = comments;
})
Do you know how I should proceed with this display, but to be sure that if the comments has been deleted (and the key is still there), the description will not be displayed?
<ion-list ng-controller="CommentsCtrl">
<ul class="list">
<li class="item item-checkbox item-checkbox-right" ng-repeat="(key,item) in userComments">
{{comments[key].desc}} // Working, but don't want deleted comments
</li>
</ul>
</ion-list>
Is this the correct way to handle this situation?
How can I say to my controller to display only existing comments ?

You can use filter in ng-repeat block in a such way:
<li ng-repeat="(key,item) in userComments | filter:{deleted:false}">
{{comments[key].desc}} // Working, but don't want deleted comments
</li>
or, if information stored in comments object use ng-if:
<li ng-repeat="(key,item) in userComments | filter:{deleted:false}" ng-if="!comments[key].deleted">
{{comments[key].desc}} // Working, but don't want deleted comments
</li>

No, this is not the correct way. When working with collections, utilize $asArray instead of $asObject. From the guide:
Synchronized arrays should be used for any list of objects that will be sorted, iterated, and which have unique ids.
Objects are useful for storing key/value pairs, and singular records that are not used as a collection.
Also, there is no need to have a deleted parameter. You could simply removed the deleted comments from the user's index. Then if it exists in that list, it is not deleted. There is no need for extra filtering fanciness.
That said, you may also want to check out the new query parameters. With those, and a slight data structure change, you could match on items with a property of deleted equal to false, rather than sorting client-side in Angular.
So all together:
var fbRef = new Firebase("https://awesome.firebaseio.com/");
var userCommentsRef =
fbRef.child("users" + creator_id + "/comments")
.orderBy('deleted')
.startAt(false)
.endAt(false);
$scope.userComments = $firebase(userCommentsRef).$asArray();

Related

Filtering MongoDB data on the front end

I have little problems with MongoDb. I have 2 collections: Auctions and Users. In the "Users" i store obviously the users (mail, name...etc) In the Auctions i store products (creationdate, createdBy, FollowedBy, Invitations, image). Now, i want to show in every user's dashboard, only the products that he follows (his name is stored in the ""FOllowedBy" when he clicks on the botton Follow). If I have to filter on the backend, all it's clear, instead i want to load all the data from the DB and then filter that on the front end with angularJs.
With a simple field like createdBy it's easy: If i Have an user called "Alex"(for example) i can show the data only if createdBy match with Alex (or every other user). But the same thing doesn't work with an array field like "FollowedBy". If in the product "Table" createdby: "Mark", FollowedBy: "Alex" and "Jack", i can easy filter all the product to show only Mark's product, but i don't understand how to show only the products followed by Alex, cause this field is an array.
This is my back-end route.
app.get('/api/dashboard', function (req, res) {
AllAuctions
.find({}, function (err, auctions) {
if (err)
res.send(err);
console.log(auctions);
res.send(auctions);
});
});
I load it with $http with angularJs:
$http.get('/api/dashboard').then(function (AllAuctions) {
$scope.allauctions = AllAuctions.data;
});
If i have to filter the field "createdBy" i can do something like this:
<div class="panel panel-default" >
<div class="panel-heading">My Auctions</div>
<div class="panel-body">
<ul class="list-group" ng-repeat="allauction in allauctions">
<li class="list-group-item" ng-show="allauction.createdBy=='Alex'">
<img ng-src="{{allauction.imgUrl}}">
<div class="title"> {{allauction.title}} </div>
</li>
</ul>
</div>
</div>
But i don't understand how to filter a field like this:
FollowedBy: [
Shelly.name,
Dario.name
]
to show only the products followed by Dario.
If you want to do it in html you can do something like
ng-show="allauction.FollowedBy.indexOf('Alex')>=0"
However when you have multiple filters it will quickly become hardcore. You should define a filtering function in javascript that will compute a filtered array on which you can do a simple ng-repeat. Or you could alternatively add a flag to your allauction objects and perform ng-if/ng-show based on this flag. For exemple:
function updateFilteredData()
{
$scope.filteredData = [];
for(var i = 0; i< $scope.allauction; i++)
{
var auction = $scope.allauction[i];
if(auction.createdBy !== currentUser.name) //I assume currentUser is json object for your Alex user
continue;
if($scope.followByFilter && auction.FollowedBy.indexOf($scope.followByFilter) < 0)
continue; //I assume followByFilter is a string containing the name of a follower you are trying to filter
$scope.filteredData.push(auction);
}
}
You need to call this function once in $http get and each time filtering conditions change (ex: followByFilter name changes...).
Ofcorse you should ng-repeat on filteredData array instead of allauction.

how to avoid duplicate items with ng-repeat and drop in angularjs?

Can you advice me the right sole of some issue?
I have next situation:
1.I load list of items from server and list it with ng-repeat in container here:
<div layout="column" id={{vm.folderId}}container flex layout-align="start center" class='container' dragula='"first-bag"'>
<div id={{item.id}}child class="md-whiteframe-5dp capitalize itemsInList" layout="column" ng-repeat="item in vm.workItems | filter:search">
<div layout="column" flex >
<div class="workItemName">{{vm.getWorkItemName(item.metadata)}}</div>
<div class="workItemDescription">{{vm.getWorkItemDescription(item.metadata)}}</div>
</div>
</div>
</div>
So lets say we have listed next list:
item_1
item_2
item_3
item_4
than I drop another element into this container, ITEM_5.
After I've droped server gives me new list: item_1 -
item_2 -
item_3 -
item_4 -
item_5
and I update my vm.workItems here:
if (data.ok) {
if (data.content.items.length != vm.workItems.length) {
vm.workItems = data.content.items; //from server
SharedDataService.setLastUpdateTimeStamp(SharedDataService.getNewUpdateTimeStamp());
}
}
After when it ng-repeat fresh items it also view droped element in list.So in the end I have:
item_1 //ng-repeated
item_2 //ng-repeated
item_3 //ng-repeated
item_4 //ng-repeated
item_5 //ng-repeated
item_5 //droped element
How to avoid this? is there some normal solving?
update. I drop element ,when it's droped I make request to server to ad new item to list:
$scope.$on('first-bag.drop', function (e, el, container) {
var elementId = parseInt(el[0].id);
var newContainerId = parseInt(container[0].id);
var newPrevElementId;
var newPrevElement = document.getElementById(el[0].id).previousElementSibling;
if(newPrevElement) {
newPrevElementId = parseInt(newPrevElement.getAttribute('id'));
} else if(!newPrevElement) {
newPrevElementId = newContainerId;
}
if(exContainerId != newContainerId) {
WebSocketService.addItem(elementId, exContainerId,
}
}
You can do
ng-repeat="item in vm.workItems track by item.id | filter:search"
If each item has a unique ID(or something unique property), and if you use track by ID, then even if server returns previous items with a new item added to it DOM will not get re-rendered, only new item will be added to the DOM.
Plunker (take a look at console)
ng-repeat adds $$hashkey property to each object when it repeats through a list of objects.
When your server returns the new data, though there are already existing objects, since, the new data(each object) does not contain $$hashkey property in it, ng-repeat re-renders then thinking they are new objects.
If you use track by ID then ng-repeat does not add a $$hashkey property, it keeps track of each item using ID. So, even if server returns same data again, since, ID's matches, ng-repeat does not repeat them. If there are new ID's then it renders then to the DOM.
Take a look at this blog post.
Try this in your function:
let data = data.content.items;
let temp = [];
data.forEach(function(item) {
if (temp.indexOf(item) === -1) {
temp.push(item);
}
});
vm.workItems = temp;
Hope this helps.

Observing nested properties in Ember controller

I'm in the middle of a small project involving Ember. It's my very first time at working with this framework and it has not been an easy learning so far :(
Right now I'm having troubles dealing with nested arrays. What I want to do is pretty standard (at least it seems that way): I have items, item categories and category types (just a way to organize them).
The idea is that there are checkboxes (categories) that allow me to filter the items that are shown in the webpage. On the other hand, there are checkboxes (types) that allow me to check multiple catgories at a time.
In order to implement this I've defined a route (in which I retrieve all the data from these models) and a controller. Originally, I only had items and categories. In this context, I observe the changes in the filters (categories) like this: categories.#each.isChecked and then show the item selection. Unfortunately, now that the hierarchy is types->categories, is not possible to observe changes in categories in the same manner according to the docs:
Note that #each only works one level deep. You cannot use nested forms like todos.#each.owner.name or todos.#each.owner.#each.name.
I google a little bit but didn't find too much about it, so I right now I was thinking in using a custom view for categories (one that extends the Ember.Checkbox) and send an event to the controller whenever a category is checked or unchecked. Is more of a "manual" work and I guess is far from Ember's way of dealing with this type of things.
Is there a standard way of doing this?
Thanks in advance for any help.
One way of solving this would be to observe the category types and filter categories, the same way that the categories are being observed.
This is an example,
http://emberjs.jsbin.com/naqebijebapa/1/edit
(one to many relationships have been assumed)
hbs
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each catType in catTypes}}
<li>{{input type="checkbox" checked=catType.isSelected }}{{catType.id}} - {{catType.name}}</li>
{{/each}}
</ul>
<hr/>
<ul>
{{#each cat in filteredCats}}
<li>{{input type="checkbox" checked=cat.isSelected }}{{cat.id}} - {{cat.name}}</li>
{{/each}}
</ul>
<hr/>
<ul>
{{#each item in filteredItems}}
<li>{{item.id}} - {{item.name}}</li>
{{/each}}
</ul>
</script>
js
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.CategoryType = Em.Object.extend({
id:null,
name:null,
isSelected:true
});
App.Category = Em.Object.extend({
id:null,
name:null,
type:null,
isSelected:false
});
var catTypeData = [
App.CategoryType.create({id:1,name:"type1"}),
App.CategoryType.create({id:2,name:"type2"}),
App.CategoryType.create({id:3,name:"type3"}),
App.CategoryType.create({id:4,name:"type4"})
];
var catData = [
App.Category.create({id:1,name:"cat1",type:catTypeData[1]}),
App.Category.create({id:2,name:"cat2",type:catTypeData[2]}),
App.Category.create({id:3,name:"cat3",type:catTypeData[0]})
];
var itemsData = [
{id:1,name:"item1",cat:catData[0]},
{id:2,name:"item2",cat:catData[0]},
{id:3,name:"item3",cat:catData[1]}
];
App.IndexRoute = Ember.Route.extend({
model: function() {
return Em.RSVP.hash({catTypes:catTypeData,cats:catData,items:itemsData});
}
});
App.IndexController = Ember.ObjectController.extend({
filteredItems:[],
filterItemsBasedOnCategory:function(){
var selectedCats = this.get("cats").filterBy("isSelected");
if(!Em.isEmpty(selectedCats))
this.set("filteredItems",this.get("items").filter(function(item){
return selectedCats.contains(item.cat);}));
else
this.set("filteredItems",[]);
}.observes("cats.#each.isSelected"),
filterCatsBasedOnCategoryType:function(){
var selectedCatTypes = this.get("catTypes").filterBy("isSelected");
if(!Em.isEmpty(selectedCatTypes))
this.set("filteredCats",this.get("cats").filter(function(cat){
var itContainsIt = selectedCatTypes.contains(cat.type);
if(!itContainsIt){
cat.set("isSelected",false);
}
return itContainsIt;
}));
else
this.set("filteredCats",[]);
}.observes("catTypes.#each.isSelected")
});

How to get a single item from a GoInstant collection?

How do you get a single item from a GoInstant GoAngular collection? I am trying to create a typical show or edit screen for a single task, but I cannot get any of the task's data to appear.
Here is my AngularJS controller:
.controller('TaskCtrl', function($scope, $stateParams, $goKey) {
$scope.tasks = $goKey('tasks').$sync();
$scope.tasks.$on('ready', function() {
$scope.task = $scope.tasks.$key($stateParams.taskId);
//$scope.task = $scope.tasks.$key('id-146b1c09a84-000-0'); //I tried this too
});
});
And here is the corresponding AngularJS template:
<div class="card">
<ul class="table-view">
<li class="table-view-cell"><h4>{{ task.name }}</h4></li>
</ul>
</div>
Nothing is rendered with {{ task.name }} or by referencing any of the task's properties. Any help will be greatly appreciated.
You might handle these tasks: (a) retrieving a single item from a collection, and (b) responding to a users direction to change application state differently.
Keep in mind, that a GoAngular model (returned by $sync()) is an object, which in the case of a collection of todos might look something like this:
{
"id-146ce1c6c9e-000-0": { "description": "Destroy the Death Start" },
"id-146ce1c6c9e-000-0": { "description": "Defeat the Emperor" }
}
It will of course, have a number of methods too, those can be easily stripped using the $omit method.
If we wanted to retrieve a single item from a collection that had already been synced, we might do it like this (plunkr):
$scope.todos.$sync();
$scope.todos.$on('ready', function() {
var firstKey = (function (obj) {
for (var firstKey in obj) return firstKey;
})($scope.todos.$omit());
$scope.firstTodo = $scope.todos[firstKey].description;
});
In this example, we synchronize the collection, and once it's ready retrieve the key for the first item in the collection, and assign a reference to that item to $scope.firstTodo.
If we are responding to a users input, we'll need the ID to be passed from the view based on a user's interaction, back to the controller. First we'll update our view:
<li ng-repeat="(id, todo) in todos">
{{ todo.description }}
</li>
Now we know which todo the user want's us to modify, we describe that behavior in our controller:
$scope.todos.$sync();
$scope.whichTask = function(todoId) {
console.log('this one:', $scope.todos[todoId]);
// Remove for fun
$scope.todos.$key(todoId).$remove();
}
Here's a working example: plunkr. Hope this helps :)

AngularJs delete record on nested grouped rows

I'm trying to build a simple page to group record and then add a button to eliminate some records.
The problem is that the record eliminated that has the same name is deleted from the wrong grouped list. And also if a list have no grouped records should disappear, and instead is always there.
Fiddle: http://jsfiddle.net/Tropicalista/qyb6N/15/
// create a deferred object to be resolved later
var teamsDeferred = $q.defer();
// return a promise. The promise says, "I promise that I'll give you your
// data as soon as I have it (which is when I am resolved)".
$scope.teams = teamsDeferred.promise;
// create a list of unique teams
var uniqueTeams = unique($scope.players, 'team');
// resolve the deferred object with the unique teams
// this will trigger an update on the view
teamsDeferred.resolve(uniqueTeams);
// function that takes an array of objects
// and returns an array of unique valued in the object
// array for a given key.
// this really belongs in a service, not the global window scope
function unique(data, key) {
var result = [];
for (var i = 0; i < data.length; i++) {
var value = data[i][key];
if (result.indexOf(value) == -1) {
result.push(value);
}
}
console.log(result)
console.log(Math.ceil(result.length / 10))
$scope.noOfPages = Math.ceil(result.length / 10);
return result;
}
$scope.currentPage = 1;
$scope.pageSize = 5;
$scope.maxSize = 2;
$scope.deleteItem = function(item){
//code to delete here
var index=$scope.players.indexOf(item)
$scope.players.splice(index,1);
};
Here is a sample of something expanding on the tip from SpykeBytes
<div ng-repeat="location in journey.locations">
<div id="location_div_{{ $index }}">
<label class="journey-label">Location name</label>
<input class="journey-input" id="location_{{ $index }}" type="text" ng-model="location.location_name" />
<button ng-show="editable" tabindex="-1" class="journey-button remove" ng-click="removeItem(journey.locations, $index)">
Remove location
</button>
Then in my controller I set up an action that takes deletes the individual item
$scope.removeItem = function(itemArray, index) {
return itemArray.splice(index, 1);
};
To hide the group when nothing is listed, you need to get the filtered list and then use ng-show to drive the display. This is a bit tricky:
<div ng-show="currentList.length>0" ng-repeat="team in teams| startFrom:(currentPage - 1)*pageSize | limitTo:pageSize | filter:searchInput"> <b>{{team}}</b>
<li ng-repeat="player in (currentList = (players | filter: {team: team}))">{{player.name}}
<button class="btn btn-small" type="button" ng-click="deleteItem(player)">Delete</button>
</li>
</div>
However I am not seeing the problem you said about removing from wrong group. Can you let me know how to reproduce it?
Index won't help you here because the {{$index}} that ng-repeat provides is within the groupings. That is, each grouping restarts the $index variable. You are going to need a unique identifier for each record though. Without that there is no way to be sure that the record you want to remove is the right one.
As far as the groupings, you can recreate the model whenever you delete something. This wouldn't work with the sample data in the Fiddle, but it works when you're dealing with a real datasource.
You can instead pass the index of the object if it is within ng-repeat.

Resources