Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialjdh
12,334 PointsShow & Hide Div by Changing Class to Active - AngularJS
I've been working on an application that has quite a few different sections and have been showing and hiding the sections based on which icon the user clicks
For instance, this is what the html would look like
<div ng-show='One'>
<p>Section One</p>
</div>
<div ng-show='Two'>
<p>Section Two</p>
</div>
<div ng-show='Three'>
<p>Section Three</p>
</div>
<!-- Navigation -->
<nav>
<a href='#' ng-click='showOne'> Show Div One </a>
<a href='#' ng-click='showTwo'> Show Div Two </a>
<a href='#' ng-click='showThree'> Show Div Three </a>
</nav>
Then in my JS I would have several functions that set the ng-show to true when the appropriate icon is click and the reset to false (hiding the section).
$scope.one = true; // setting the first div visible when the page loads
$scope.two = false; // hidden
$scope.three = false; // hidden
// Now have three functions that change the ng-show based on the click
$scope.showOne = function (){
$scope.one = true;
$scope.two = false;
$scope.three = false;
}
$scope.showTwo = function (){
$scope.one = false;
$scope.two = true; // now show this one
$scope.three = false;
}
As you can see, this is pretty redundant code and can be pretty inefficient if we have a lot of different sections.
Can someone show me how to show and hide sections by adding the class Active based on which nav item is click, please? I've been reading the Angular docs and find them kind of confusing yet.
I've set up a Plunker already for this is anyone would like to try: http://plnkr.co/edit/FDuUzm9oDuYHJAoFMmsd?p=preview
Thanks!
5 Answers
Kevin Lozandier
Courses Plus Student 53,747 PointsAlmost ran out of time to get this done before midnight today (PST):
I have time to show you the dirty way to do this (before I have time to write the controller way tomorrow later this week); the dirty way is accomplished by the following code
<div class='row' ng-init="item = 1">
<div class='col-sm-12' ng-show='item == 1'>
<h1>Section One</h1>
</div>
<div class='col-sm-12' ng-show='item == 2'>
<h1>Section Two</h1></h1>
</div>
<div class='col-sm-12' ng-show='item == 3'>
<h1>Section Three</h1>
</div>
</div>
<div class='row'>
<nav>
<!-- What was here before was questionable markup; should be in a list given the *relationship* the links had with each other -->
<ul>
<li ng-class="{active:item == 1}">
<a href='#' ng-click="item = 1">
Show Div One
</a>
</li>
<li i ng-class="{active:item == 2}">
<a href='#' ng-click="item = 2">
Show Div Two
</a>
</li>
<li i ng-class="{active:item == 3}">
<a href='#' ng-click="item = 3">
Show Div Three
</a>
</li>
</ul>
</nav>
</div>
This is bad to do because all this logic in your HTML is a code smell; it's feels very wrong considering the tools of abstraction AngularJs provides developers (and JS in general) to avoid this much logic in your views with its Model-View-Whatever model design.
Worse, this is hard to manage and test—the latter critical for an Angular application.
You need a controller
What you need is a controller to initialize what value corresponds to an active class in the beginning, and then events being delegated to that controller.
Since you're essentially doing common behavior associated with tabs, you'd start doing something like the following (variable names perhaps a bit too simplistic and verbose in some parts):
<div ng-controller="TabController as tabController">
app.controller("tabController", function(){
this.activeTab = 1; // Can be a qualifier other than a numerical value; what matters is a data type that's easy to use comparison operators (==, ===, !=, <=, =>, and so on) with
// selectTab method that changes the active tab, and a tabSelected/isSelected method that returns true or false to change classes with
});
Accordingly, your ng-click and ng-show annotations will refer to the controller to handle all the logic, an example would be the following code snippet.
<li ng-class="{ active: tabController.isTabSelected(2) }"></li>
Hope this helps for now.
jdh
12,334 PointsHey Kevin,
Firstly, thank you for providing some excellent responses and comments. I understand what you're saying in that its best practice to move as much of the logic into a js file.
I've looked into you tab example and came across this snippet
<body ng-controller='PanelController as panel'>
<section>
<ul class='nav nav-pills'>
<li ng-class="{active: panel.isSelected(1) }">
<a href='#' ng-click="panel.selectTab(1)">Tab One</a>
</li>
<li ng-class="{active: panel.isSelected(2) }">
<a href='#' ng-click="panel.selectTab(2)">Tab Two</a>
</li>
<li ng-class="{active: panel.isSelected(3) }">
<a href='#' ng-click="panel.selectTab(3)">Tab Three</a>
</li>
</ul>
</section>
<div class='panel' ng-show='panel.isSelected(1)'>
<h4>Tab one</h4>
</div>
<div class='panel' ng-show='panel.isSelected(2)'>
<h4>Tab Two</h4>
</div>
<div class='panel' ng-show='panel.isSelected(3)'>
<h4>Tab Three</h4>
</div>
</body>
var app = angular.module('demoApp', []);
app.controller('PanelController', function(){
this.tab = 1;
this.selectTab = function(setTab){
this.tab = setTab;
}
this.isSelected = function(checkTab){
return this.tab === checkTab;
}
});
However, now this example brings up a few other questions...
- Why are we using "this" instead of "$scope"?
- Why would this example only work with the Controller-As syntax?
Kevin Lozandier
Courses Plus Student 53,747 PointsHello again, @jdh:
$scope
is a best practice technique and for backwards compatibility; it doesn't matter much for controllers since Angular version 1.2 and above. They're roughly interchangeable.
Don't forget to inject $scope
if you choose to use it, especially when you're comfortable enough with the framework to write tests first (the framework is built to be testable from the very beginning and testing is heavily encouraged before any implementation details are done).
I actually always use $scope
, but used this
in my example because it's usually more understandable for developers unfamiliar with Angular's opinionated ways of doing things but comfortable with JavaScript enough to understand how this
works.
The syntax I used to access my controller in the view isn't required.
Kevin Lozandier
Courses Plus Student 53,747 PointsNote the example can be improved (more flexible, even less logic in the view) further more with directives; I recommend reviewing the Treehouse's Angular JS course
Kevin Lozandier
Courses Plus Student 53,747 PointsHi, jdh:
It seems the behavior you're attempting to accomplish would be better done through a controller with the controller keeping tab on what tab should be active, with one function changing the state of the one property that dictates what is the active element.
Accordingly, you can then use ng-class
to only show/hide that particular active element; ng-class
allows you to insert a particular class based on a particular condition has passed.
An example would be ng-class=" {active: contract === true }"
Does that make sense?
jdh
12,334 PointsHey Kevin,
Thanks for the response. I kind of understand what you're getting at but not entirely. I think what you're saying is to add a function in the controller that would look something like this:
$scope.select = function(item){
$scope.selected = item;
}
then in the html have the below
<!-- on the nav anchor items -->
<a href="#" ng-click='select(item)'>Item One</a>
<!-- on the div that we want to show and hide -->
<div ng-class="{active: selected === true}">
<p></p>
</div>
Is that somewhat close?
FWIW, I came up with another way to show and hide than my original way however I'd like to try to figure out how to add ng-class to active so that I can apply certain CSS to active icons, etc.
Here's what I did
<div class='row'>
<div class='col-sm-12' ng-show='item == 1'>
<h1>Section One</h1>
</div>
<div class='col-sm-12' ng-show='item == 2'>
<h1>Section Two</h1></h1>
</div>
<div class='col-sm-12' ng-show='item == 3'>
<h1>Section Three</h1>
</div>
</div>
<div class='row'>
<nav>
<a href='#' ng-click="item = 1">
Show Div One
</a>
<a href='#' ng-click="item = 2">
Show Div Two
</a>
<a href='#' ng-click="item = 3">
Show Div Three
</a>
</nav>
</div>
Kevin Lozandier
Courses Plus Student 53,747 PointsHey, jdh:
Your code, currently omitting a controller, would greatly benefit from a controller. The controller can then manage the initial link that's active that can also also be set with ng-init
in your view.
You then can have a method within the controller be called with the use of ng-click
to change what is currently selected.
You can than use ng-class
to have certain classes attached to the active nav item based on criteria that makes the most sense for your app.
In fact, you can even have the controller be aware of an Angular service (the way you share data that can be used throughout your app in one, centralized location for the rest of your app's controllers and directives to use ), that has all the link names and the link they correspond to through a property within the controller that you can then use ng-repeat
in the view with to reduce the repetitiveness of your code.
If you need more clarification, I'm more likely able to provide code snippets this weekend due to my limited availability this Friday.
jdh
12,334 PointsHi Kevin Lozandier ,
I'd be great if you could provide a snippet for this. I have an idea of what you're saying in that I could manually set one of the nav items to active and then create a function in the Controller which would add and remove the active class =,
Appreciate the help!
Kevin Lozandier
Courses Plus Student 53,747 PointsHi, jdh:
I didn't realize it'll be President's Day tomorrow; I'm going to delay a code snippet reply until sometime tomorrow due to my limited availability this evening to address the question via a code snippet.
That said, you're interpretation of what I recommended with my earlier responses is correct when it comes to a controller being important for your use case to be reusable, performant, and testable
jdh
12,334 PointsThanks again Kevin Lozandier!
jdh
12,334 Pointsjdh
12,334 PointsKevin Lozandier