Puneet Mahendra Puneet Mahendra - 4 months ago 27
AngularJS Question

toggling one directive effects other dircetive inside ngrepeat

I wrote an angularjs directive to show and hide ajax spinners. The visibility of the spinner is toggled by show and hide buttons whose functionality is written inside the MainController. There is a variable inside the controller which is set to true and false based on the button click. This variable is passed to the directive using isolate scope. When I try to toggle one spinner, all the other spinners are also visible. How can I change my code to only toggle the particular spinner.

https://plnkr.co/edit/AFmBVbHaBPk66T7UjPC5?p=preview



// Code goes here
angular.module('app',[])
.controller('MainController',[MainController])
.directive('loadingDirective',[loadingDirective]);
function MainController(){
var mc = this;
mc.showMe = showMe;
mc.hideMe = hideMe;
mc.loading = false;
function showMe(){
mc.loading = true;
}
function hideMe(){
mc.loading = false;
}
}
function loadingDirective() {
return {
restrict: 'E',
replace:true,
scope:{
loading:"=loading"
},
template: '<span class="spinner">Loading…</span>',
link: function (scope, element, attr) {
scope.$watch('loading', function (val) {

if (val)
$(element).show();
else
$(element).hide();
});
}
};
}

/* Styles go here */

.spinner {
position: relative;
/* [1] */
display: inline-block;
width: 1em;
/* [2] */
height: 1em;
/* [2] */
font-size: 32px;
/* [3] */
border-bottom: 1px solid;
/* [4] */
vertical-align: middle;
overflow: hidden;
/* [5] */
text-indent: 100%;
/* [5] */
-webkit-animation: 0.5s spinner linear infinite;
animation: 0.5s spinner linear infinite;
/**
* 1. Make the spinner a circle.
*/
/**
* The (optically) non-spinning part of the spinner.
*
* 1. Border around entire element fills in the rest of the ring.
* 2. Paler than the part that appears to spin.
*/
}
.spinner, .spinner:after {
border-radius: 100%;
/* [1] */
}
.spinner:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: 1px solid;
/* [1] */
opacity: 0.5;
/* [2] */
}

/**
* Size variants (built by adjusting `font-size`).
*/
.spinner--small {
font-size: 16px;
}

.spinner--large {
font-size: 64px;
}

/**
* Color overrides.
*/
.spinner--light {
color: #fff;
}

.spinner--dark {
color: #333;
}

@-webkit-keyframes spinner {
to {
-webkit-transform: rotate(360deg);
}
}
@keyframes spinner {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}

<!DOCTYPE html>
<html ng-app="app">

<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>

<body>
<h1>Hello Plunker!</h1>
<div ng-controller="MainController as mc">
<div ng-repeat="i in [1,2,3,4,5]">
<loading-directive loading="mc.loading"></loading-directive>
<button ng-click="mc.showMe()">show</button>
<button ng-click="mc.hideMe()">hide</button>
</div>
</div>
</body>

</html>




Answer

If you want the spinners to have their own states, then they should be controlled by different variables.

In your example it is achievable by using an array to hold the variables

<div ng-repeat="i in [1,2,3,4,5]">
    <loading-directive loading="mc.loading[i]"></loading-directive>
    <button ng-click="mc.show(i)">show</button>
    <button ng-click="mc.hide(i)">hide</button>
</div>

mc.loading = {};
function show(i){
  mc.loading[i] = true;
}
function hide(i){
  mc.loading[i] = false;
}

In a more real case example where you have some data and you use ng-repeat over them, you should assign the loading states inside the elements themselves.

This is a common technique to assign state to each items in ng-repeat

vm.fruits = [
    {name:"apple"},
    {name:"orange"},
    {name:"starfruit"}
]

function load(fruit) { fruit.loading = true; }
function noLoad(fruit) { fruit.loading = false; }

<div ng-repeat="fruit in vm.fruits">
    <loading-directive loading="fruit.loading"></loading-directive>
    {{fruit.name}}
    <button ng-click="mc.load(fruit)">show</button>
    <button ng-click="mc.noLoad(fruit)">hide</button>
</div>