A repository sits between a data source and the application components that need its data. The data source obtains product records; the repository distributes them and provides operations on them, so components need not know how the records were obtained. An Angular data model can pair a descriptive Product class with a data source and a repository. section 5.3.3 section 9.6.1
Start with the operations callers need. A product repository can return every product, find one by ID, save a new or existing product, and delete one. Its first data source can simply hold an in-memory array; the repository gives application code a place to access and manipulate that data. section 9.6.1
Table 9.8. The methods defined in the repository
|
Name
|
Description
|
|
getProducts
|
This method returns an array containing all the
Product objects in the model.
|
|
getProduct
|
This method returns a single
Product object based on its ID.
|
|
saveProduct
|
This method updates an existing
Product object or adds a new one to the model.
|
|
deleteProduct
|
This method removes a
Product object from the model based on its ID.
|
The method table gives a compact starting interface for product lookup and changes. section 9.6.1
Signals offer another way to expose repository data. A product repository can publish the data source’s products signal and derive a categories signal with computed. The category calculation maps products to category names, substitutes “(None)” for missing categories, removes duplicates, and sorts the result. It is recomputed when the underlying products signal changes. section 5.3.3
Listing 5.13. The contents of the product.repository.ts file in the src/app/model folder
import { Injectable, Signal, computed } from "@angular/core";
import { Product } from "./product.model";
import { StaticDataSource } from "./static.datasource";
@Injectable()
export class ProductRepository {
products: Signal<Product[]>;
categories: Signal<string[]>;
constructor(private dataSource: StaticDataSource) {
this.products = dataSource.products;
this.categories = computed(() => {
return this.dataSource.products()
.map(p => p.category ?? "(None)")
.filter((c, index, array) =>
array.indexOf(c) == index).sort();
})
}
getProduct(id: number): Product | undefined {
return this.dataSource.products().find(p => p.id == id);
}
}
The complete ProductRepository shows constructor injection, lookup by ID, and a computed categories signal in one file. section 5.3.3
If callers should read products but not change them directly, keep a writable signal private and expose its read-only form. Route changes through repository methods instead. Components can then derive their own computed values from the exposed signal; removing a product updates values affected by the changed data. section 10.4.4 section 20.2.2
The data source can change without moving data-loading details into components. For an asynchronous REST source, inject RestDataSource, subscribe to its getData result, and set the repository’s products signal when records arrive. This replaces the earlier constructor step that set the signal from a static source. section 20.2.2 section 23.3.3
Listing 23.7. Using the new data source in the repository.model.ts file in the src/app/model folder
import { Injectable, Signal, computed, signal } from "@angular/core";
import { Product } from "./product.model";
//import { StaticDataSource } from "./static.datasource";
import { RestDataSource } from "./rest.datasource";
@Injectable()
export class Model {
private products = signal<Product[]>([]);
private locator = (p: Product, id?: number) => p.id == id;
constructor(private dataSource: RestDataSource) {
//this.products.set(this.dataSource.getData());
this.dataSource.getData()
.subscribe(data => this.products.set(data));
}
// ...methods omitted for brevity...
}
The focused edit shows the repository switching to a REST data source and updating its products signal when the request returns. section 23.3.3








