bazzinga
bazzinga

Reputation: 175

AngularJS: How to make local variables work which are not part of ng-repeat

I have an application where I am receiving a list of person objects in persons and iterating over to build a table dynamically.

The code looks like this:

<table>
  <tr ng-repeat = "personInfo in persons">
    <td>{{personInfo.name}}</td>
    <td>{{personInfo.gender}}</td>
    <td>
      <select ng-model="selected_address" ng-options="address.placename for address in personInfo.address"></select>
      <span ng-show="make_address_editable==false;">{{selected_address.address}}</span>
      <input type="text" style="width: 22em;" ng-show="make_address_editable" ng-model="selected_address.address" />
      <input style="margin-left: 2em;" type="button" value="{{btnValue}}" ng-show="selected_address" ng-click="make_address_editable=!make_address_editable; changeBtnValue($index)"/>
    </td>
  </tr>
</table>

The part where I am stuck is how to change the value of input[type="button"] or {{btnValue}} individually for each person which edits/saves a person's address. I don't want to assign it as a property on the personInfo object as I would not need it later. In my controller I initialize the value of make_address_editable and btValue but I'm only able to control the behavior of make_address_editable on ng-click for each person. If I call a function on ng-click which changes btnValue depending on what the user clicked it changes the btnValue for each row/person.

Controller code:

$scope.make_address_editable = false;
$scope.btnValue = 'Edit';
$scope.changeBtnValue = function(index){
  if ($scope.btnValue === 'Edit'){
    $scope.btnValue = 'Save';
  }
  else{
    $scope.btnValue = 'Edit';
  }
}

I don't know how to use index in the method to change btnValue for each person in the table.

Any help would be much appreciated.

Thanks!

Upvotes: 1

Views: 13382

Answers (2)

Patrick
Patrick

Reputation: 6948

There should be no problem including a btnValue within the personInfo object as this is a ViewModel and you are really driving the view (value of button) based on what is in that model. You may not be using it later in terms of using that value when persisting the person to a datastore, but your view is definitely using it constantly as it is keeping the two-way binding in sync.

You should really be putting make_address_editable inside your personInfo model as well since I don't think you want to have a "global" flag for each individual person. Having make_address_editable directly on $scope will prevent you from tracking it on a person by person basis.

If you really must make a local variable, you can try this. See jsfiddle I made starting with your example:

angular.module('coolApp', [])
    .controller('PersonCtrl', function ($scope) {
        $scope.persons = [{'name': 'Alex', 'gender': 'Male', 'address': [{'placename': 'Home', 'address': '1234 Dr'},{'placename': 'Office', 'address': '12345 Dr'}]}, {'name': 'Alexis', 'gender': 'Female', 'address': [{'placename': 'Home', 'address': '12345 Dr'},{'placename': 'Office', 'address': '123456 Dr'}]}];

    //$scope.make_address_editable = false;
    //$scope.btnValue = 'Edit';
    $scope.changeBtnValue = function (viewModel) {
        if (viewModel.editable === true) {
            viewModel.btnValue = 'Save';
        } else {
            viewModel.btnValue = 'Edit';
        }
    };
});

Then use ng-init within your repeater to make a viewModel object that is local to the repeater scope. This will keep your persons from changing:

<div ng-app="coolApp">
    <div ng-controller="PersonCtrl">
        <table>
            <tr ng-repeat="personInfo in persons" ng-init="viewModel={btnValue: 'Edit', editable: false}">
                <td>{{personInfo.name}}</td>
                <td>{{personInfo.gender}}</td>
                <td>
                    <select ng-model="selected_address" ng-options="address.placename for address in personInfo.address"></select> <span ng-show="viewModel.editable===false;">{{selected_address.address}}</span>

                    <input type="text" style="width: 22em;" ng-show="viewModel.editable" ng-model="selected_address.address" />
                    <input style="margin-left: 2em;" type="button" value="{{viewModel.btnValue}}" ng-show="selected_address" ng-click="viewModel.editable=!viewModel.editable; changeBtnValue(viewModel);" />
                </td>
            </tr>
        </table>
    <br><br>
    {{persons}}
    </div>
</div>

Upvotes: 1

Kumar
Kumar

Reputation: 1349

btnValue is supposed to change for all "persons". But I think you'd want it to change for specific "person" objects only.

There is an ugly way to do this. Pass the event to the function.

ng-click="make_address_editable=!make_address_editable; changeBtnValue($event)"

Inside the changeBtnValue function, change the "value" attribute of the corresponding input tag.

$scope.changeBtnValue = function(event){
          var inputTag = event.target;
}

"inputTag" variable will hold HTML Element object representing the corresponding input tag. Change the "value" attribute of the tag to "save"/"edit" or anything else. THis method is ugly because you manipulate the View from within the JS code. People usually try to avoid this.

Upvotes: 0

Related Questions