1

I am updating a form to have two sets of radio buttons that turns on or off panels. The first says "Yes" to contact the customer or "No". If we are to contact, then present choice of which method is used, phone or email. (Previously there was just the question to contact or not.)

While I can get the show function to work properly, I do not understand why I cannot get the radio button to show as selected. (It could be that I am fairly green to AngularJS.)

  <div class="radio styled-radio" id="callTheCustomerDiv">
    <input class="form-control" id="callTheCustomerRadio1"
           name="callTheCustomer" type="radio" value="Yes"
           ng-required="issueType=='IncidentDiv'"
           ng-model="$parent.callTheCustomer"
           ng-click="contactSelection('none')"/>
    <label for="callTheCustomerRadio1"> Yes</label> - Please indicate how the customer wants to be contacted.<br/>
    <div class="form-group" id="howToContactDiv" ng-show=state class="row fullWidth">
      <div id="contactOptions" class="form-group">
        <label for="phoneRadio">Phone</label>
        <!-- radio indicator is not working correctly -->
        <input id="phoneRadio" class="form-control" type="radio"
               ng-click="contactSelection('phone')"
               name="phoneSelect" ng-model="$parent.phoneSelect"
               value="{{$parent.phoneSelect}}" />
         &nbsp; &nbsp; &nbsp; &nbsp;
        <label for="emailRadio">Email</label>
        <input id="emailRadio" class="form-control" type="radio"
               ng-click="contactSelection('email')" name="emailSelect"
               ng-model="$parent.emailSelect"
               value="{{$parent.emailSelect}}" />
        <br/>    <!--     phoneSelect : {{phoneSelect}} -->
      </div>
      <div id="phoneContact" ng-show="contactType=='phone'">
        <span>Phone
          <input class="form-control input-sm" type="text"
                 name="callTheCustomerPhone"
                 id="callTheCustomerPhone"
                 ng-model="$parent.callTheCustomerPhone"
                 ng-required="$parent.contactType=='phone' && accForm.phoneNumber.$viewValue === ''"
                 maxlength="20"/>
        </span>
      </div>
      <br/>
      <div id="emailContact" ng-show="contactType=='email'">
        <span>Email
        <input class="form-control input-sm" type="text"
               name="emailTheCustomer" id="emailTheCustomer"
               ng-model="$parent.emailTheCustomer"
               ng-required="$parent.contactType=='email' && accForm.emailTheCustomer.$viewValue === ''"
               maxlength="150"/></span>
      </div>
    </div>
    <input class="form-control" id="callTheCustomerRadio2"
           name="callTheCustomer" type="radio" value="No"
           ng-required="issueType=='IncidentDiv'"
           ng-model="$parent.callTheCustomer"
           ng-click="contactSelection('no')"/>
    <label for="callTheCustomerRadio2">No</label>

  </div>

Looks like this when Phone is selected:

enter image description here

and like this when email is selected:

enter image description here

I added this method to the Main controller:

$scope.contactSelection = (function (contact) {
  if (contact=='none'
    ||contact=='email'
    ||contact=='phone'){
    $scope.state = true;
    $scope.contactType = contact;
    if (contact=='phone'){$scope.phoneSelect = true;$scope.emailSelect = false;}
    if (contact=='email'){$scope.emailSelect = true;$scope.phoneSelect = false;}

  }else{
    $scope.state = false;
    $scope.contactType = 'no';
    $scope.emailTheCustomer = '';
    $scope.callTheCustomerPhone = '';
    $scope.phoneSelect = false;
    $scope.emailSelect = false;
  }
});
georgeawg
  • 48,608
  • 13
  • 72
  • 95
