Looping through a javaScript object's array with angular - javascript

So I'm absolutely new to writing any type of code, I'm trying to build a simple website using AngularJS and I'm having a problem with looping through a controller's object array through a directive template. My goal is to output each array string into a separate list item element in the template,
for example in the controller I have
$scope.items = [
{
list: ["1", "2", "3"],
name: "whatever"
},
{
list: ["4", "5", "6", "7", "8"],
name: "whatever2"
}]
in the directive template I have written something like this
<h1>{{ directive.name }}</h1>
<ul id= "items"></ul>
<script>
for (var i = 0; i < directive.list[].length; i++) {
document.getElementById("items").innerHTML =
"<li>{{" + directive.list[i] + "}}</li>";
};
</script>
the name object retrieves correctly in index.html using the template but list object will not output the array, gave it my best shot, tried troubleshooting to get it right and I can't seem to figure it out, need help :( hope I'm being clear enough, this is my first attempt at creating anything and still trying to get familiar with all the jargon.

Here is a shot:
<div ng-repeat="i in items">
<h1>{{ i.name }}</h1>
<ul>
<li ng-repeat="item in i.list">{{i}} </li>
</ul>
</div>
And the description:
Basically you want to loop through all the items in your array <div ng-repeat.. then for each of those items you want to create the <ul>

It's simple -- replace your <script> with this:
<ul>
<li ng-repeat="item in items">{{ item.name }}
<ul id="items">
<li ng-repeat="list_item in item.list">{{ list_item }}</li>
</ul>
</li>
</ul>

Related

ngRepeat Filter by Array name?

I have the following JSON structure in my Angular app:
Sample: http://pastie.org/pastes/9476207/text?key=u6mobe15chwyiz1jakn0w
And the following HTML which is displaying every product in the parent array:
<div class="bb-product-grid" ng-repeat="brand in notebooks">
<ul ng-repeat="products in brand">
<li ng-repeat="Notebook in products">
<span class="product-title">{{Notebook.Model}}</span>
<span class="product-description"><p>{{Notebook.Description}}</p></span>
<span class="product-image">image</span>
<span class="product-add-to-bundle"><button>Add to bundle</button></span>
<span class="product-price">{{Notebook.Price}}<sub>pcm</sub></span>
</li>
</ul>
</div>
I want to be able to filter the products by the brand names (array names, Acer, Apple etc.).
By default, only the 'Apple' products will be visible. You will then click a button to change the visible results to reflect what brand you've selected. So if you then select HP, you'll only see 'HP' products.
I'm a little stuck on this because the JSON structure is pretty nested, if it was 1/2 levels I'd be able to cope but I can't figure this out, I seem to only show all the products or break the app. Any advice on the best-practise approach to this (I'm guessing I might have to create a custom filter?) will be greatly appreciated!
If you can get your data to be in this format:
$scope.data = [
{
brand: 'Acer',
laptops: ['acer_1', 'acer_2', 'acer_3', 'acer_4', 'acer_5']
},
{
brand: 'Apple',
laptops: ['apple_1', 'apple_2', 'apple_3', 'apple_4', 'apple_5']
},
{
brand: 'Asus',
laptops: ['asus_1', 'asus_2', 'asus_3', 'asus_4', 'asus_5']
},
{
brand: 'HP',
laptops: ['hp_1', 'hp_2', 'hp_3', 'hp_4', 'hp_5']
},
{
brand: 'Lenovo',
laptops: ['lenovo_1', 'lenovo_2', 'lenovo_3', 'lenovo_4', 'lenovo_5']
},
{
brand: 'Toshiba',
laptops: ['toshiba_1', 'toshiba_2', 'toshiba_3', 'toshiba_4', 'toshiba_5']
}
];
Then you can use have a filter like so:
$scope.search = {
brand: 'HP'
};
With HTML:
<select ng-model="search.brand">
<option ng-repeat="company in data">{{company.brand}}</option>
</select>
<ul ng-repeat="company in data | filter:search">
<li><b ng-bind="company.brand"></b></li>
<ul ng-repeat="laptop in company.laptops">
<li ng-bind="laptop"></li>
</ul>
</ul>
Here is a jsfiddle.
brands is an array of objects with the brand names as keys. It doesn't make sense to use filters to solve this problem ... however it's easy to get the products if you have the key name. Imagine you have searchName = 'Apple' and obj = [{Apple: []}, {Acer: []}]. You could just use obj[0].Apple.
this.filterItem = "Apple";
this.productsToShow = productObject[0][this.filterItem];
<select ng-model=ctrl.filterItem><!-- brand names go here --></select>
<ul ng-repeat="product in ctrl.productsToShow">
Don't use filter, when only ever one is supposed to be visisble. Instead, skip the iteration over the brands, and instead just iterate over the selected brand. All you need to do is then make sure you set selectedBrand to the correct subset of your data structure.
Fiddle: http://jsfiddle.net/5wwboysu/2/
Show brand:
<span ng-repeat="(name,brand) in notebooks.Notebooks[0]" ng-click="selected(brand)">
{{name}} - <br>
</span>
<ul ng-repeat="products in selectedBrand">
<!-- snip -->
</ul>
$scope.selected = function(brand) {
$scope.selectedBrand = brand;
};
However, if you have control over the JSON, I'd try to return it in a format better suited for what you're trying to do.
Add this filter which will take a brand:
<ul ng-repeat="brand in notebooks| filter:filterBrand">
in controller
$scope.SelectedBrandName= "Apple";
$scope.filterBrand = function(brand){
if(brand.Name == $scope.SelectedBrandName)
return true;
return false;
};
This will look at each brand and only allow brands that return true. If you have a button that changes the $scope.SelectedBrandName value to a different name, it will automatically filter through the ng-repeat to only brands that match.

