Osy
Osy

Reputation: 1633

Angular directive and Jquery slideToggle function implementation

Using angular and jquery I implemented slideToggle function. In order to only apply this function to one specific HTML element, I am using within ng-click function one parameter, my code works fine but, I want to know if exists another better way to implement directives and ng-click functions in angular:

index.html

<!DOCTYPE html>
<html ng-app="myApp" ng-controller="MainController">
<head>
    <title></title>
    <link type="text/css" rel="stylesheet" href="css/styles.css"/>
</head>
<body>
    <div>
        <input type="button" ng-click="toggle('first')" value="Toggle First">
        <input type="button" ng-click="toggle('second')" value="Toggle Second">
        <input type="button" ng-click="toggle('third')" value="Toggle third">
        <div class="div1" section="first" toggle="first" >
            <p>This is section #1</p>
        </div>
        <div class="div1" toggle="second">
            <p>This is another section #1</p>
        </div>
        <div class="div1" toggle="third">
            <p>This is 3 section #1</p>
        </div>
    </div>
</body>
<footer>
    <script src="js/jquery.min.js"></script>
    <script src="js/angular.js"></script>
    <script src="js/directives.js"></script>
</footer>
</html>

styles.css

.div1 {
    background: Brown;
    width: 200px;
    height: 200px;
    text-align: center;
}
.div1 p {
    position: relative;
    top: 50%;
}

directives.js

angular.module("myApp", []) //
    .controller('MainController', function($scope) {
        $scope.toggle = function(section) {
            console.log('<toggle function> section :' + section);
            $scope.section = section;
            $scope.$broadcast('event:toggle');
        }
    }) //
    .directive('toggle', function() {
        return function(scope, elem, attrs) {
            scope.$on('event:toggle', function() {
                if(attrs.toggle == scope.section){
                    elem.slideToggle('fast');
                }
            });
        };
    });

One concern of my own is the way that I am communicating between directive and scope:

        $scope.section = section;

and

        if(attrs.toggle == scope.section){

I will appreciate any advice for a better Angular implementation.

Thanks

Upvotes: 7

Views: 25402

Answers (3)

GFoley83
GFoley83

Reputation: 3569

Plunker Demo: http://plnkr.co/edit/u69puq?p=preview

FYI, at the time of writing, the AngularJS team are adding an ngAnimate directive which will (hopefully) offer most of the animate functionality that jQuery currently offers, e.g. Slide, Fade etc, which is sorely missing at the moment IMO.

HTML

<div data-ng-controller="MainController">      
    <input type="button" value="Toggle First" ng-click="box1=!box1">
    <input type="button" value="Toggle Second" ng-click="box2=!box2">
    <input type="button" value="Toggle third" ng-click="box3=!box3">

    <div class="div1" data-slide-toggle="box1" data-slide-toggle-duration="100" >
        <p>This is section #1</p>
    </div>
    <div class="div2" data-slide-toggle="box2" data-slide-toggle-duration="2000">
        <p>This is another section #1</p>
    </div>
    <div class="div3" data-slide-toggle="box3">
        <p>This is 3 section #1</p>
    </div>
</div>

JS

var myApp = angular.module("myApp", []);

myApp.controller('MainController', function($scope) {
  $scope.box1 = $scope.box2 = $scope.box3 = true;
});

myApp.directive('slideToggle', function() {  
  return {
    restrict: 'A',      
    scope:{
      isOpen: "=slideToggle"
    },  
    link: function(scope, element, attr) {
      var slideDuration = parseInt(attr.slideToggleDuration, 10) || 200;      
      scope.$watch('isOpen', function(newVal,oldVal){
        if(newVal !== oldVal){ 
          element.stop().slideToggle(slideDuration);
        }
      });
    }
  };  
});

Upvotes: 14

evoratec
evoratec

Reputation: 59

After trying the solution of Peter i can see that directives below slideToggle cannot acces to parent scope. This is my solution.

evoapp.directive('slideToggle', function() {
    return {
        restrict: 'A',
        scope: false ,
        link: function(scope, element, attr) {
            var slideDuration = parseInt(attr.slideToggleDuration, 10) || 200;
            var toggle = attr.toggle ;

            scope.$watch(toggle, function(newVal,oldVal){
                if(newVal !== oldVal){
                    element.stop().slideToggle(slideDuration);
                }
            });
        }
    };
});

Upvotes: 0

Peter
Peter

Reputation: 109

@GFoley83 This helped me out a lot thanks! I'm a newbie, so not sure if this is the right way to add a suggestion to help, sorry if it's not. I'm also no expert, so i'm happy for my suggestion to be improved.

I needed the item to start hidden and the ng-init didn't seem to do anything. So, I've added a fourth box with an attribute set to start hidden

Using the code @GFoley83 provided I have an additional attribute "start shown" added to the div which will be toggled.

 <input type="button" value="Toggle Fourth" ng-click="box4=!box4">
 <div class="div1" data-slide-toggle="box4" data-start-shown="false">
    <p>This is 4 section and starts hidden</p>
 </div>

The directive has also been updated and now has a check on this new attribute.If it's passed false, we'll hide - otherwise it will continue as before and start by showing the item.

     return {
        restrict: "A",
        scope: {
            isOpen: "=slideToggle"  // attribute
        },
        link: function (scope, element, attr) {
            var slideDuration = parseInt(attr.slideToggleDuration, 10) || 200;
            if (attr.startShown=="false") {
                element.hide();
            }
            scope.$watch('isOpen', function (newVal, oldVal) {
                if (newVal !== oldVal) {
                    element.stop().slideToggle(slideDuration);
                }
            });
        }
    };

Plunker updated : http://plnkr.co/edit/WB7UXK?p=preview

Upvotes: 4

Related Questions