iowatiger08
  • 1,892
  • 24
  • 30
  • Using `$parent` is problematic. If the template nests more than one directive which instantiates a child scope, the two-way binding will have a data hiding problem. It is better to bind `ng-models` to a property of an object in the controller scope. – georgeawg May 24 '19 at 17:43
  • If `$parent` binds the ng-model to `$rootScope`, the controller $scope will not be able to set the model value. – georgeawg May 24 '19 at 17:53
  • Yes/No questions are better handled with [checkboxes](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D#examples). – georgeawg May 24 '19 at 18:04
  • The model value is getting set, just the display does not change. If I switch the name to be something like phoneOrRadio as @nash11 suggests, the radio toggles on for email even though clicked on phone. I looked at other radio button settings and they use the similar scope notation. – iowatiger08 May 24 '19 at 19:19
  • The model is getting set because the `ng-click` handler is setting it manually. If the binding is correct, the `ng-model` controller should set it automatically and there would be no need to set it manually. The `ngModelController` needs the proper binding to render to the HTML. Avoid using `$parent`. Define objects in the $scope for your model, then reference a property of that object. See [Nuances of scope inheritance in AngularJS](https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs) – georgeawg May 24 '19 at 19:47

3 Answers3

2

Radio buttons normally come in groups and the name attribute is used to group them.

There are two radio groups in your code, the Yes/No and Phone/Email.

The Yes/No radio buttons have the same name callTheCustomer while the Phone/Email radio buttons have different names. Change the name attributes for the phone and email radio buttons to something more meaningful to that group like phoneOrEmail.

nash11
  • 8,220
  • 3
  • 19
  • 55
1

Avoid using $parent

Using $parent is problematic. If the template nests more than one directive which instantiates a child scope, the two-way binding will have a data hiding problem. It is better to bind ng-models to a property of an object in the controller scope.

If $parent binds the ng-model to $rootScope, the controller $scope will not be able to set the model value.

The model value is getting set, just the display does not change.

The model is getting set because the ng-click handler is setting it manually. If the binding is correct, the ng-model controller should set it automatically and there would be no need to set it manually. The ngModelController needs the proper binding to render to the HTML.

Avoid using $parent. Define objects in the $scope for your controller, then reference a property of that object. See Nuances of scope inheritance in AngularJS.

The below example binds correctly even though the ng-if directive adds a child scope:

angular.module("app",[])
.controller("ctrl",function($scope) {
    $scope.fd1={
         sel1: null,
         yes1: false
    };
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
    <form name="form1">
         <input ng-model="fd1.yes1" type="checkbox" />
         OK to Contact?<br>
         <div ng-if="fd1.yes1">
           <input ng-model="fd1.sel1" type="radio" name="sel1" value="email" />
           email
           <input ng-model="fd1.sel1" type="radio" name="sel1" value="phone" />
           phone
           <br>{{fd1.sel1}}
           <div ng-show="fd1.sel1">
             <input ng-model="fd1.info1">
             <br>
             <button ng-click="fd1.sel1=null">Reset choice</button>
           </div>
         </div>  
     </form>
</body>
Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • So I am selecting your answer because it lead to the solution and now I understand more about the $rootScope and $scope and $parent. I will post an update as solution with what I did. – iowatiger08 May 28 '19 at 21:18
0

Couple things were in play here

  1. I had to change the label-input tag order.

     <div class="radio styled-radio" id="callTheCustomerDiv">
        <input class="form-control" id="callTheCustomerRadio1" name="callTheCustomer" type="radio" value="Yes"
               ng-required="issueType=='IncidentDiv'"
               ng-model="callTheCustomer"
               ng-click="contactSelection(null)" />
        <label for="callTheCustomerRadio1"> Yes</label> - Please indicate how the customer wants to be contacted.<br/>
        <div class="form-group" id="howToContactDiv"
        ng-if="customerContact.contactOptionShow" class="row fullWidth">
          <div id="contactOptionsDiv" >
            <div id="contactOptionsSelectDiv" >
                <input  class="form-control" id="phoneRadio" type="radio" name="contactSelection"
                ng-model="customerContact.contactSelection" value="phone" />
                <label for="phoneRadio">Phone</label>
              &nbsp; &nbsp; &nbsp; &nbsp;
              <input  class="form-control" id="emailRadio" type="radio" name="contactSelection"
              ng-model="customerContact.contactSelection" value="email" />
              <label for="emailRadio">Email</label>
              <br/>
            </div>
            <div id="phoneContactDiv" ng-show="customerContact.contactSelection=='phone'">
              <span>Phone
              <input class="form-control input-sm" type="text" name="callTheCustomerPhone" id="callTheCustomerPhone"
                    ng-model="callTheCustomerPhone"
                    ng-required="customerContact.contactSelection=='phone' && accForm.phoneNumber.$viewValue === ''"
                    maxlength="20"/></span>
            </div>
            <br/>
            <div id="emailContactDiv" ng-show="customerContact.contactSelection=='email'">
              <span>Email
              <input class="form-control input-sm" type="text" name="emailTheCustomer" id="emailTheCustomer"
                    ng-model="emailTheCustomer"
                    ng-required="customerContact.contactSelection=='email' && accForm.emailTheCustomer.$viewValue === ''" maxlength="150"/></span>
            </div>
          </div>
        </div>
        <input class="form-control" id="callTheCustomerRadio2" name="callTheCustomer" type="radio" value="No"
               ng-required="issueType=='IncidentDiv'" ng-model="callTheCustomer"
               ng-click="contactSelection('clear')" />
        <label for="callTheCustomerRadio2">No</label>
      </div>
    
  2. I completely removed the $parent references and simplified the function by using the properties of the variable.

modified the mainController method and variable to look like:

  $scope.customerContact ={
    contactOptionShow : false,
    contactSelection : null
  };
    $scope.contactSelection = (function (contact) {
      if (contact != null
          && contact=='clear'      ){
          $scope.customerContact.contactOptionShow = false;
          $scope.customerContact.contactSelection = '';
          $scope.emailTheCustomer = '';
          $scope.callTheCustomerPhone = '';
      }else{
        $scope.customerContact.contactOptionShow = true;
      }
    });

I think it reads better now as well.

iowatiger08
  • 1,892
  • 24
  • 30
  • 1
    Misko, one of the creaters of the AngularJS framework recommends following the "best practice" of [always have a '.' in your ng-models.](http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m) – georgeawg May 28 '19 at 21:47