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 trial

JavaScript

jdh
jdh
12,334 Points

Show & 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

Almost 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
jdh
12,334 Points

Hey 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...

  1. Why are we using "this" instead of "$scope"?
  2. Why would this example only work with the Controller-As syntax?

Hello 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.

Note 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

Hi, 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
jdh
12,334 Points

Hey 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>

Hey, 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
jdh
12,334 Points

Hi 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!

Hi, 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
jdh
12,334 Points

Thanks again Kevin Lozandier!