
Using ngModel with select
Let's look at how select has been set up:
<select . . . name="duration" [(ngModel)]="exercisePlan.duration"> <option *ngFor="let duration of durations" [value]="duration.value">{{duration.title}}</option> </select>
We are using ngFor here to bind to an array, durations, which is in the Workout component class. The array looks like this:
[{ title: "15 seconds", value: 15 }, { title: "30 seconds", value: 30 }, ...]
The ngFor component will loop over the array and populate the drop-down values with the corresponding values in the array with the title for each item being displayed using interpolation, {{duration.title}}. And [(ngModel)] then binds the drop-down selection to the exercisePlan.duration in the model.
Notice here that we are binding to the nested model: ExercisePlan. And, we may have multiple exercises to which we will be applying this binding. With that being the case, we have to make use of another Angular form directive—ngModelGroup—to handle these bindings. ngModelGroup will allow us to create a nested group within our model that will contain the list of exercises included in the workout and then in turn loop over each exercise to bind its duration to the model.
To start with, we will add ngModelGroup to the div tag that we have created within the form to hold our list of exercises:
<div id="exercises-list" class="col-sm-2 exercise-list" ngModelGroup="exercises">
That takes care of creating the nested list of exercises. Now, we have to handle the individual exercises within that list, and we can do that by adding another ngModelGroup to the individual divs that contain each exercise:
<div class="exercise tile" [ngModelGroup]="i">
Here, we are using the index in our for loop to dynamically create an individual model group for each of our exercises. These model groups will be nested inside the first model group that we created. Temporarily, add the tag <pre>{{ f.value | json }}</pre> to the bottom of the form and you will be able to see the structure of this nested model:
{ "exercises": { "0": { "duration": 15 }, "1": { "duration": 60 }, "2": { "duration": 45 }, "exerciseCount": 3 }, "workoutName": "1minworkout", "title": "1 Minute Workout", "description": "desc", "restBetweenExercise": 30 }
This is powerful stuff that enables us to create complicated forms with nested models, all of which can use ngModel for databinding.
You may have noticed a subtle difference in the two ngModelGroup directive tags we just introduced. The second of the two is wrapped in angle brackets, [], while the first is not. This is because with the first tag we are just naming our model group, whereas with the second we are binding it dynamically to each exercise's div tag using the index of our for loop.
Like input, select too supports two-way binding. We saw how changing select updates a model, but the model-to-template binding may not be apparent. To verify that a model to a template binding works, open the 7 Minute Workout app and verify the duration dropdowns. Each one has a value that is consistent with the model value (30 seconds).
Angular does an awesome job of keeping the model and view in sync using ngModel. Change the model and see the view updated; change the view and watch as the model is updated instantaneously.
Now, let's add validation to our form.
The code for the next section is also available for everyone to download on GitHub at https://github.com/chandermani/angular6byexample. Checkpoints are implemented as branches in GitHub. The branch to download is as follows: GitHub Branch: checkpoint4.6 (folder—trainer). Or if you are not using Git, download the snapshot of Checkpoint 4.6 (a ZIP file) from the following GitHub location: https://github.com/chandermani/angular6byexample/archive/checkpoint4.6.zip. Refer to the README.md file in the trainer folder when setting up the snapshot for the first time. Again, if you are working along with us as we build the application, be sure and update the styles.css file, which we are not discussing here.