Skip to content

Instantly share code, notes, and snippets.

@adamauckland
Created July 29, 2019 11:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adamauckland/868a1d0f227428a9f059d7eeac74b77a to your computer and use it in GitHub Desktop.
Save adamauckland/868a1d0f227428a9f059d7eeac74b77a to your computer and use it in GitHub Desktop.
/**
* The model returned from a web service
*/
class VehicleModel {
public make: string;
public model: string;
public colour: string;
}
/**
* A place to store models to make the views faster
*/
class VehicleCache {
protected static cache: Array<VehicleModel>;
public hasKey(id: string): boolean {
// return true if VehicleCache.cache has a vehicle with id
}
public set(vehicle: VehicleModel): void {
// put some logic here to stop duplicates
VehicleCache.cache.push(vehicle);
}
public get(id: string): VehicleModel {
return VehicleCache.cache.find(vehicle => vehicle.id === id);
}
}
/**
* The resource only handles web services, will be unit tested with mocks
*/
class VehicleResource {
public get(id: string): Observable<VehicleModel> {
return new Observable((subscriber: Subscriber<VehicleModel>) => {
this
.httpClient
.get(`/vehicle/${id}`)
.subscribe((vehicle: VehicleModel) => {
subscriber.next(vehicle);
subscriber.unsubscribe();
}, (error) => {
subscriber.error(error);
});
});
}
}
/**
* The repository handles pulling items out of the cache or web service
*/
class VehicleRepository {
constructor(
private vehicleCache: VehicleCache,
private vehicleResource: VehicleResource,
) {}
public get(id: string): Observable<VehicleModel> {
return new Observable<VehicleModel>((subscriber: Subscriber<VehicleModel>) => {
if (this.vehicleCache.hasKey(id)) {
subscriber.next(this.vehicleCache.get(id));
}
this
.vehicleResource
.get(id)
.subscribe((vehicleModel: VehicleModel) => {
this.vehicleCache.set(vehicleModel);
subscriber.next(vehicleModel);
subscriber.unsubscribe();
}, (_error) => {
subscriber.error(_error);
});
});
}
}
/**
* metadata for the view
*/
class ViewModelMeta {
public isNew: boolean;
}
/**
* ViewModels specific to each View, wrap the model so we can attach metadata for transitions without
* polluting the common model
*/
class NewVehicleViewModel {
public meta: ViewModelMeta;
public vehicleModel: VehicleModel;
constructor() {
this.meta = new ViewModelMeta();
}
}
/**
* The composition manager assembles everything the view will need.
*/
class NewVehicleViewCompositionManager {
constructor(
private vehicleRepository: VehicleRepository,
) {}
public load(id: string): NewVehicleViewModel {
let newVehicleViewModel = new NewVehicleViewModel();
newVehicleViewModel.meta.isNew = true;
newVehicleViewModel.vehicleModel = null;
this.vehicleRepository.get(id).subscribe((vehicleModel: VehicleModel) => {
newVehicleViewModel.vehicleModel = vehicleModel;
});
}
}
/**
* The controller picks a composition manager to hydrate the viewmodel
*/
class NewVehicleController implements OnInit, OnDestroy {
private loadDataSubscription: any;
protected viewModel: NewVehicleViewModel;
constructor(
protected newVehicleViewCompositionManager: NewVehicleViewCompositionManager,
protected pageRoute: ActivatedRoute,
)
public ngOnInit(): void {
const id = this.pageRoute.snapshot.params["id"];
this.loadDataSubscription = this.newVehicleViewCompositionManager.load(id);
}
public ngOnDestroy(): void {
this.loadDataSubscription.unsubscribe();
}
}
@adamauckland
Copy link
Author

example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment