Search code examples
angularjsng-showcontrol-state

control state of ng-show in a loop of a particular element


I'm dynamically generating a list of actions within my form. Example, save, approve, reject. When you click one of those actions, I'd like a spinner to appear within that button until I get a successful response from the server.

I have the following code.

buttons

<button type="submit" value="{{e.label}}" ng-click="getCtrlScope().formData.requestAction=e.action;" class="btn {{e.btnStyle}}"  ng-repeat="e in buttonActions">
    <span ng-show="saveState == 'save'"> {{e.label}} </span> 
    <span ng-show="saveState == 'loading'"> {{e.label}} 
        <i class="fa fa-spinner fa-spin"></i>
    </span> 
    <span ng-show="saveState == 'completed'"> {{e.label}} 
        <i class="fa fa-check"></i>
    </span>
</button>

js

$scope.saveState = 'save' ;

$scope.save = function() {      
    $scope.saveState = 'loading'

    $http.post('../reviewRequest.json', $scope.formData).then(
        function(response) {    
            $scope.saveState = 'save'
        }, function(response) {
    });
};  

As you can probable see, when you click an action all my buttons will start spinning. How do I limit the spin to a single button?


Solution

  • This is not a good practic, as mentioned by @fantarama, you should use a property inside the element, not the $scope.

    Anyway, if you don't mind to, you could store the element you clicked on with your ng-click call

    ng-click="getCtrlScope(e)..."
    

    Store that element in some variable:

    function getCtrlScope(element) {
        $scope.clickedElement = element;
        ...
    }
    

    Then, in your HTML:

    <span ng-show="saveState == 'save' && e !== clickedElement"> {{e.label}} </span> 
    <span ng-show="saveState == 'loading' && e === clickedElement"> {{e.label}} 
        <i class="fa fa-spinner fa-spin"></i>
    </span>
    <span ng-show="saveState == 'completed' && e === clickedElement"> {{e.label}}
        <i class="fa fa-check"></i>
    </span>
    

    EDIT

    Assuming your POST call is located in the getCtrlScope, you could do something like:

    • As mentionned before, add the element in the function call that you'll pass to the save function
    • Store the state in the element itself (don't forget to initialize it)
    • Update the ng-show conditions

    HTML:

    <button type="submit" value="{{e.label}}" ng-click="getCtrlScope(e).formData.requestAction=e.action;" class="btn {{e.btnStyle}}" ng-repeat="e in buttonActions">
        <span ng-show="e.saveState == 'save'"> {{e.label}} </span> 
        <span ng-show="e.saveState == 'loading'"> {{e.label}} 
            <i class="fa fa-spinner fa-spin"></i>
        </span>
        <span ng-show="e.saveState == 'completed'"> {{e.label}}
            <i class="fa fa-check"></i>
        </span>
    </button>
    

    JS:

    $scope.save = function(element) {      
        element.saveState = 'loading'
    
        $http.post('../reviewRequest.json', $scope.formData).then(
            function(response) {    
                element.saveState = 'save'
            }, function(response) {
        });
    };
    

    Based on your Plunker, I modified it a little. As you can see, the state of each button changes in an independant way as you click on them.

    See the result here.