Get original object name from firebase object inside ng-repeat - angularjs

I get from firebase an object with the following code:
var ref = firebase.database().ref().child("categories");
ref.orderByChild("status")
.equalTo('featured')
.on('value', function(snap) {
console.log('Categories in range', snap.val());
$scope.categories = snap.val();
})
The object I get contains a couple of objects with the data which look like my example here:
Example Object from firebase
The results i use in my template view with ng-repeat.
<ul class="list">
<li ng-repeat="(category_id, category) in categories | toArray | orderBy:'position'">
<div style="background-image: url({{category.images[0].src}});" ng-click="category_action(category_id)">{{category.name}}
</div>
</li>
My problem is now that I want to give the original object name back with ng-click. I tried to use the (key, value) solution from angular for ng-repeat but I dont get the original object name back and only a index number like 0,1,2,3 etc.
What I need is the original name like in this example KU8JfAZRCsPJy9uOMqm.
If i only get the index number (0,1,2) back how can I know in my controller which object name this was originaly? Like for example $scope.categories.???
Thank you for your help in advance, I did research but found only people with the same problem but no working solution.
PS: I use firebase 3.x and angular 1.x

Related

How to properly select an object in an angular dropdown menu in a ionic app

I have a Ionic 1 app and I need some help to simplify my code and understand the best way to get around this thing:
I have a json like this:
[{"Cod":"A","Denumire":"production","Culoare":"#808000","ID":"1","Activ":"1"},
{"Cod":"B","Denumire":"transportation","Culoare":"#C4FFC4","ID":"2","Activ":"1"}]
and it's assigned in my controllers.js to $scope.categories
What I want is to load it in a drop-down menu and set the selected value to a variable snag.CategorieID which I have in the html page.
I want to assign it from the html and not the controller!
The problem is that snag.CategorieID is the ID from $scope.categories and it seems to me first I have to find out the index of the object $scope.categories that has the ID == snag.CategorieID.
I have managed to get it working like this:
<label class="item-borderless item-input item-select">
<div class="input-label">
Category
</div>
<div ng-repeat="c in categories">
<div ng-if="c.ID == snag.CategorieID">
<select ng-init="snaginf.category = categories[categories.indexOf(c)]" ng-model="snaginf.category" ng-options="cat.Denumire for cat in categories"></select>
</div>
</div>
</label>
The thing is even if it works I have the feeling things should be simpler.
Could anyone help me simplify it?
Thanks in advance,
Rares
I am not a fun of ng-init since the code should be in the controller, but if you must do it that way, you can use a filter to get the category using a categoryID like this:
<select ng-init="snaginf.category = (categories | filter: {ID: snag.CategorieID})[0]" ng-model="snaginf.category" ng-options="cat as cat.Denumire for cat in categories track by cat.ID"></select>
In this piece of code snaginf.category saves all the category object and assumes the ID of the categories is unique.
The filter takes the categories array and returns a new array with those categories which their ID equals snag.CategorieID. Since it's supposed to return only one category inside the array, we just get that element using [0].
Why don't you keep just the selector in place.
<select ng-init="snaginf.category = categories[0]" ng-model="snaginf.category" ng-options="cat.Denumire for cat in categories"></select>
Live Sample:
http://play.ionic.io/app/c0a0efd8603b

How do I ask AngularJS to recalculate the value of a complex function?

I have an AngularJS controller with the following function:
$scope.getExampleValue = function(exampleId) {
// calculate a value using underscore's _.where()
// clause against two JSON arrays on the $scope
// and the exampleId parameter passed in
return computedValue;
}
The function's parameter (exampleId) is rendered from the server, so the resulting HTML looks like this:
<div ng-controller="ExampleController">
...
<span>{{ getExampleValue(3) }}</span>
<span>{{ getExampleValue(4) }}</span>
<span>{{ getExampleValue(5) }}</span>
...
</div>
The problem I have is that AngularJS doesn't know to call getExampleValue() again when the JSON arrays used in the function have changed: they're 2 simple JSON arrays, new JSON items can be added or removed, or properties of existing JSON items in either array can be modified which affect the result.
I've looked at $scope.watch() and $scope.watchCollection() but I'm unsure how I can use them without changing my approach to bind against already computed values rather than against the function I prefer.
Essentially I think I'm asking how to notify AngularJS that a complicated bound value has changed, then I could wrap that notification up in a $scope.watch()..
Thanks for your help.
You are looking for $scope.$apply:
When you change the JSON asyncly, run:
$scope.$apply(function () {
// update the properties on $scope
});

Updating values inside an ng-repeat

I'm trying to use AngularJS's ng-repeat to check if a value from a previous iteration is the same as the current.
if previous === current
After, the check I would like to update previous to equal current.
previous = current
I have no idea how to update values from inside an ng-repeat in Angular.
Here is an example:
<div ng-repeat="product in Products | filter: filters[0] | orderBy:'Section'">
<div ng-if="product.Section !== $parent.previous">
{{product.Section}}
</div>
$parent.previous = product.Section
</div>
Update:
While this question might still be helpful in some situations, after some thought I realised that what I really want is new objects grouping products by section: How can I group data with an Angular filter?

