Reputation: 1645
I have a button group with some buttons, two created with html and others are created with a ng-repeat. I want that on click the button have an active class so I can custom it to show it's activated.
So here is what I do :
<div class="btn-group" role="group" aria-label="Basic example"
ng-init="selectedTab = 'raw'">
<button class="btn"
ng-click="selectView('raw'); selectedTab = 'raw'; console.log(selectedTab);"
ng-class="{'active':selectedTab === 'raw'}">Raw data
</button>
<button class="btn"
ng-click="selectView('summary'); selectedTab = 'summary'; console.log(selectedTab);"
ng-class="{'active':selectedTab === 'summary'}">Summary
</button>
<button class="btn" ng-repeat="(key, value) in views"
ng-click="selectView(key); selectedTab = key; console.log(selectedTab);"
ng-class="{'active':selectedTab === key}">
{{ key }}
</button>
</div>
My problem is that for the two first one all works fine, when I click on the first button the class active is added, and when I click on the second the class is removed from the first one and added to the second one.
The problem is about the buttons generated by the ng-repeat, when I click on them it's add the active class to the button but when I click on another button it's not removing the class, so they can all have the activate class.
What am I doing wrong ?
Upvotes: 0
Views: 1985
Reputation: 48968
New AngularJS developers often do not realize that ng-repeat
, ng-switch
, ng-view
, ng-include
and ng-if
all create new child scopes, so the problem often shows up when these directives are involved.
The assignment selectedTab = key
is being done on the child scope created by the ng-repeat
directive. The solution is to do the assignment to a property of an object:
<div class="btn-group" role="group" aria-label="Basic example"
ng-init="selected = {tab:'raw'}">
<button class="btn"
ng-click="selectView('raw'); selected.tab = 'raw';"
ng-class="{'active':selected.tab === 'raw'}">Raw data
</button>
<button class="btn"
ng-click="selectView('summary'); selected.tab = 'summary';"
ng-class="{'active':selected.tab === 'summary'}">Summary
</button>
<button class="btn" ng-repeat="(key, value) in views"
ng-click="selectView(key); selected.tab = key;"
ng-class="{'active':selected.tab === key}">
{{ key }}
</button>
</div>
For more information, see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
Upvotes: 2
Reputation: 7194
Remember that ng-repeat
creates its own local scope so any variables you reference there that are not defined on the parent scope will be created locally. Although you can put multiple commands in ng-click
it is discouraged - it also seems that doing so will not cause a digest cycle for those items inside the ng-repeat
.
You can resolve all of this by making selectedTab
a property on your controller and having the selectView
method set the value of selectedTab
. Here's a quick example:
angular.module('app', [])
.controller('ctrl', ($scope) => {
$scope.selectedTab = 'raw';
$scope.views = {
Detail1: 'details',
Detail2: 'details',
Detail3: 'details'
};
$scope.selectView = function(view) {
$scope.selectedTab = view;
}
});
.active {
color: red !important;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div class="btn-group"
role="group"
aria-label="Basic example">
<button class="btn"
ng-click="selectView('raw')"
ng-class="{'active':selectedTab === 'raw'}">Raw data</button>
<button class="btn"
ng-click="selectView('summary')"
ng-class="{'active':selectedTab === 'summary'}">Summary</button>
<button class="btn"
ng-repeat="(key, value) in views"
ng-click="selectView(key)"
ng-class="{'active':selectedTab === key}">
{{ key }}
</button>
</div>
</div>
Upvotes: 5