Monday, August 12, 2013

Quick AngularJS Tip: Relating User Input Data to Metadata

I started learning AngularJS a few weeks back because I thought it would work well for an internal tool I was building (more on that in a later post).

Many AngularJS examples, including those in the current AngularJS tutorial (like this one), illustrate how to use the ng-repeat and ng-options directives using a typical Javascript array of objects.  So given such an array in the model:

$scope.plans= [
  {"name": "Basic", "cost": "$250.00"},
  {"name": "Deluxe","cost": "$325.00"},
  {"name": "Premium","cost": "$335.00"}    	
];

...you can output a list of the data like so:

<ul ng-repeat="plan in plans">
  <li>
    {{plan.name}}: {{plan.cost}}
  </li>
</ul>

But, as noted in the AngularJS API, you can also use ng-repeat and ng-options to loop through a set of properties/keys in an object, and that works even if each property references another object.  So you can accomplish the same thing with this model object:

$scope.plans= {
  "Basic": {"cost": "$250.00"},
  "Deluxe": {"cost":"$325.00"},
  "Premium": {"cost": "$335.00"}    	
};

...and this HTML:

<ul ng-repeat="(planKey,planObject) in plans">
  <li>
    {{planKey}}: {{planObject.cost}}
  </li>
</ul>

The advantage to using the latter technique comes into play when you want to relate something in the part of the model you want to preserve / process with a part of the model that provides metadata.

Taking the above examples a step further, say you wanted to create a short conference registration form. The user has to select a conference package, and you want to provide information about each package (what it entails and the cost) but you don't want that information to be part of the form submission. You can do this by having the user's conference selection pull the related data from the plan metadata.

So with the following AngularJS controller function:

function ConfReg($scope, $http) {
  $scope.reg= {};
  $scope.reg.plan= "Basic";
  
  $scope.plans= {
    "Basic": {
      "description": "Access to all first-run conference sessions on Thursday and Friday.",
      "cost": "$250.00"
    },
    "Deluxe": {
      "description": "Access to all first-run conference sessions on Thursday and Friday, access to repeat sessions on Saturday.  Complimentary breakfast included all three days.",
      "cost": "$325.00"
    },
    "Premium": {
      "description": "Access to all first-run conference sessions on Thursday and Friday, access to repeat sessions on Saturday.  Complimentary breakfast included all three days.  Also includes conference t-shirt and kazoo.",
      "cost": "$335.00"
    }

  };
}

...and the corresponding HTML:

<h3>Conference Registration</h3>
  <form name="regForm" id="regForm" ng-controller="ConfReg">
    <p>
      <label>First Name:</label>
      <input type="text" name="firstName" id="firstName" ng-model="reg.firstName" />
    </p>
			
    <p>
      <label class="control-label" for="lastName" id="lblLastName">Last Name:</label>   
      <input type="text" name="lastName" id="lastName" ng-model="reg.lastName" />
    </p>
				
    <p>
      <label class="control-label" for="plan" id="lblPlan">Conference plan:</label>   
      <select name="plan" id="plan" ng-model="reg.plan" ng-options="planName as planName for (planName,planObject) in plans"></select>
    </p>
    <div>
      <p>{{plans[reg.plan].description}}</p>
      <strong>Price:</strong> {{plans[reg.plan].cost}}
    </div>
				
</form>

...The user's selection of plan determines which plan object data is pulled from the "plans" part of the model for display in that div block, yet the data sent as the form submission (the "reg" object) only includes the plan name.

No comments:

Post a Comment