Is there a "with... as..." clause in angular? - angularjs

Here is my use case
<li ng-repeat="item in heterogeneousFruitArray">
<div ng-if="item.type == 'apple'">
... custom apple stuff ...
</div>
<div ng-if="item.type == 'orange'">
... custom orange stuff ...
</div>
... stuff for all fruits ...
</li>
It would increase readability if I could say
<li ng-repeat="item in heterogeneousFruitArray">
<div ng-if="item.type == 'apple'">
<span ng-with="item as apple">
<h1>{{apple.name}}</h1>
Crispness: {{apple.crispness}}
... and so on...
</span>
</div>
...
</li>
Is there a way to do this with angular?
I realize that this pattern could be me coding with an accent because of my django templates background. If I'm miles away from 'how it's done in angular', please let me know.

ngInit was designed with aliasing in mind, especially within an ngRepeat:
<li ng-repeat="item in heterogeneousFruitArray">
<div ng-if="item.type == 'apple'" ng-init="apple=item">
... custom apple stuff ...
</div>
<div ng-if="item.type == 'orange'" ng-init="orange=item"">
... custom orange stuff ...
</div>
... stuff for all fruits ...
</li>
Here is official documentation on ngInit:
The only appropriate use of ngInit is for aliasing special properties of ngRepeat. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
Demo Plunker

In tandem with ngInit for aliasing, you should also consider using ngSwitch, instead of having multiple ngIfs where only one is evaluated to true at a time.
Using ngSwitch has the same result, but increases readability and is more efficient as it evaluates the condition once in every digest loop (in contrast to multiple ngIfs where the same expression is evaluated once for every ngIf).
E.g.:
<li ng-repeat="item in heterogeneousFruitArray">
<div ng-switch="item.type">
<!-- Apple-specific template -->
<span ng-switch-when="apple" ng-init="apple=item">
<h1>{{apple.name}}</h1>
Crispness: {{apple.crispness}}
</span>
<!-- Orange-specific template -->
<span ng-switch-when="orange" ng-init="orange=item">
<h1>{{orange.name}}</h1>
Juicy-ness: {{orange.juicyness}}
</span>
<!-- Default template (used when `item.type` is unknown/unexpected) -->
<span ng-switch-default ng-init="unknown=item">
<h3>What kind of fruit is "{{unknown.type}}" ???</h3>
</span>
</div>
</li>

You can always use AngularJS Filters to implement that.
Html:
<div ng-app='myApp' ng-controller='myCtrl'>
<ul>
<li ng-repeat='item in items | filter:{name: "orange"}'>
{{item.id}} - {{item.name}}
</li>
</ul>
</div>
Js:
var app = angular.module('myApp',[]);
app.controller('myCtrl', function($scope){
$scope.items = [
{
id: 12,
name: 'apple'
},
{
id: 13,
name: 'orange'
},
{
id:14,
name:'grape'
}
];
});
JSFiddle.

Related

How to filter list according to $query entered?

I list some contacts. and want to filter md-list according to entered value $query.
<md-contact-chips
ng-model="ctrl.contacts"
md-contacts="ctrl.delayedQuerySearch($query)"
md-contact-name="name"
md-contact-image="image"
md-contact-email="email"
md-require-match="true"
md-highlight-flags="i"
filter-selected="ctrl.filterSelected"
placeholder="To">
</md-contact-chips>
<md-list class="memberList">
<md-subheader class="md-no-sticky">Contacts</md-subheader>
<md-list-item class="md-2-line contact-item" ng-repeat="(index, contact) in ctrl.allContacts | filter: $query" <!--need set filter according to enrered value-->
ng-if="ctrl.contacts.indexOf(contact) < 0">
<img class="md-avatar"
ng-src="assets/images/avatars/avatar-1.png"
src="assets/images/avatars/avatar-1.png">
<div class="md-list-item-text compact">
<h3>{{contact.name}}</h3>
<p>{{contact.email}}</p>
</div>
</md-list-item>
</md-list>
will be glad for help.thanks.
"query" is a custom action on an AngularJS $resource. It's available for use OOTB - you don't need to create an special code to register or use it.
There's a good example in the AngularJS tutorial:
https://docs.angularjs.org/tutorial/step_05
app/phone-list/phone-list.template.html:
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="$ctrl.query" />
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in $ctrl.phones | filter:$ctrl.query">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
I assume that $query is something that is injected inside your controller.
In order to use it in the view you need to bind it to the $scope of the controller.
inside your controller please add the following line assusing $query is of type string. (ie. Value or constant)
$scope.query = $query;
My feeling is that $query is actually a service of some sort. if that is the case it will have some sort of access function to get the query such as getQuery(), assuming that is the case plese add the following code to inside your controller.
$scope.query = $query.getQuery();
$scope.$watch(function() {
return $query.getQuery();
}, function(query){
$scope.query = query;
});
and then last but not least change your html to
filter: query
please note the question is somewhat broad as it does not explain what $query is.. i hope this helps.. cheers

