Angular ng-class code - angularjs

I am very new to Angular and I have this piece of code:
<nav ng-controller="navController">
<ul>
<li ng-class="{'active': location('/') || location('/homepage')}">
view puppies
</li>
</ul>
</nav>
With the corresponding controller:
obApp.controller('navController', function($scope, $location) {
$scope.location = function(loc){
if(loc == $location.path()){
return true;
}else{
return false;
}
}
})
Now everything works fine. When I'm on the homepage the list element gets the class active added to it, HOWEVER there is also the angular ng-class directive code shown in the source, like so (this is the HTML source output of the browser):
<li ng-class="{'active': location('/') || location('/homepage')}" class="active">
view puppies
</li>
Should the list element not look like: <li class="active">view puppies</li> ? Logic would dictate that it should look like it is now, because ng-controller="x" shows in the source as it is as well. However that's a hook if you will. The other one's an expression. It looks funky and I'm not sure if it's ok or not. Please, if you answer, detail your answer.

There is nothing wrong with your code, ng-class directive doesn't remove the attribute ng-class from the element when the expression evaluates to true, since you could have more than one expression/class in your ng-class attribute value.
Additionally if the directive did remove the attribute (ng-class) angular would have no way of knowing that you have applied that directive to the element. For example, your element had ng-if on it, it would be removed from the DOM and appended conditionally.
Example
ng-class="{'active': location('/') || location('homepage'), 'disabled': location('private'))}"

Such usage of ng-class is non-effective because $scope.location method will be called at least twice (for location('/') and location('homepage')) each digest loop. There could be a lot of digest loops due to balancing mechanism.
Better approach is to calculate if element is active on location change.
Controller:
$scope.$on('$locationChangeSuccess', function(){
$scope.isHomePage = location('/') || location('/homepage');
});
Markup:
<nav ng-controller="navController">
<ul>
<li ng-class="{'active': isHomePage}">
view puppies
</li>
</ul>
</nav>

Related

AngularJs scrollTo issue

I am making a menu with AngularJs and I want that, clicking on the item, the page will scroll to the section of the clicked item.
My code is the following:
script.js
var app = angular.module('allApps',['ui.bootstrap']);
app.controller("menuCtrl",function($scope, $location, $anchorScroll){
$scope.menuItems=[
{page:"Biography", id:"bio"},
{page:"Curriculum Vitae", id:"cv"},
{page:"Gallery", id:"gallery"},
{page:"Video", id:"video"},
{page:"Press", id:"press"},
{page:"News", id:"news"},
{page:"Contact", id:"contact"}
];
$scope.scrollTo = function(id) {
$location.hash(id);
console.log($location.hash());
$anchorScroll();
};
});
menu.html
<ul class="nav nav-pills" id="mainMenu" ng-controller="menuCtrl">
<li class="active">Home</li>
<li ng-repeat="item in menuItems"><a ng-click="scrollTo('{{item.id}}')" href="">{{item.page}}</a>
</ul>
the code like this doesn't work but, if I write explicitly
ng-click="scrollTo('bio')"
it does (obviously it will scroll all the links to biography page).
I thought it was a problem in reading AngularJs directions but if I check it with firebug I can see it takes the correct id's.
May you tell me what is wrong?
You had incorrect expression in ng-click directive, basically it should not contain {{}} interpolation directive in it. The expression which you provide to ng-click will directly evaluate with the context of controller
ng-click="scrollTo(item.id)"

Angular JS ng-repeat and the 'this'

i'm new in AngularJS, but did some jQuery before. i've got a problem to understand how to get the clicked element / it's parent to make some changes like change the text, an icon or a class in the item where i made the click.
the simple HTML:
<ul ng-controller="basketCtrl">
<li ng-repeat="item in item">
<button ng-click="addToBasket(Itemid,this,whatever)">
<i class="myBasketicon">
<span>Buy now</span>
</button>
</li>
</ul>
what i want to do:
$scope.addTobasket = function (id, elem, whatever){
// to some JSON-Server-stuff - that works perfect
// now my problems, :
//change this -> myBasketIcon -> myOKicon
//change this -> span text Buy now-> Thanks for buying
// give the this -> li an class => 'changed'
}
I really tried a lot, f.e with ng-model in the tags, arrays... search the web half the day... but didn't find anything that matches my problem.
Maybe it's just the way of thinking not the angular way... so please help :O)
Kind regard from Hamburg, Germany
Timo
You should be able to do this by changing a property (angular way), no need to access the element in the ng-click handler,and using ng-class and angular binding on that property.
<ul ng-controller="basketCtrl">
<li ng-repeat="item in items" ng-class="{'changed': item.added}">
<button ng-click="addToBasket(item)">
<i ng-class="{'myBasketicon':!item.added,'myOKicon':item.added }">
<span>{{item.added ? "Thanks for buying" : "Buy now"}}</span>
</button>
</li>
</ul>
and in your handler just do:
$scope.addTobasket = function (item){
item.added = true;
}
Most cases, whole purpose of using angular is to avoid DOM manipulation and let angular manage it, you just deal with the models/viewmodels and bindings.
You should add methods for the icon class and text that change their results based on the state of the object, or use custom a custom directive. You definitely don't want to be doing any DOM manipulation (changing text/classes etc) the way you would have done with jQuery.
For the method-based approach, something like this for your markup:
<li ng-repeat="item in item">
<button ng-click="addToBasket(item)">
<i ng-class="getClass(item)">
<span>{{getMessage(item)}}</span>
</button>
</li>
and on your controller:
.controller('ShoppingListCtrl', function($scope) {
$scope.getClass = function(item) {
return item.inBasket ? 'myOkIcon' : 'myBasketIcon';
};
$scope.getMessage = function(item) {
return item.inBasket ? 'Thanks for buying' : 'Buy now';
};
})
This could also be done with a custom directive which is a super powerful way to do things (and definitely worth figuring out) but may be overkill for just starting out. If you find you are adding a lot if methods for doing these sorts of things go with directives.

