Skyframe Documentation
Use cases
Use cases
This guide explain how to use Skyframe use cases to build navigable pages and popups.
What is it?
The Skyframe use cases are a convenient way to define "access points" to UI views (pages and popups) in our Single Page App.
Classification
We classify the Skyframe use cases in two levels:
- Primary level (Pages): In this level, the use cases are triggered through Angular Routes.
- Secondary level (Popups): The use cases in this level can only be triggered by another use case. For example, a page (primary level use case) or popup (secondary level use case) can have a button, which has a click event callback that opens a popup (secondary level use case).
Usage
So we have this entity module:
@NgModule({ imports: [...], declarations: [..., CustomerListPageComponent], providers: [...], entryComponents: [...], exports: [..., CustomerListPageComponent]})@SkfModule({ entities: [Customer]})export class CustomerModule {}the entity routing module:
const routes: Routes = [{ path: '', component: CustomerListPageComponent }];
@NgModule({ imports: [RouterModule.forChild(routes), CustomerModule], exports: [RouterModule]})export class CustomerRoutingModule {}and the app routing module:
const routes: Routes = [ ..., { path: 'customers', // Lazy-loaded child routing module. loadChildren: () => import('./modules/customers/customers-routing.module').then( mod => mod.CustomerRoutingModule ) }, ...];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule]})export class AppRoutingModule {}As you can see, we have a CustomerModule exporting the customer list page component CustomerListPageComponent. After that, the CustomerRoutingModule imports the CustomerModule, in order to define a route path '' (root) to CustomerListPageComponent that it exports. Finally, the AppRoutingModule defines a route path 'customers' that points to CustomerRoutingModule as its child loading module.
With all this defined, when we access the /customers URL, Angular router will look up the routes defined in AppRoutingModule, and the customers route would be matched. Once it matched the customers route, it will load the CustomerRoutingModule, and finally, its '' route will be activated, so the customer list page (CustomerListPageComponent) is rendered.
Registering use cases
To register a use case, we would need the Shell#registerUseCase method:
@NgModule({ imports: [...], declarations: [..., CustomerListPageComponent], providers: [...], entryComponents: [...], exports: [..., CustomerListPageComponent]})@SkfModule({ entities: [Customer]})export class CustomerModule { constructor(shell: Shell) { shell.registerUseCase( Customer, // Associates the use case to `Customer` entity. 'list', // The use case alias we would use to trigger it. '/customers', // The Angular route for primary use case. CustomerListPageComponent // The component to be rendered when triggered. ); }}As you see, we added a constructor to the CustomerModule class and injected the Shell provider. Then we used the Shell#registerUseCase to register a use case for the Customer entity with the 'list' alias, that references the '/customers' Angular route and the component class CustomerListPageComponent.
Triggering as a primary use case
Once we had registered a use case, we are ready to activate it using the Shell#triggerUseCase method. The method receives the entity class that the use case is bound to, and the use case alias we defined at use case registration in the previous step.
For example:
@Component({ ... })export class SidebarComponent { constructor(private shell: Shell) {}
public clickCustomerItem(): void { // Navigates to customer list page this.shell.triggerUseCase(Customer, 'list'); }}Triggering as a secondary use case
The Shell#triggerUseCaseAsPopUp method allows us to activate a popup instead of a page:
@Component({ ... })export class DashboardComponent { constructor(private shell: Shell) {}
public showCustomersPopup(): void { // Opens a popup showing the customer list page this.shell.triggerUseCaseAsPopUp(Customer, 'list'); }}The popup window can be customized with an optional dialogOptions (see MatDialogConfig doc):
@Component({ ... })export class DashboardComponent { constructor(private shell: Shell) {}
public showCustomersPopup(): void { this.shell.triggerUseCaseAsPopUp(Customer, 'list', { dialogOptions: { maxHeight: '90vh', minWidth: '500px' } }); }}Use cases without entity class
In some cases, there may be use cases that have no entities to be bound to. We can use Shell#registerRootUseCase to create use cases without associating any entity to it.
@NgModule({ imports: [...], declarations: [..., DashboardPageComponent], providers: [...], entryComponents: [...], exports: [..., DashboardPageComponent]})export class DashboardModule { constructor(shell: Shell) { shell.registerRootUseCase( 'dashboard', '/dashboard', DashboardPageComponent ); }}Now you can use Shell#triggerRootUseCase to trigger it:
@Component({ ... })export class LoginComponent { constructor(private shell: Shell) {}
public async login(): Promise<void> { await tryLogin(); // Navigate to dashboard page if login success this.shell.triggerRootUseCase('dashboard'); }}Presets
The presets are a good way to define a set of customizations for a use case.
Registering a use case with presets:
this.shell.registerRootUseCase('task', '/task', TaskListComponent, { pendingTasks: { headerText: 'Pending', headerColor: 'yellow' }, doingTasks: { headerText: 'Doing', headerColor: 'blue' }, finishedTasks: { headerText: 'Finished', headerColor: 'green' }, pausedTasks: { headerText: 'Paused', headerColor: 'gray' }});Activate a preset for a component via skf-preset directive:
<app-task-list skf-preset="pendingTasks" [data]="..."></app-task-list>
<app-task-list skf-preset="doingTasks" [data]="..."></app-task-list>
<app-task-list skf-preset="finishedTasks" [data]="..."></app-task-list>
<app-task-list skf-preset="pausedTasks" [data]="..."></app-task-list>Retrieve the preset inside our component:
import { Preset, SkfPresetDirective } from '@skyframe/angular';
@Component({ selector: 'app-task-list', templateUrl: './task-list.html', styleUrls: ['./task-list.scss']})export class TaskListComponent implements AfterViewInit { @Preset() public preset?: SkfPresetDirective;
public ngAfterViewInit(): void { if (this.preset) { const { headerText, headerColor } = this.preset.getPreset(); this.addHeader(headerText, headerColor); } }}