Filter the list with letter in angular js

I have a list displayed in table where I need to filter the result with first letter of name,above the list I have a letter A B C D and so on.
After click the letter list will be filter by its first name
For ex: list details are Apple Boy Bridge
after click A, Apple will be displayed
Instead of fruit, I had to filter names of countries to display their sales representatives:
'use strict';
angular.module('sodemo')
.filter('firstLetter', function () {
return function (input, letter) {
input = input || [];
var out = [];
input.forEach(function (item) {
//console.log("current item is", item, item.charAt(0));
if (item.charAt(0).toLowerCase() == letter) {
out.push(item);
}
});
return out;
}
});
A quick way to generate an array with letters of the alphabet:
$scope.alphabet = "abcdefghijklmnopqrstuvwxyz".split("");
and the view, which also sets a different background colour if the letter is active:
<button type="button" class="btn-alphabet btn btn-default" ng-repeat="letter in alphabet" ng-click="setActiveLetter(letter)" ng-class="{'btn-primary': letter==activeLetter}">{{letter}}</button>
I filtered elements of the array of countries like this:
<ul class="list-group countries-salesreps" >
<li class="list-group-item" ng-repeat="country in filteredCountriesArray = (countriesArray | firstLetter:activeLetter)" ng-click="showSalesRep(country)" ng-class="{'btn-primary': country==currentCountry}">{{country}}</li>
</ul>
You can check if there are elements in the filtered list using .length:
<div class="alert alert-warning" ng-hide="filteredCountriesArray.length">No available countries starting with <em>{{activeLetter}}</em></div>
So the question has been answered but I came across this looking for an answer and being quite new to angular found it kind of hard to read and understand properly. I then found this tutorial explaining filters and how they work in general and in his examples he creates a 'startsWithLetter' filter which I found quite useful: http://toddmotto.com/everything-about-custom-filters-in-angular-js/
Just thought I would post it in case anyone had trouble understanding like I did.
this is old but maybe this plunker can help, using angular's filter filter.
Define an expression like so:
// Filter Expression
this.filterActive = function(value){
if (self.active) {
return value.charAt(0).toLowerCase() === self.active;
}
return true;
}
Then in html:
<ul>
<li ng-repeat="country in ctrl.countries | filter:ctrl.filterActive" ng-bind="country"></li>
</ul>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
<ul>
<li ng-repeat="name in list | filter:letterFilter">
{{name.firstName}}
</li>
</ul>
try above code this is simple to implement:

