npm package: @smartbit4all/view-context
Description: ## References
Latest version: 1.5.44

# Smart View Context

## References

These packages must be updated in case of a new version:

-   There are no references yet

---

## How to use

### Installation

**First you must install the [_@smartbit4all/session_](https://www.npmjs.com/package/@smartbit4all/session) in order to use this package.**

**Secondly, you must install the [_@smartbit4all/authentication_](https://www.npmjs.com/package/@smartbit4all/authentication) in order to use this package.**

Go to your project, open the terminal and use the following command:

    npm i @smartbit4all/view-context

Then import it in the AppModule:

`app.module.ts:`

    import { SmartViewContextModule, SmartViewContextService } from '@smartbit4all/view-context';
    ...
    @NgModule({
        declarations: [...]
        imports: [
            ...
            SmartViewContextModule,
        ],
        providers: [
            ...
    	    SmartViewContextService,
        ]
        ...
    })

### Prerequisites

**If you have installed and done the required steps to use the SmartSessionService and SmartAuthentication**, please complete the generated AuthenticationService with these new features belove.

**However, if you have not**, please follow the guides of the [_@smartbit4all/session_](https://www.npmjs.com/package/@smartbit4all/session) and [_@smartbit4all/authentication_](https://www.npmjs.com/package/@smartbit4all/authentication).

### Usage

In order to use this package properly, please create a folder called `viewcontext` in which a `view-context.pages.model.ts` shall be created:

-   src/app/viewcontext/
    -   view-context.pages.ts
    -   view-context.handlers.ts

`view-context.pages.ts:`

    export enum Pages {
        // mandatory
        VIEW_CONTEXT = 'viewContextUuid',

        // optionals
        ANY_PAGE = 'anyPage',
        ANY_DIALOG = 'anyDialog,
        ANY_COMPONENT = 'anyComponent,
        ...
    }

`view-context.handlers.ts:`

    import { SmartViewHandlerModel, ViewType } from '@smartbit4all/view-context';
    import { Pages } from './view-context.pages';
    import { AnyDialogService } from '../any-dialog/any-dialog.service';

    export const viewContextHandlers: SmartViewHandlerModel[] = [
        // ViewType.Normal
        {
            name: Pages.ANY_PAGE,
            route: 'any'
        },
        // ViewType.Dialog
        {
            name: Pages.ANY_DIALOG,
            component: AnyDialogComponent
        },
        // ViewType.Normal or ViewType.Dialog
        {
            name: Pages.ANY_COMPONENT,
            route: 'any',
            component: AnyComponent
        }
    ];

Don't forget to define some ApiErrors and inject it into the **AuthenticationService**:

`view-context.api-errors.ts:`

    const viewContextApiErrors: SmartViewContextApiErrors = {
        viewContextApiErrors:  [
            {
                code: "001",
                dialogTitle?: "Error"
                message: "An error message"
                buttonLabel?: "Ok"
            }
        ]
    }

The **AuthenticationService** must be prepared to use the **SmartViewContextService**. The ">>>" marks the snippets you have to add.

`authentication.service.ts:`

    @Injectable({
        providedIn: 'root'
    })
    export class AuthenticationService {
        ...

    >>> headersUpdated: Subject<void> = new Subject();
    >>> bffApisHaveBeenSetUp: boolean = false;

        // The value of this property has to match with the one in the backend.
    >>> messageDialogViewName: string = "messageDialog";

        constructor(
            ...,
        >>> private viewContext: SmartViewContextService,
        >>> private anyBffApi: AnyBffApiService,
        >>> private platformLocation: PlatformLocation
        ) {
        >>> this.viewContext.setViewHandlers(viewContextHandlers);
        >>> this.viewContext.setViewApiErrors(viewContextApiErrors);
        >>> this.viewContext.setMessageDialogName(this.messageDialogViewName);

            // set up SmartSessionService
        >>> this.url = (this.platformLocation as any).location.origin + '/api';
            ...
        >>> this.viewContext.setUrl(this.url);
            ...

        >>> this.setBffApis();
        }

    >>> private setBffApis(): void {
            this.anyBffApi.configuration.basePath = this.url;
            ...
        }

    >>> private updateHeaderOfBffApi(bffApi: any): void {
            bffApi.defaultHeaders = this.session.setUpDefaultHeaders(bffApi.defaultHeaders);
        }

        async startSession(): Promise<void> {
        >>> this.session.apiCanBeSetUp.subscribe(() => {
        >>>     this.updateHeaderOfBffApi(this.anyBffApi);
        >>>     ...

        >>>     // Replace anyBffApi with the first bffApi you set up above
        >>>     if (this.anyBffApi.defaultHeaders.get(Pages.VIEW_CONTEXT)) {
        >>>         this.bffApisHaveBeenSetUp = true;
        >>>     }

        >>>     this.headersUpdated.next();
        >>> });

            await this.session.initialize();

        >>> let isAuthenticated = await this.session.getIsAuthenticated();
        >>> if (isAuthenticated) {
        >>>     let uuid = this.viewContext.getUuidOfPage(Pages.VIEW_CONTEXT);
        >>>     await this.viewContext.initialize(uuid);
    	>>>     this.viewContext.getAndSyncViewContext(uuid);
        >>> }
        }

        async login(request: LocalAuthenticationLoginRequest): Promise<boolean> {
            await this.auth.login(request);
            let isAuth = await this.session.getIsAuthenticated();
            if (isAuth) {
        >>>     await this.viewContext.initialize();

        >>>     await this.anyBffApi.startAnyPage().toPromise();
        >>>     let uuid = this.viewContext.getUuidOfPage(Pages.VIEW_CONTEXT);
        >>>     await this.viewContext.getAndSyncViewContext(uuid);
            }
            return isAuth;
        }

        ...

    }

### SmartLink

In order to use SmartLinks in your application, follow the instruction:

Firstly, set up the routing:

`app-routing.module.ts:`

    const routes: Routes = [
        // Out of the box solution
        {
            path: `redirect/:${SmartLinkChannelVariableInPath}/:${SmartLinkUuidVariableInPath}`,
            component: SmartViewRedirect,
        },
        // Custom solution
        {
            path: `customRedirect/:${SmartLinkChannelVariableInPath}/:${SmartLinkUuidVariableInPath}`,
            component: RedirectComponent,
        },
        ...
    ]

Then you can set up your custom redirect component as you wish:

`redirect.component.ts:`

    @Component({
        selector: "app-redirect",
        templateUrl: "./redirect.component.html",
        styleUrls: ["./redirect.component.css"],
    })
    export class RedirectComponent extends SmartViewRedirect {
        constructor(
            service: SmartViewContextService,
            navigation: SmartNavigationService,
            route: ActivatedRoute
        ) {
            super(
                service,
                navigation,
                route
            );
        }
    }

Then subscribe for the _openSmartLink_ event and handle it in your AuthenticationService:

`authentication.service.ts:`

    constructor(...) {
        ...
        this.viewContext.openSmartLink.subscribe(() => {
            this.handleOpenSmartLinkEvent();
        })
    }

    private async handleOpenSmartLinkEvent(): Promise<void> {
        // Do your business logic here

        // If authentication is needed to open the link:
        let isAuthenticated = await this.isAuthenticated();
        if (isAuthenticated) {
            this.viewContext.openSmartLink.complete();
        } else {
            // Navigate to login screen without reloading the site!
        }
    }

    // Modify this if authentication is needed to open links
    async login(request: LocalAuthenticationLoginRequest): Promise<boolean> {
        await this.auth.login(request);
        let isAuth = await this.session.getIsAuthenticated();
        if (isAuth) {
            await this.viewContext.initialize();

            await this.anyBffApi.startAnyPage().toPromise();

            // If there is a smart link waiting for opening...
            if (this.viewContext.isOpeningSmartLink) {
                this.viewContext.openSmartLink.complete();
                return isAuth;
            }

            let uuid = this.viewContext.getUuidOfPage(Pages.VIEW_CONTEXT);
            await this.viewContext.getAndSyncViewContext(uuid);
        }
        return isAuth;
    }

### UiActions

The ViewContext package contains a common manager for the UiActions. To use this tool, please follow the instructions.

`action.descriptors.ts:`

    import {
        UiActionButtonIconPos,
        UiActionButtonType,
        UiActionDescriptor
    } from '@smartbit4all/view-context';

    // Build your own action descriptors
    export const ActionDescriptors: Map<string, UiActionDescriptor> = new Map([
        [
            'EXAMPLE',
            {
                title: 'Example action',
    	        type: UiActionButtonType.Raised,
    	        color: 'primary',
                dialog: {
                    title: 'Example dialog title',
                    text: 'Example dialog text',
                    actionButton: {
                        caption: 'Example action',
                        color: 'primary'
                    },
                    cancelButton: {
                        caption: 'Cancel',
                        color: ''
                    }
                },
                inputDialog: { ... },
                confirmDialog: { ... },
                feedbackType: UiActionFeedbackType.SNACKBAR;
                feedbackText: "Example feedback";
            }
        ]
    ])

`authentication.service.ts:`

    constructor(...) {
        ...
        // Set your descriptors
        this.viewContext.setActionDescriptors(ActionDescriptors);

        // Set basic feedback
        this.viewContext.commonFeedbackText = "Basic feedback."
        ...
    }

`example.component.ts:`

    @Component({
        selector: 'app-example',
        templateUrl: './example.component.html',
        styleUrls: ['./example.component.css']
    })
    export class ExampleComponent implements UseUiAction {
        possibleActions: UiAction[]; // Comes from the backend
        actions: UiActionModel[] = [];

        // UseUiAction properties
        submit: Subject<void> = new Subject();
    	reSubscribeToChange: Subject<void> = new Subject();

        constructor(private service: ExampleService) {}

        // UseUiAction functions
        getAdditionalParams(uiAction: UiAction): UiActionAdditionalParams {
            // return additional parameters if needed
            throw new Error('Method not implemented.');
        }
        getModel() {
            // return the model of the page
            throw new Error('Method not implemented.');
        }
        async performUiActionRequest(uiActionRequest: UiActionRequest): Promise<any> {
            // perform the uiActionRequest on the proper service
            await this.service.performAction(uiActionRequest)
        }
        handleSpecificDemandsAsynchronously(uiAction: UiAction): Promise<UiActionSpecificDemandResponse> {
            // handle specific demands if needed
            throw new Error('Method not implemented.');
        }

        // Construct
        constructActions(): void {
            this.actions = [];
            let exceptions = ['EXCEPTION'];
            this.possibleActions.map((uiAction: UiAction) => {
                this.actions?.push({
                    uiAction,
                    serviceToUse: this, // bind a class which implements the UseUiAction
                    exception: exceptions.includes(uiAction.code!)
                });
            });
        }
    }

`example.component.html:`

    <smart-ui-action-toolbar [uiActionModels]="actions"></smart-ui-action-toolbar>

---

## Version logs

## @smartbit4all/view-context v1.5.1

**Type: Update**

Requires "@smartbit4all/dialog": "^1.1.0".

## @smartbit4all/view-context v1.5.0

**Type: Update**

From now on the `@smartbit4all/view-context` includes a new module which manages the **UiActions**.

## @smartbit4all/view-context v1.4.14

**Type: Update**

This update contains a solutions for the viewContext error handling after session expiration.

How to use:

In your project your authentication service must implement the SmartViewContextHandlerService interface. After that inject the authentication service into the SmartViewContextService with the setViewContextHandlerService function.

## @smartbit4all/view-context v1.4.9

**Type: Update**

The `UiAction` interface got a new property:

    export interface UiAction {
        ...
        /**
        * Additional parameters for executing the UI action.
        */
        params?: { [key: string]: any; };
    }

## @smartbit4all/view-context v1.4.7

**Type: Bugfix**

Simultaneously opened dialogs could cause an issue when closing them.

## @smartbit4all/view-context v1.4.1

**Type: Update**

The `SmartRedirectConfiguration` has been removed.

The `SmartViewRedirect` became to a component.

## @smartbit4all/view-context v1.4.0

**Type: Update**

Opening a page in our website using a custom link became as easy as it should be.

To apply this new functionality in your application, follow the instructions above.

**New type has been introduced:**

This `SmartRedirectConfiguration` interface is designed to directly set the path variable names for the `SmartViewRedirect` class.

    export interface SmartRedirectConfiguration {
        channelInUrl: string;
        uuidInUrl: string;
    }

## @smartbit4all/view-context v1.3.3

**Type: Update**

The `UiActionInputType` enum got a new option: **Textarea**.

## @smartbit4all/view-context v1.3.2

**Type: Update**

New feature introduced: `getViewConstraint`

Gets related constraints by the uuid of the page.

## @smartbit4all/view-context v1.3.1

**Type: Update**

Major changes have been made in the models:

-   The original `ViewContext` has been renamed to `ViewContextData`
-   The new `ViewContext` is only for backend purposes
-   The `ViewData` no longer contains the property _parameters_
-   The `ViewData` got a new property: _message?: MessageData;_

## @smartbit4all/view-context v1.2.6

**Type: Update**

New type introduced: `UiActionRequest`.

Some of the existing objects have been changed:

-   `UiAction` no longer contains apiName and apiFunction
-   `ViewData` got a model property

## @smartbit4all/view-context v1.2.5

**Type: Update**

New types introduced: `ViewConstraint` and `ComponentConstraint`.

## @smartbit4all/view-context v1.2.4

**Type: Update**

New type introduced: `UiActionSource`.

The `UiAction` got an **identifier** property.

## @smartbit4all/view-context v1.2.0

**Type: Feature**

This version extends the basic features of the `SmartViewContext` with built-in error handling.

**Requirements:**

In case of an error, the backend has to return a `ViewApiError` with the **error code 409**.

    export interface ViewApiError {
        code?: string;
        message?: string;
    }

The package uses an interceptor which catches this error and shows it in a dialog. The settings of the error dialogs can be injected into the `SmartViewContextService`, just like the SmartViewHandlerModel.

    export interface SmartViewContextApiError {
        code: string;
        dialogTitle?: string;
        message: string;
        buttonLabel?: string;
        customAction?: () => void;
    }

    export interface SmartViewContextApiErrors {
        viewContextApiErrors: Array<SmartViewContextApiError>;
    }

## @smartbit4all/view-context v1.1.0

**Type: Feature**

This update contains a new function which help to show and handle messages provided by the ViewContext. The flow is thoroughly handled by this package.

**Type: Update**

From now on the uuid parameter of the `getAndSyncViewContext` function is optional.

    public async getAndSyncViewContext(uuid?: string): Promise<void> {
        if (!uuid) {
            uuid = this.getUuidOfPage(this.tabIdName);
        }
        ...
    }

## @smartbit4all/view-context v1.0.8

**Type: Feature**

In this update `SmartViewContextService` got a new Subject property (_viewContextHasBeenSynced_) which emits an event when the syncView was called.

## @smartbit4all/view-context v1.0.7

**Type: Feature**

In this update `SmartViewContextService` got a new functionality which helps to restore the opened view data after a refresh:

`SmartViewContextService:`

    public restoreViews(): void

## @smartbit4all/view-context v1.0.6

**Type: Feature**

In this update `SmartViewContextService` got a new functionality which helps to get the `ViewData` object by page name:

`SmartViewContextService:`

    public async getViewContextDataByPageName(pageName: string): Promise<ViewData>

## @smartbit4all/view-context v1.0.5

**Type: Feature**

In this update `SmartViewContextService` got a new functionality which helps to detect uuid changes of certain pages.

`SmartViewContextService:`

    public uuidOfPageHasBeenChanged: Subject<string> = new Subject();

## @smartbit4all/view-context v1.0.4

**Type: Bugfix**

ViewContextUUID was not added to HeaderParameters when the viewContext was initialized as an existing one.

## @smartbit4all/view-context v1.0.3

**Type: Bugfix**

Two bug has been fixed in this update:

-   The addig header parameters to SmartSession
-   Routing with query parameters

## @smartbit4all/view-context v1.0.1

**Type: Update**

The package has been published.

## @smartbit4all/view-context v0.1.2

**Type: Feature**

In this update several changes have been done:

1. model changes
2. function publicity changes
3. function implementation

### Model changes

The `SmartViewHandlerModel` has been rethinked, hence the **service** property **has been changed to component**, while **the type has been removed**.

`smart-view-context.model.ts:`

    export interface SmartViewHandlerModel {
        name: string;
        route?: string;
        component?: ComponentType<any>;
    }

### Function publicity changes

In the `SmartViewContextService` the publicity of the **openView and closeView** has been changed **from private to public**.

### Function implementation

The closeView has been implemented, however, it only closes dialogs.

    public closeView(view: ViewData): void {
        let viewHandler: SmartViewHandlerModel | undefined = this.viewHandlers.find(
            (handler) => handler.name === view.viewName
        );

        if (!viewHandler) {
            throw new Error(`SmartViewHandlerModel was not found by view name: ${view.viewName}`);
        }

        if (view.type === ViewType.Dialog) {
            this.dialog.closeDialog();
        } else if (view.type === ViewType.Normal) {
            // TODO: close normal page???
        } else {
            throw new Error("The type of the view and its viewHandler is not the same!");
        }

        view.state = ViewState.Closed;

        this.openedViewUuids.slice(this.openedViewUuids.indexOf(view.uuid), 1);

        sessionStorage.removeItem(view.viewName);

        this.updateViewContext({
            updates: [
                {
                    state: ViewState.Closed,
                    uuid: view.uuid,
                },
            ],
            uuid: this.viewContext?.uuid,
        });
    }

## @smartbit4all/view-context v0.1.1

**Type: Feature**

The SmartViewContext has been created.