Modifying DOM (slideDown/slideUp) with AngularJS and jQuery

sp00m picture sp00m · Mar 26, 2014 · Viewed 23.1k times · Source

I'm trying to implement a slideDown/slideUp animation with AngularJS. I can't use CSS3's transition (unfortunately) since the height is set to auto (and I don't want to use the max-height workaround), so I'm trying to use jQuery's slideToggle method.

Given the following markup:

<ul>
    <li ng-repeat="entry in data">
        <span>{{entry.title}}</span>
        <a ng-click="clicked($event)" href>more?</a>
        <p>{{entry.description}}</p>
    </li>
</ul>

I implemented the following method in my controller:

$scope.clicked = function($event) {
    var a = jQuery($event.target);
    var p = a.next();
    p.slideToggle();
};

FIDDLE

Even if it seems to work as expected, I understood that modifying DOM shall be done exclusively within directives.

After having read AngularJS' documentation (which I find a bit light IMHO), directives are still a bit vague to me, so could anyone tell me whether the following directive respects AngularJS's best pratices or not?

.directive('testDirective', [
function() {
    return {
        restrict: 'A',
        scope: {
            entry: '=testDirective'
        },
        template: '<span>{{entry.title}}</span> ' +
                  '<a ng-click="clicked($event)" href>more?</a>' +
                  '<p>{{entry.description}}</p>',
        link: function(scope, element) {
            var p = jQuery(element.find('p'));
            scope.clicked = function($event) {
                p.slideToggle();
            };
        }
    };
}])

FIDDLE

Could it be improved? Am I allowed to use jQuery within a directive? Does it respect the separation of concerns?

Answer

LostInComputer picture LostInComputer · Mar 26, 2014

Alternatively, you can use AngularJS's $animate:

.animation('.slide', function() {
    var NG_HIDE_CLASS = 'ng-hide';
    return {
        beforeAddClass: function(element, className, done) {
            if(className === NG_HIDE_CLASS) {
                element.slideUp(done); 
            }
        },
        removeClass: function(element, className, done) {
            if(className === NG_HIDE_CLASS) {
                element.hide().slideDown(done);
            }
        }
    }
});

Use ng-hide or ng-show to show or hide the description.

    <li ng-repeat="entry in data">
        <span>{{entry.title}}</span>
        <a ng-click="expand = !expand" href="#">more?</a>
        <p class="slide" ng-show="expand">{{entry.description}}</p>
    </li>

See JSFiddle

P.S. you must include jquery and angular-animate.js