AngularJS dynamic list of filters

I have a list of products. Each product has three properties: mustBuy, promotion (both booleans) and productName. What I'm trying to do is to apply one filter at the time, based on the user input. If the user clicks on a button called "Must buy", the products will be filtered by the mustBuy field, so once I click that button I should only see the products that have a mustBuy property with a value of true.
I want to generate those filter buttons in a dynamic manner because I might add extra properties later on. Right now I have a list of hard coded buttons used for filtering:
<a ng-click="myFilter = {mustBuy:true}">
<img src="/images/must-filter.png" />
</a>
<a ng-click="myFilter = {promotion:true}">
<img src="/images/promo-filter.png" />
</a>
This is what "myFilter" filters.
<div ng-repeat="product in products | filter:myFilter">...</div>
It works fine but I want to make those filters dynamic as I'll add more in the future. This is what I've tried:
(v1):
Js controller:
$scope.filters = [{ image: "must-filter.png", myFilter: { mustBuy: true } }, { image: "promo-filter.png", myFilter: { promotion: true } }];
Html:
<a ng-repeat="f in filters | filter:f.myFilter">
<img src="/images/{{f.image}}" />
</a>
(v2):
Js controller:
$scope.filters = [{ image: "must-filter.png", myFilter: { mustBuy:true } }, { image: "promo-filter.png", myFilter: {promotion:true} }];
Html:
<a ng-repeat="f in filters"
ng-click="{{f.myFilter}}">
<img src="/images/{{f.image}}" />
</a>
(v3):
Js controller:
$scope.filters = [{ image: "must-filter.png", myFilter: "myFilter = {mustBuy:true}" }, { image: "promo-filter.png", myFilter: "myFilter = {promotion:true}" }];
Html:
<a ng-repeat="f in filters"
ng-click="{{f.myFilter}}">
<img src="/images/{{f.image}}" />
</a>
The third approach (v3) has EXACTLY the same HTML output the original (hardcoded) approach had, except it doesn't work, which makes me think there is something more (binding) happening behind the scenes? How can I achieve the functionality described in the first paragraph?
Without looking too much (read at all) why your attempts failed, you can achieve what you want like this:
1.)
Define a list of filters and the currently selected (applied) filter in the contoller:
$scope.filters = [
{name: 'none', filterExpr: ''},
{name: 'must buy', filterExpr: {mustBuy: true}},
{name: 'promotion', filterExpr: {promotion: true}}
];
$scope.selectedFilter = $scope.filters[0];
(My filter-object have a name and a filterExpr, but you can adjust them to your needs.)
2.)
Define a function for changing the applied filter (again in the contoller of course):
$scope.setFilter = function (filter) {
$scope.selectedFilter = filter;
};
3.)
Let the user change the applied filter (in the view):
<ul>
<li ng-repeat="filter in filters">
{{filter.name}}
</li>
</ul>
4.)
Filter the items, based on the currently selected filter's filter-expression:
<ul>
<li ng-repeat="item in items | filter:selectedFilter.filterExpr">
{{item.description}}
</li>
</ul>
See, also, this short demo.
As you can see in this FIDDLE, you must use a function or custom filter to chain your dynamic filters.
Filter set:
//this set will filter everything but true-false
$scope.set1 = [function(item){return item.mustBuy;},
function(item){return !item.promotion}];
Filter function:
$scope.multipleFilters = function(item){
var v = true;
for(var i=0; i<$scope.set1.length; i++){
v = v && $scope.set1[i](item);
}
return v;
};
Use like this:
<div ng-repeat="product in products | filter:multipleFilters">
{{product.mustBuy}} - {{product.promotion}}
</div>
The point is using functions for filters like function(item){return item.mustBuy}, not expressions like {mustBuy:true}

Angular Checking What Filters Return

