Handling shared state among a lot of elements in Angular I am working on a project in Angular where I have a number of similar data objects. When you click on anyone of them it's state and amount of data shown will change. All of the objects start in the minimal state, when you click on one it becomes medium. From a medium you can click on the button to go to maximum or click anywhere else on it to go back down. There can only be one medium or maximum at a time. If there is one medium there can be no maximum, and vice versa. 




Since all of the objects are by default min, I don't track that. I use a string to track which element is in an expanded state. Then I compare that objects key and the string in an ng-if to change its what is being shown. The size is managed by a conditional class, and CSS. 

I chose ng-if, because there maybe a lot of elements loaded in the beginning, but there can only be one med/max at a time. I would rather load the med/max when the user wants it, instead of loading all of the med/max at that start and use ng-show for state view.

Is there a cleaner way to handle state change among many related objects? Does Angular have something to deal with this?


angular.module('SizeAndState', [])
.factory('Data', function() {
        return {
            query: function() {
                return {
                    a:{ name: 'A', date: '1/2/03', number: 1},
                    b:{ name: 'B', date: '4/5/06', number: 2},
                    c:{ name: 'C', date: '8/9/10', number: 3},
                    d:{ name: 'D', date: '3/2/01', number: 4},
                    e:{ name: 'E', date: '1/2/14', number: 5},
                }
            }
        };
    })
    .controller('SizeAndStateController', ['$scope', 'Data', function($scope, Data) {
        $scope.toMin = function (clickEvent){
            clickEvent.stopPropagation();
            $scope.medElement = '';
            $scope.maxElement = '';

        };
        $scope.toMed = function (elementName, clickEvent){
            if($scope.maxElement=='') {
               if($scope.medElement == elementName){
                   $scope.toMin(clickEvent);
               } else {
                   $scope.medElement = elementName;
               }
            }
        };
        $scope.toMax = function (clickEvent){
            clickEvent.stopPropagation();
            $scope.maxElement = $scope.medElement;
            $scope.medElement = '';

        };
        $scope.data = Data.query();
        $scope.medElement = '';
        $scope.maxElement = '';
    }]);
.campaign{
  display: inline-block;
  width: 100px;
  height: 100px;
  background-color: #ff685b;
  border: 1px solid #ffffff;
  color: #ffffff;
  vertical-align: top;
}
.campaign.med{
  width: 125px;
  height: 125px;
}
.campaign.max{
  width: 150px;
  height: 150px;
}
&lt;script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"&gt;&lt;/script&gt;
&lt;div ng-app="SizeAndState"&gt;
&lt;div ng-controller="SizeAndStateController"&gt;
    &lt;div class="campaign"
         ng-repeat = "element in data"
         ng-class="{'med':medElement==element.name, 'max':maxElement==element.name}"
         ng-click="toMed(element.name, $event)"&gt;
      
        &lt;span&gt;{{element.name}}&lt;/span&gt; &lt;span ng-if='medElement!=element.name &amp;&amp; maxElement!=element.name'&gt;Min&lt;/span&gt;
  &lt;span ng-if='medElement==element.name'&gt;
   &lt;span&gt;Med&lt;/span&gt;
   &lt;h5&gt;{{element.date}}&lt;/h5&gt;
            &lt;button class="btn"
                    ng-click="toMax($event)"&gt;
     to Max
            &lt;/button&gt;
        &lt;/span&gt;
       
       &lt;span ng-if='maxElement==element.name'&gt;
   &lt;span&gt;Max&lt;/span&gt; 
   &lt;h5&gt;{{element.date}}&lt;/h5&gt;
   &lt;h5 ng-if='maxElement==element.name'&gt;{{element.number}}&lt;/h5&gt;
           &lt;button
                ng-if="maxElement == element.name"
                ng-click="toMin($event)"&gt;
             to Min
         &lt;/button&gt;
        &lt;/span&gt;      
      
    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
Your View looks fine (HTML and CSS). However, representing the state of your data elements with two string variables that may or may not be storing the "name" field on the parent data object is a very bad way of representing state. 

The guiding principal for these design questions: your object model should make it impossible to ever represent an inconsistent or meaningless state. With your object model there are many ways to represent inconsistent and meaningless states, for example:


It's possible for a data item to be simultaneously in state Med
and state Max 
If medElement and maxElement variables contain
values which are not equal to the name field on the parent object,
those values are ignored


As a result your controller code is needlessly complex and very difficult to read.

Solution:

Represent the state of your data objects with an enum called ElementSize that can take exactly one of three possible state values: minimum, medium, and maximum. Then, use the ng-switch directive to specify what html you want to render for each of the three possible states.