Avoid multiple use of a Filter in AngularJs

I am trying to avoid using the same filter on the same collection multiple times in AngularJS application.
What is the correct way to do it:
<div ng-app="myApp" ng-controller="PeopleCtrl">
<ul ng-init="(person in people|filter:{show:true}) as myPeople" ng-if="myPeople.length>0">
<h4>My People</h4>
<li ng-repeat="person in myPeople">{{person.name}}; display: {{person.show}};
</ul>
</div>
CODE PEN
When I use (person in people|filter:{show:true}) as myPeople I try to use an alias in order to not repeat the same filter{show:true} everywhere...
You should do it something like this:
<div ng-init="myPeople = (people | filter:{show:true})">
<div ng-if="myPeople.length>0">
<h4> My People</h4>
<ul>
<li ng-repeat="person in myPeople">
{{person.name}}; age: {{person.age}}; show: {{person.show}};
</li>
</ul>
</div>
</div>

In what scope is ng-repeat "as alias" available

I'm using angular 1.4.8. I want to save filtered result from ng-repeat and use it to determine whether to enable a "load more" button at the bottom of the list. I have looked at this question:
AngularJS - how to get an ngRepeat filtered result reference
And many others, they suggest to use "as alias" from ng-repeat, here's what my code looks like:
<ul class="media-list tab-pane fade in active">
<li class="media">
<div ng-repeat-start="item in items | limitTo: totalDisplayed as filteredResult">
{{item}}
</div>
<div class="panel panel-default" ng-repeat-end>
</div>
<div>
{{filteredResult.length}}
</div>
</li>
</ul>
<button ng-click="loadMore()" ng-show="totalDisplayed <= filteredResult.length">
more
</button>
However, I found filteredResult.length is displayed fine right after ng-repeat, but the button is never shown. If I try to display filteredResult.length in where the button is, it will show null.
So is there a rule for "as alias" scope? I've seen plenty of examples work but mine doesn't, what am I missing here?
EDIT:
The accepted answer uses controllerAs which indeed will resolve the problem. However, charlietfl's comment using ng-init to save filteredResult to parent scope is also very neat and that's the solution I use in my code eventually.
Probably some of classes in your <ul class="media-list tab-pane fade in active"> or <li class="media"> is selector for a directive that would have its own scope. So you store filteredResult in e.g. tab-pane's scope and then try to have access out of it's scope in outside of ul tag.
Try to use Controller as instead of scope:
angular
.module('plunker', [])
.controller('MainCtrl', function() {
vm = this;
// make an array from 1 to 10
var arr = [];
while (arr.length < 10) {
arr.push(arr.length + 1)
};
vm.items = arr;
vm.totalDisplayed = 5;
vm.filteredResult = [];
});
<body ng-controller="MainCtrl as main">
{{main.items}}
<ul class="media-list tab-pane fade in active">
<li class="media">
<div ng-repeat-start="item in main.items | limitTo: main.totalDisplayed as filteredResult">
{{item}}
</div>
<div class="panel panel-default" ng-repeat-end>
</div>
<div>
filteredResult = {{main.filteredResult = filteredResult}}
</div>
</li>
</ul>
<button ng-click="loadMore()" ng-show="main.totalDisplayed <= main.filteredResult.length">
more
</button>
</body>
http://plnkr.co/edit/drA1gQ1qj0U9VCN4IIPP?p=preview

Two ng-repeat, one affect another