I have a list with ng-repeat and a filter , the filter filters based on an html input . If the filter produces an empty list , I want to show the user some informative message . I can't figure out how...
Search the list: <input type="text" ng-model="searchText">
<div ng-controller="my-ctrl">
<ul id="list">
<li ng-repeat="element in myElements | filter:searchText" >
{{element.data }}
</li>
</ul>
***<div>{{myElements.length}}</div>*** //length stays
// the same no matter what the filter is returning...
<div ng-show="myElements.length">My Informative Msg</div>
What I tried doing was showing my informative msg based on myElements.length , I thought that the filter will update the value of myElements in my scope . However , what I find as I search for items (e.g reducing or enlarging the list) is that myElements.length stays the same .
Is there some elegant solution to know when the filter is empty , or more generally to know what the filter is returning ?
There is a neat trick for that. Instead of filtering the real list, create a new filtered list "in the fly" which will contain only the filtered items or none. If none, show the message.
app.controller('MainCtrl', function($scope) {
$scope.items = ["Foo", "Bar", "Baz"];
});
And:
<input ng-model="searchText" />
<ul>
<li ng-repeat="item in filteredItems = (items | filter: searchText)">
{{ item }}
</li>
</ul>
<div ng-hide="filteredItems.length">There is no item matching</div>
So if your filter doesn't match any item, you will see the message.
Plunker here: http://plnkr.co/edit/3os7gIxSjlyDO1CGKcFT?p=preview
I think you should create your own filter which will do that.
Here it is: http://jsfiddle.net/QXSR8/
HTML
<div>
{{answer | empty}}
</div>
JS
angular.module('project', []).
filter('empty', function() {
return function(input) {
if (!input || !input.length){
return "Array is empty";
} else {
return input;
}
}
});

Angular factory filter - Can't get data passed to the filter

I'm looking through the Angular docs and I can't better documented stuff.
My problem is as follows:
I have a filter:
.filter('filteringService', function () {
return function (photos, categsList) {
if (photos !== undefined) {
var filteredPosts = [];
console.log('Categories or Selected Categories:');
console.log(categsList);
//DEVEL Only, assign a SEARCH value, can't pass the data from the list now.
categsList = 'people';
if (categsList === undefined) {
for(i=0; i < photos.length; i++) {
filteredPosts.push(photos[i]);
}
} else {
console.log('Trying to push a category slug to search for.');
//TASK: Convert in multiple possible selections.
filteredPosts = [];
}
console.log('Filter returns ' + filteredPosts.length + ' posts');
return filteredPosts;
}
};
});
And I have the template
<div class="photos">
<div class="filters">
<ul>
<li><a>ALL</a></li>
<li ng-repeat="category in categsList">
<a ng-checked="category[0]" ng-model="category[0]">{{ category[1] }}</a>
</li>
</ul>
</div>
<ul class="photos-list">
<li ng-repeat="photo in photos|filteringService:category">
<h1>{{ photo.title }} click LINK</h1>
<ul class="categories">
<li ng-repeat="category in photo.categories">
{{ category.title }}
</li>
</ul>
</li>
</ul>
</div>
There's a huge object with posts called photos and then there's a category list called categsList.
The photos object has the items from the categs list in it. I WANT to be able to filter with the CLICKED element through that list, and maybe multiple filter, but first to pass on the actual filter value to the filter service, I can't seem to do that.
How should I do that?
Apparently I managed to pass the filter value, in a dirty way (I GUESS), like this
<a ng-bind="category.slug" ng-click="returnFilter(category.slug);" ng-model="category.slug">{{ category.title }}</a>
it goes here
$scope.returnFilter = function(theSlug) {
$scope.filterBy = theSlug;
};
and it comes out here
<li ng-repeat="photo in photos|filteringService:filterBy">
It's working, but Is this correct?
EDIT: Also with this way in mind, I could pass an array as theSlug so I can do multiple filtering, and when clicking two times on the same item it would get it out of the array. hmmm
EDIT 2:
Let's say the resulting array is under 15 items, could do I run some action in the controller knowing this?
Actually the other way around, could I query from the controller the resulting array that the filter outputs?
I can't $watch the first array, I guess the filter creates a new array and puts those results in page. How could I watch the resulting array for changes and do stuff in the controller?

Categories