Uncertainty about the use of ngInclude

I render recursive template with ngInclude. Here is my code:
<!--
definitionsRenderer.html
-->
<ul data-ng-if="currentLoc.definitions && templateIsloaded">
<li ng-repeat="definition in currentLoc.definitions">
{{definition.text}}
</li>
</ul>
<ul data-ng-if="templateIsloaded">
<li ng-repeat="loc in currentLoc.loc" ng-include="'./partials/definitionsRenderer.html'"></li>
</ul>
I need code to be executed asynchronous. So for scope of every included template
I want to have a variable - templateIsloaded with value false by default. When template is loaded and rendered it value to be true. How do that?
Best regards.
To have separate $scope for each of recursive called includes you can simply add ng-controller.
<li ng-repeat="loc in currentLoc.loc" ng-controller="definitionsRendererCtrl" ng-include="'./partials/definitionsRenderer.html'"></li>

How to change the content of a tab while you are on the same tab using AngularJS and Bootstrap?

Using AngularJS and Bootstrap, let say there are 3 tabs: tab1, tab2, and tab3. There are also some links on each tabs. Now for example, tab1 is active. The question is: how to change the content of the tab1 by clicking a link within the same tab?
main.html:
<div class="tabbable">
<ul class="nav nav-tabs">
<li ng-class="{active: activeTab == 'tab1'}"><a ng-click="activeTab = 'tab1'" href="">tab1</a></li>
<li ng-class="{active: activeTab == 'tab2'}"><a ng-click="activeTab = 'tab2'" href="">tab2</a></li>
<li ng-class="{active: activeTab == 'tab3'}"><a ng-click="activeTab = 'tab3'" href="">tab3</a></li>
</ul>
</div>
<div class="tab-content">
<div ng-include="'/'+activeTab"></div>
</div>
tab1.html:
<h1>TAB1</h1>
Something
something.html
<h1>SOMETHING</h1>
Now the question is how to change the tab1 content to something.html while the tab1 is active?
As pointed out in other examples there are many ways to do this. Direct DOM manipulation is not really the Angular way of thinking about this kind of use case. A better way to think about it might be:
What possible content can this tab contain?
What will control its' being displayed?
Using the ng-if or ng-switch directive allows you to selectively limit the content based on a variable defined in the scope.
Consider this possibility:
<div class="tabbable">
<ul class="nav nav-tabs">
<li ng-class="{active: activeTab == 'tab1'}"><a ng-click="activeTab = 'tab1'" href="">tab1</a></li>
<li ng-class="{active: activeTab == 'tab2'}"><a ng-click="activeTab = 'tab2'" href="">tab2</a></li>
<li ng-class="{active: activeTab == 'tab3'}"><a ng-click="activeTab = 'tab3'" href="">tab3</a></li>
</ul>
</div>
Based on your code for the included file you could do this:
<div class="tab-content">
<div ng-if="content==='A'" ng-include="'/'+activeTabA"></div>
<div ng-if="content==='B'" ng-include="'/'+activeTabB"></div>
</div>
Another approach is to utilize ng-view and routing. It is more complicated than conditionally including but less complicated than writing a whole new directive.
In short, you define a container element with the ng-view attribute like this
<div ng-view></div>
Then set up a routing table in your javascript code like this
$routeProvider.when('/tab1', {templateUrl: 'partials/tab1.html', controller: 'tab1Controller'});
$routeProvider.when('/tab2', {templateUrl: 'partials/tab2.html', controller: 'tab2Controller'});
For more detail see this link: http://docs.angularjs.org/api/ngRoute.directive:ngView
This is actually a fairly common AngularJS issue, and is handled pretty nicely by nested directives. It works well enough that it's actually one of the demos in the Custom Directive Guide on the AngularJS docs page. It's the last example, "Creating Directives that Communicate". You can see the full code there, but the idea is that you create a 'container' directive for the tab group and a 'pane' directive for the inside content. Your HTML ends up looking like this:
<my-tabs>
<my-pane title="Hello">
<h5 id="pane1">Hello</h5>
<p>This content is in the first pane.</p>
</my-pane>
<my-pane title="World">
<h5 id="pane2">World</h5>
<em>This content is in the second pane.</em>
</my-pane>
</my-tabs>
As #musically_ut pointed out in his comment, there are a lot of ways to handle this. This is just one way, but I think it works out pretty well.

Angular switch in repeater

Given a list such as
var list = ['one','two','three'];
In angular, I want to iterate through the list only rendering certain items. Something like:
<ul ng-controller="main">
<li ng-repeat="item in list" ng-switch on="item">
<span ng-switch-when="one">{{item}}</span>
</li>
</ul>
And have the output look like this:
<ul>
<li><span>one</span></li>
</ul>
Instead, I get:
<ul>
<li><span>one</span></li>
<li></li>
<li></li>
</ul>
I have tried ng-hide but is woefully inefficient since I have a large number of items and only want to display one or two and ng-hide renders all of them and then hides the inactive ones with css. This is a problem because I am doing this in a JQuery Mobile app which tries to decorate all list items, including the hidden ones, killing performance.
JSFiddle at http://jsfiddle.net/ghendricks/MXu3a/
You are correct that ng-hide should not be used here, it is a job for filters.
You can provide a custom function to filter the list: http://jsfiddle.net/ERMVj/
$scope.selectOne = function (input) { return input == "two" || input == "one"; };
<li ng-repeat="l in list | filter:selectOne">
<span>{{l}}</span>
</li>

Resources