diplosaurus
diplosaurus

Reputation: 2588

Active button states in Angular + Bootstrap

Seems like a simple problem but I'm actually having trouble with it.

Plunk here

Basically I have a ng-repeat of buttons and then a block of text after that clicking the button will show. However, when I click a button I want to hide the blocks of text from all the other buttons and remove the active states from the other buttons. Basically only 1 button block of text should be shown at a time.

Seems easy, but the way ng-hide handles scope (scope: true) means I can't really look into the other scopes and turn each of them off. The other thing is that I don't want to alter the actual array from ng-repeat if at all possible. This is data from an API that I have to send back and I'm attempting to not alter the actual data structure if I can.

<div class="row" ng-repeat="button in buttons">
    <div class="col-sm-2">
        <button ng-click="showInfo = !showInfo" class="btn btn-primary">{{button.name}}</button>
    </div>
    <div ng-show="showInfo" class="col-sm-3">
        <div class="alert alert-success">{{button.extraInfo}}</div>
    </div>
</div>

And JS

app.controller('MainCtrl', function($scope) {
  $scope.buttons = [
    { name: 'One', extraInfo: 'Extra info for button 1' },
    { name: 'Two', extraInfo: 'Extra info for button 2' },
    { name: 'Three', extraInfo: 'Extra info for button 3' }
  ];
});

Upvotes: 1

Views: 9075

Answers (3)

Saad
Saad

Reputation: 952

If you don't want to change the actual array, then maintain another object or array which will hold the key to each button's show/hide state.

  $scope.showInfo = {};
  $scope.buttons = [
    { name: 'One', extraInfo: 'Extra info for button 1' },
    { name: 'Two', extraInfo: 'Extra info for button 2' },
    { name: 'Three', extraInfo: 'Extra info for button 3' }
  ];

  $scope.changeShowInfo = function(index) {
    for(var prop in $scope.showInfo) {
      $scope.showInfo[prop] = false;
    }
    $scope.showInfo[index] = true;
  };

Solved Plunker

Upvotes: 3

Or Guz
Or Guz

Reputation: 1018

You want 1 button active each time, so you better use radio buttons with a currentItem kept in the scope by using ng-bind.

HTML:

<body ng-controller="MainCtrl">
    <div name="myForm">
        <div ng-repeat="button in buttons">
            <label>
                <input type="radio" ng-model="$parent.selectedItem" ng-value="button"> {{button.name}}
            </label>
        </div>
    </div>
    <div class="alert alert-success">Extra info: {{selectedItem.extraInfo}}</div>
</body>

Didn't need to change your JS.

See Plunker here

Upvotes: 0

Uluk Biy
Uluk Biy

Reputation: 49195

I suggest to create new array which has the same length as buttons array, and this array will hold boolean values to indicate where the item active or not.

I didn't log in to plunk so here the modified version of yours.

index.html

  <body ng-controller="MainCtrl as vm">
    <div class="row" ng-repeat="button in buttons track by $index">
      <div class="col-sm-2">
        <button ng-click="vm.setActive($index)" ng-class="vm.isActive[$index] ? 'btn btn-primary' : 'btn'">{{button.name}}</button>
      </div>
      <div ng-show="vm.isActive[$index]" class="col-sm-3">
        <div class="alert alert-success">{{button.extraInfo}}</div>
      </div>
    </div>
  </body>

app.js

app.controller('MainCtrl', function($scope) {
  $scope.buttons = [
    { name: 'One', extraInfo: 'Extra info for button 1' },
    { name: 'Two', extraInfo: 'Extra info for button 2' },
    { name: 'Three', extraInfo: 'Extra info for button 3' }
  ];
  var vm = this;
  vm.isActive =[];
  for(var i=0, len=$scope.buttons.length; i < len; i++){
      vm.isActive[i] = false;
    }

  vm.setActive = function(ind) {
    for(var i=0, len=vm.isActive.length; i < len; i++){
      vm.isActive[i] = i===ind;
    }
  }

});

Upvotes: 4

Related Questions