Building  Large-Scale Web Applications with Angular
上QQ阅读APP看书,第一时间看更新

Adding WorkoutBuilderService

WorkoutBuilderService monitors the state of the workout that a user of the application is building. It:

  • Tracks the current workout
  • Creates a new workout
  • Loads the existing workout
  • Saves the workout

Copy workout-builder-service.ts from the workout-builder/builder-services folder under trainer/src/app in checkpoint 4.5.

The code 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.5 (folder—trainer). If you are not using Git, download the snapshot of Checkpoint 4.5 (a ZIP file) from the following GitHub location: https://github.com/chandermani/angular6byexample/archive/checkpoint4.5.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 to update the styles.css file, which we are not discussing here.

While we normally make services available application-wide,  WorkoutBuilderService will only be used in the Workout Builder feature. Therefore, instead of registering it with the providers in AppModule, we have registered it in the provider array of WorkoutBuilderModule as follows (after adding it as an import at the top of the file):

@NgModule({
....
providers: [WorkoutBuilderService]
})

Adding it as a provider here means that it will only be loaded when the Workout Builder feature is being accessed and it cannot be reached outside this module. This means that it can be evolved independently of other modules in the application and can be modified without affecting other parts of the application.

Let's look at some of the relevant parts of the service.

WorkoutBuilderService needs the type definitions for WorkoutPlan, ExercisePlan, and WorkoutService, so we import these into the component:

import { WorkoutPlan, ExercisePlan } from '../../core/model';
import { WorkoutService } from '../../core/workout.service';

WorkoutBuilderService has a dependency on WorkoutService to provide persistence and querying capabilities. We resolve this dependency by injecting WorkoutService into the constructor for WorkoutBuilderService:

 constructor(public workoutService: WorkoutService) {}

WorkoutBuilderService also needs to track the workout being built. We use the buildingWorkout property for this. The tracking starts when we call the startBuilding method on the service:

startBuilding(name: string){ 
    if(name){ 
        this.buildingWorkout = this.workoutService.getWorkout(name) 
        this.newWorkout = false; 
    }else{ 
        this.buildingWorkout = new WorkoutPlan("", "", 30, []); 
        this.newWorkout = true; 
    } 
    return this.buildingWorkout; 
} 

The basic idea behind this tracking function is to set up a WorkoutPlan object (buildingWorkout) that will be made available to components to manipulate the workout details. The startBuilding method takes the workout name as a parameter. If the name is not provided, it implies we are creating a new workout, and hence a new WorkoutPlan object is created and assigned; if not, we load the workout details by calling WorkoutService.getWorkout(name). In any case, the buildingWorkout object has the workout being worked on.

The newWorkout object signifies whether the workout is new or an existing one. It is used to differentiate between save and update situations when the save method on this service is called.

The rest of the methods, removeExercise, addExercise, and moveExerciseTo, are self-explanatory and affect the exercise list that is part of the workout (buildingWorkout).

WorkoutBuilderService is calling a new method, getWorkout, on WorkoutService, which we have not added yet. Go ahead and copy the getWorkout implementation from the workout-service.ts file in the services folder under trainer/src in checkpoint 4.5. We will not dwell on the new service code as the implementation is quite simple.

Let's get back to left nav and implement the remaining functionality.