How to pull key name from array of objects in AngularFire

Can someone help me pull the unique object name from contents ($Id). I'm able to loop over $scope.data in my template with ng-repeat but I can't get the name of that array. Need this to build a URL reference.
In short, how do I get "2015-02-27T20:24:11-06:00"? I can pull out item.count, item.handle, item.img_url, but {{item.$id}} doesn't work.
obj.$asArray();
obj.$asObject();
If you fetch the data $asArray() and save it on $scope.data you should simply be able to get the $id as you describe.
<div ng-repeat="item in data">
<p>id: {{item.$id}}</p>
</div>
However, I can't help but notice that your casing in $Id is wrong.

How to set a boolean flag to collapse/expand a row with ng-repeat

I have this plunker code.
What I'm trying to do, is to display the gray box one time per row.
To achieve this, I thought to modify the partition filter in order to return a JSON to add it a new property by row to know if the gray box is expanded or not.
But, I could Not successfully return a JSON.
Do you know how to modify the filter to return a JSON or a better way to show the gray box by row?
Related questions:
Push down a series of divs when another div is shown
Update 1
The issue could be easily resolved by using the correct scope for the ng-repeat for the row without modifying the filter, thanks to #m59.
http://plnkr.co/edit/eEMfI1lv6z1MlG7sND6g?p=preview
Update 2
Live Demo
If I try to modify the item, it seems the ng-repeat would be called again losing the props values.
<div ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}">
<div ng-repeat="item in friendRow"
ng-click="collapse(item)"
ng-class="{myArrow: showArrow}">
{{item.name}} {{item.age}} years old.
<div>{{item.name}}</div>
</div>
<div collapse="!props.isExpanded">
some content
<br/>
<input type="text" ng-model="currentItem.name">
</div>
</div>
js
$scope.collapse = function(item){
this.props.isExpanded = !this.props.isExpanded;
this.showArrow = !this.showArrow;
$scope.currentItem = item;
};
This causes the gray box to collapse each time the item is modified. Any clue?
I've updated my code/answer regarding partitioning data. It's important to fully understand all of that before deciding on an approach to your project.
The problem you have in your plnkr demo is that you're modifying the parent $scope and not the scope of the ng-repeat for that row.
Just set a flag on the row and toggle it when clicked:
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="isExpanded = false"
ng-click="isExpanded = !isExpanded"
>
<div ng-repeat="item in friendRow">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!isExpanded">
some content
</div>
</div>
To access the correct scope within a function in the controller, you can use the this keyword instead of $scope. this will refer to the scope the function is called from, whereas $scope refers to the scope attached to the element with ng-controller (a parent of the ng-repeat scopes you want to target).
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-click="collapse()"
>
JS:
$scope.collapse = function() {
this.isExpanded = !this.isExpanded;
};
If you want to keep the ng-click directive on the item element instead of putting it on the row element as I have done, then you're dealing with another child scope because of that inner ng-repeat. Therefore, you will need to follow the "dot" rule so that the child scope can update the parent scope where the collapse directive is. This means you need to nest isExpanded in an object. In this example, I use ng-init="props = {}", and then use props.isExpanded. The dot rule works because the children share the same object reference to props, so the properties are shared rather than just copied, just like in normal JavaScript object references.
Live Demo
<div
class="row"
ng-repeat="friendRow in friends | partition:2"
ng-init="props = {}"
>
<div ng-repeat="item in friendRow" ng-click="collapse()">
{{item.name}} {{item.age}} years old.
</div>
<div collapse="!props.isExpanded">
some content
</div>
</div>
JS:
$scope.collapse = function(){
this.props.isExpanded = !this.props.isExpanded;
};
Update
We keep going through more and more issues with your project. You really just need to experiment/research and understand everything that's going on on a deeper level, or it will just be one question after another. I'll give it one last effort to get you on the right track, but you need to try in the basic concepts and go from there.
You could get past the issue of props reinitializing by putting $scope.expandedStates and then passing the $index of the current ng-repeat to your function (or just using it in the view) and setting a property of expandedStates like $scope.expandedStates[$index] = !$scope.expandedStates[$index]. With the nested ng-repeat as it is, you'll need to do $parent.$index so that you're associating the state with the row rather than the item.
However, you'll then have another problem with the filter: Using my old partition code, the inputs inside the partitions are going to lose focus every time you type a character. Using the new code, the view updates, but the underlying model will not. You could use the partition filter from this answer to solve this, but from my understanding of that code, it could have some unexpected behavior down the road and it also requires passing in this as an argument to the filter. I don't recommend you do this.
Filters are meant to be idempotent, so stabilizing them via some kind of memoization is technically a hack. Some argue you should never do this at all, but I think it's fine. However, you definitely should ONLY do this when it is for display purposes and not for user input! Because you are accepting user input within the partitioned view, I suggest partitioning the data in the controller, then joining it back together either with a watch (continuous) or when you need to submit it.
$scope.partitionedFriends = partitionFilter($scope.friends, 2);
$scope.$watch('partitionedFriends', function(val) {
$scope.friends = [].concat.apply([], val);
}, true); // deep watch

Resources