Today I faced a strange problem for me in AngularJS.
In this example I have two ng-repeat in product (two or three images and the same number of colors) and one ng-repeat for pagination (irrelevant in this case I assume).
HTML:
<div class="item-wrapper" ng-repeat="item in pagedItems[currentPage]">
<div class="item">
<!-- Item image -->
<div class="item-image">
<ul>
<li ng-repeat="desc in item._source.description" ng-show="$first">
<img class="preview" ng-src="server/{{desc.smallImage.url}}">
</li>
</ul>
</div>
<!-- Item details -->
<div class="item-details">
<div class="product-colors">
<ul class="btn-group pull-right">
<li ng-repeat="color in item._source.description">
<img class="color" ng-src="server/{{color.thumbnailImage.url}}" />
</li>
</ul>
</div>
</div>
</div>
All I wanted to do is that click on one of colors (img.color) changes corresponding img.preview visibility. In my attempts I was always able to changed every img.preview in whole list, not the one I clicked on.
MY ATTEMPTS:
HTML
<li ng-repeat="desc in item._source.description" ng-show="$index === selectedColor">
<li ng-repeat="color in item._source.description" ng-click="changeColor($index)">
JS (controller)
$scope.changeColor = function(idx) {
$scope.selectedColor = idx || 0; //default show always first img.preview from list
};
MY ATTEMPTS #2 (working)
HTML
<li><img class="preview" ng-src="server/{{desc[__selected === $index ? __num : 0].smallImage.url}}">
<li ng-repeat="color in item._source.description" ng-click="changeColor($index, key)">
JS (controller)
$scope.changeColor = function(idx, key) {
$scope.__selected = key;
$scope.__num = idx;
};
This might be quite simple:
Considering desc and color will be referring to same object as they are of the same source.
So, desc and color should be identical and setting a property on either of them supposed to reflect on the other.
Make the changes as follow and try, havent tested though:
<li ng-repeat="desc in item._source.description" ng-show="item.__selected ? desc==item.__selected : $first">
and
<li ng-repeat="color in item._source.description">
<img class="color" ng-src="server/{{color.thumbnailImage.url}}" ng-click="item.__selected = color" />
</li>

Can I use the ng-if on a ng-repeater?

I'm new to angular and was wondering how to tackle the following situation. I'm using angular with jquery mobile and am creating a one page app. When the app loads, I have it create pages or divs inside the body tag. I have two types of pages, "typical pages" and "video pages" but I want to use one object to create both page types. I want to use one object because I also use that object to create my menu and I don't want to have to edit multiple objects.
My issue is, I want to create a "typical page" for each element in the object but I only want to create a "video page" for each object that doesn't have "videoPage: false". I tried using ng-if on the "video page" ng-repeater (see html below) but it doesn't seem to be working. Can I use the ng-if on a ng-repeater?
I have the following data/object:
function App($scope){
$scope.pages = [
{
id: "intro",
title: "Intro",
videoPage: false
},{
id: "activeStretches",
title: "Active Stretches by Area"
},{
id: "passiveStretches",
title: "Passive Stretches by Area"
}
];
}
And the html:
<body ng-app ng-controller="App">
<div data-role="page" id="{{page.id}}" ng-repeat="page in pages">
<div data-role="header" data-theme="h">
<a data-rel="back" data-icon="arrow-l">Back</a>
<h1>{{page.title}}</h1>
</div>
<div data-role="content">
<div class="content-side">
<ul data-role="listview" data-inset="true">
<li ng-repeat="menu in pages" data-theme="{{menu.title == page.title && 'h'}}">
{{menu.title}}
</li>
</ul>
<div class="logo"></div>
</div>
<div class="content-main">
Typical Page
</div>
</div>
</div>
<div data-role="page" id="{{page.id}}Video" ng-repeat="page in pages" ng-if="page.videoPage">
<div data-role="header" data-theme="h">
<a data-rel="back" data-icon="arrow-l">Back</a>
<h1 class="vidTitle"></h1>
</div>
<div data-role="content">
<div class="content-side">
<ul data-role="listview" data-inset="true">
<li ng-repeat="menu in pages" data-theme="{{menu.title == page.title && 'h'}}">
{{menu.title}}
</li>
</ul>
<div class="logo"></div>
</div>
<div class="content-main">
Video Page
</div>
</div>
</div>
</body>
ng-if is new as of 1.1.15 so make sure you're using a new (currently unstable) version of Angular.
And, yes, you can mix ng-if with ng-repeat. Here's a fiddle showing it in action (using 1.2): http://jsfiddle.net/rbQry/1/
Note this line (switch the ng-if to "true" and the repeat comes back):
<li ng-repeat="phone in phones | filter:query" ng-if="false">

Resources