Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am changing a user list/table I made with Razor and JQuery into AngularJS. One of the features of this table was the ability to show/hide additional user details. The table had two sets of tr with the second one being display:none but would slide down/be visible when its above tr was clicked to show details.

Example (Original) Working Example

enter image description here

I tried to apply the code onto my AngularJS but it does not seem to work for some reason (no errors either)

Original Code (skeleton)

<table>
   <tr class="col-xs-12">
            <td class="col-xs-2">@Html.DisplayFor(modelItem => item.name)</td>
            <td class="col-xs-2">@Html.DisplayFor(modelItem => item.email)</td>
            // ect...
   </tr>
   <tr class="col-xs-12" style="display:none">
      <td colspan="12">
         <p>
            @Html.DisplayFor(other stuff)
         </p>
      </td>
   </tr>
</table>

New AngularJS Code

<table class="table table-striped col-xs-12">
    <tbody ng-repeat="actors in Users">
        <tr class="col-xs-12">
            <td class="col-xs-2">{{actors.name}}</td>
            <td class="col-xs-2">{{actors.email}}</td>
            // ect...
        </tr>
        <tr class="col-xs-12" style="display:none">
            <td colspan="6">
                <p>
                    {{actors.otherThings}}
                </p>
            </td>
        </tr>
    </tbody>
</table>

JQuery (Original had td[colspan=12] instead of td[colspan=6])

<script>
    // Toggle Additional Details
    $(function () {
        $("td[colspan=6]").find("p").hide();
        $("td[colspan=6]").addClass("nopadding");

        // && !$(e.target).is('span')
        $("tr").click(function (e) {
            if (!$(e.target).is('button') && !$(e.target).is('input') && !$(e.target).is('select')) {
                var $target = $(this);
                var $detailsTd = $target.find("td[colspan=6]");
                if ($detailsTd.length) {
                    $detailsTd.find("p").slideUp();
                    $detailsTd.addClass("nopadding");
                } else {
                    $detailsTd = $target.next().find("td[colspan=6]");
                    $detailsTd.find("p").slideToggle();
                    $detailsTd.toggleClass("nopadding");
                }
            }
        });
    });
</script>

CSS

/* Removes padding from interactive rows */
.table > tbody > tr > td.nopadding {
    padding: 0px;
}

I am still new to AngularJS so maybe I am missing something simple, but I just want to be able to expand show/hide the additional tr. Not sure why it does not work for my Angular code.

AngularJS

var app = angular.module("app", ['ngRoute', 'ngResource']);

app.config(function ($routeProvider) {
    $routeProvider

    .when('/', {
        templateUrl: '/pages/home.html',
        controller: 'HomeController'
    })

    .when('/home', {
        templateUrl: '/pages/home.html',
        controller: 'HomeController'
    })

    .when('/unknown', {
        templateUrl: '/pages/unknown.html',
        controller: 'UnknownController'
    })
      .otherwise({
          templateUrl: '/pages/home.html',
          controller: 'HomeController'
      });
});


app.factory('userService', function ($http) {
    var userService = {};
    userService.getUsers = function () {
        return $http({
            url: '/API/APITest',
            method: "GET"
        })
    }
    return userService;
});


app.controller('HomeController', function ($scope, userService, $resource, $http) {
    $scope.orderByField = 'name';
    $scope.reverseSort = false;
    $scope.ordering = "(ascending)";

    $scope.orderByFieldFunction = function (value) {
        $scope.reverseSort = !$scope.reverseSort;
        $scope.orderByField = value;
        if ($scope.reverseSort === false) {
            $scope.ordering = "(ascending)";
        } else {
            $scope.ordering = "(descending)";
        }
    }
    userService.getUsers().success(function (users) {
        $scope.Users = users;
    });
});

app.controller('UnknownController', function ($scope, userService, $resource, $http) {
    $scope.title = "404 - Does Not Exist";
});
share|improve this question
    
Where is your Angular code? –  putvande Aug 14 at 14:13
    
@putvande Oh is that even needed? I figured this is a JQuery issue since it is only DOM manipulation... I can post though if you want. –  Austin Aug 14 at 14:16
    
You are asking how to change your show / hide etc. jQuery functions to Angular. –  putvande Aug 14 at 14:19
    
@putvande updated. –  Austin Aug 14 at 14:25
    

2 Answers 2

up vote 1 down vote accepted

You can add the jQuery in a directive like this, if this is what you're after:

Example: http://jsfiddle.net/58mLfw6z/

AngularJS Directive

app.directive('showmore',function() {
    return {
        restrict:'A',
        link: function(scope,element,attr) {
            element.on('click',function() {
              element.next('tr').toggle();
            });    
        }
    }
});

// or apply a flag to the model with an ng-click on the tr
$scope.more = function(actor) {
    if (!actor.more) {
        actor.more = true;
    }
    else {
        actor.more = false;
    }
};

HTML

<table>
    <tbody ng-repeat="actors in Users">
        <tr showmore ng-click="more(actors)">
            <td class="col-xs-2">{{actors.name}}</td>
            <td class="col-xs-2">{{actors.email}}</td>
        </tr>
        <tr style="display:none;">
            <td colspan="2">
                <p>
                    {{actors.otherThings}}
                </p>
            </td>
        </tr>
    </tbody>
</table>
share|improve this answer
    
Preferably I do not want a button or anything. With the other code I could click the tr above, or the details tr itself to expand/close. See example HERE. Clicking the tr triggers it all. Is this possible with Angular? –  Austin Aug 14 at 14:31
    
Updated my answer, however, if you're still having issues understanding this, them you might need to revisit jQuery traversing and how AngularJS directives work. –  Rob Aug 14 at 14:36
    
Gotcha, I am new to Angular though and I figured there'd be a cleaner approach than having to use a directive, I've been told that I shouldn't be touching those while starting new and that something like ng-click or ng-class should suffice. Thoughts? –  Austin Aug 14 at 14:38
    
Careful, toggle is not supported by Angular's jqLite implementation you actually need to load jQuery before Angular to get this ability. –  m.e.conroy Aug 14 at 14:52
1  
@Austin yes there is a way to do this via ng-click by passing the element clicked to a function in the controller that would then execute the same code this directive does, but its not a good idea to do DOM manipulation in a controller, its suggested that DOM manipulation be done in a directive. Whomever told you not to use directives is completely wrong, directives are the true power of Angular. –  m.e.conroy Aug 14 at 14:56

I think OP already has found the answer. This post is just to say that ng-repeat has its own scope, an isolated scope.

In this plnkr, http://jsfiddle.net/aanders50/HB7LU/5497/, which OP posted in the comment

<table>
    <tr>
        <th>Name</th>
        <th>Email</th>
    </tr>
    <tbody ng-repeat="actors in Users">            
        <tr ng-click="showDetails = ! showDetails">
            <td>{{actors.name}}</td>
            <td>{{actors.email}}</td>
        </tr>
        <tr ng-show="showDetails" style="background-color:rgba(0, 255, 255, 0.2)">
            <td colspan="2">
                <p>birthday: {{actors.birthday}}</p>
            </td>
        </tr>
    </tbody>
</table>

showDetails are bound to each tbody where ng-repeat is defined. Thus, each table has its own $scope.showDetails

share|improve this answer
    
That was gonna be my next question as to why my answer worked. Thanks for the input :) –  Austin Aug 14 at 18:11
    
My point is that you just created a crazy amount of $$watchers and scoped variables for each item in your Users array, when one directive would've accomplished the same feat while not creating any $$watchers to check during the $digest cycle because it uses jQuery's .on(). Read the More DOM manipulation in Directives section of this article: binpress.com/tutorial/… –  m.e.conroy Aug 14 at 18:54
    
@Rob 's directive above in my opinion is still the right way to go. Its small more readable and doesn't add to Angular bottlenecks in the $digest cycle. –  m.e.conroy Aug 14 at 18:57
    
I see, maybe I should use that then. Would something like pagination help reduce that issue or will it still bind everything on each page ahead of time? –  Austin Aug 14 at 19:17

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.