This project was generated with Angular CLI version 8.3.0.
The demo uses a super simple and easy to use RESTSful API provided by json-server.
npm run server
will spool up the API on localhost:3000
.
The data used by the API is stured in src/assets/db.json
. This file is modified in place by API operations, so expect to see it change as the data is manipulated.
Run npm start
for a dev server. Navigate to http://localhost:4200/
. The app will automatically reload if you change any of the source files.
You can use the CLI...
Run
ng generate component component-name
to generate a new component. You can also useng generate directive|pipe|service|class|guard|interface|enum|module
.
Or, use the Angular Console VSCode extension, which is way easier.
-
Locate and examine the common parts of the two included services
StudentService
andInstructorService
. -
Build an abstract service,
BaseCrudService
, that provides most of these common elements@Injectable() export abstract class BaseCrudService { abstract endpoint: string // must be implemented by implementor constructor(private http: HttpClient) {} fetchAll(): Observable<any[]> { // TO BE IMPLEMENTED } create(item: Object): Observable<any> { // TO BE IMPLEMENTED } ... }
-
Utilize generics to support typing on the abstract service in order to preserve strong typing by implementers
@Injectable() export abstract class BaseCrudService<T> { abstract endpoint: string // must be implemented by implementor constructor(protected http: HttpClient) {} fetchAll(): Observable<T[]> { // TO BE IMPLEMENTED } create(item: Object): Observable<T> { // TO BE IMPLEMENTED } ... }
-
Replace the existing services with new ones that utilize the abstract service to drastically reduce the code needed to implement a service
@Injectable() export class StudentService extends BaseCrudService<Student> { endpoint = 'students' constructor(protected http: HttpClient) { super(http) } }
BONUS: Refactor the models using a BaseItem
interface so it's possible to rely on the id
field inside the abstract service, so getById
can be moved into BaseCrudService
@Injectable()
export abstract class BaseCrudService<T extends BaseItem> {
...
}
BONUS: Build an abstract mock service that can be used to easily make mock services that can be used safely in test suites without needing to import the HttpClientTestingModule
@Injectable()
export abstract class BaseMockCrudService<T extends BaseItem> {
abstract mockData: T[]
constructor() {}
fetchAll(): Observable<T[]> {
return of(this.mockData)
}
create(item: T): Observable<T> {
// CODE TO ADD item T this.mockData
return of(item)
}
...
}
@Injectable()
export class MockStudentService extends BaseMockCrudService<Student> {}
-
Locate and examine the two list components,
StudentListComponent
andInstructorListComponent
-
Extract the common elements of the list components into an abstract
BaseListComponent
-
Be sure to utilize generics to support data typing for the table data source
@Component({ ... }) export abstract class BaseListComponent<T> implements AfterViewInit { @Output() edit = new EventEmitter<T>() abstract tableColumns: string[] dataSource: MatTableDataSource<T> @ViewChild(MatSort, { static: false }) sort: MatSort constructor() { this.dataSource = new MatTableDataSource<T>() } abstract ngAfterViewInit() abstract onDelete(item: T) }
-
Use the new abstract component to update the existing List Components to reduce the duplicate code
@Component({ ... }) export class StudentListComponent extends BaseListComponent<Student> { tableColumns = ['name', 'majors', 'actions'] constructor(private studentService: StudentService) { this.studentService.fetchAll() .subscribe(data => this.dataSource.data = date) } ngAfterViewInit() { ... }) }
- Now that you are equipped with quick ways to add a new service and list component, try building out a new form/list data management feature for the following:
- Classes:
{ name, instructor, classroom, schedule (M-F), time }
- Classes: