Skip to content

04. UI Layout

ali edited this page Apr 16, 2021 · 3 revisions

UI Layout

One of the major features of Narik is UI layout. As you can inherit from a component ts class, you can use a layout from a base HTML file in a component layout. It's completely different from the content projection. In UI Layout, unlike content projection, no need to have two different components, actually all part of the created UI(UI created after apply layout) has one ts file.

Whit this feature you can create multiple components with one HTML UI.

How you should do that

  1. Create Layouts

    First of all, you should define your layouts. a layout is a HTML file like other HTML files inside your application. it contains HTML tag, you're angular component's tag, directives and...
    But also it can contain narik-section. narik-section specify places that can be customized by views use this layout.

    for example :

    This is default Narik layout for detail(Edit) forms:

    <narik-busy-indicator>
        <narik-mat-toolbar toolbarKey="{{config?.toolbarKey || 'editToolBar'}}">
        </narik-mat-toolbar>
        <mat-card>
            <form *ngIf="form" [formGroup]="form" novalidate #formElement>
            <div narik-section="formContent"></div>
            </form>
        </mat-card>
    </narik-busy-indicator>

    Every view that wants to use this layout, can only change narik-section sections.
    Each view can have multiple narik-section with different names.

    You can have multiple layouts and put them wherever you want inside your application structure.

  2. Config Layout Resolver

    After You define layouts, you should specify how they are used. For this, you should define a LayoutResolver class. this class must have a Resolve method that receives a key and returns layout information.

    LayoutResolver Example:

        class LayoutResolver {
            Resolve(key) {
                switch (key) {
                case "NarikListUi":
                    return {
                        layout: "",
                        layoutUrl: "./src/app/layouts/list-layout.html",
                    };
                case "NarikEditUi":
                    return {
                        layout: "",
                        layoutUrl: "./src/app/layouts/edit-layout.html",
                    };
                case "widget":
                    return {
                        layout: "",
                        layoutUrl: "./src/app/layouts/widget-layout.html",
                    };
                case "widget2":
                    return {
                        layout: "",
                        layoutUrl: "./src/app/layouts/widget-layout2.html",
                    };
    
                default:
                    break;
                }
            }
        }
        module.exports = LayoutResolver;

    The layoutUrl should be a relative path to tsconfig.app.json.

    This sample tells that if some view request "NarikListUi" layout, "./src/app/layouts/list-layout.html" will be returned.

    Resolve method's output is an object with two fields: layout and layoutUrl.

    If the layout field has value, it will be used, otherwise, layoutUrl will be used. Maybe you use layout when your layout is small and you don't want to create an HTML file for that.

    After you define LayoutResolver class you should introduce it to webpack.

    To do this, change extra-webpack.config.js like this:

    const path = require("path");
    const AngularCompilerPlugin = require("@ngtools/webpack/src");
    const LayoutResolver = require("./build-tools/layout-resolver");
    
    module.exports = (config) => {
        const index = config.plugins.findIndex((p) => {
            return p instanceof AngularCompilerPlugin.ivy.AngularWebpackPlugin;
        });
        const options = config.plugins[index].pluginOptions;
        options.directTemplateLoading = false;
        config.plugins.splice(index);
    
        config.plugins.push(
            new AngularCompilerPlugin.ivy.AngularWebpackPlugin(options)
        );
    
        config.module.rules.unshift({
            test: /\.html?$/,
            use: [
            "raw-loader",
            {
                loader: "@narik/webpack-tools",
                options: {
                resolver: new LayoutResolver(),
                basePath: path.dirname(options.tsconfig),
                },
            },
            ],
        });
    
        return config;
    };
  3. Use Layout

    To use layouts, you can use #layout in your html templates.

    See this example:

    <!-- #layout:'NarikEditUi' -->
    <ng-template #formContent>
        <narik-dynamic-form
            [layoutGap]="config?.options?.layoutGap || 10"
            [columnsCount]="config?.options?.columnsCount || 1"
            [groupFields]="config?.options?.groupFields"
            [activeTabGuard]="config?.options?.activeTabGuard != false"
            [activeAutoFocus]="config?.options?.activeAutoFocus != false"
            [defaultFocusField]="config?.options?.defaultFocusField"
            [model]="currentEntity"
            [fields]="fields"
            [form]="form"
        ></narik-dynamic-form>
    </ng-template>

    This template uses NarikEditUi layout. After applying layout on this template, this template will be like this.

    <narik-busy-indicator>
        <narik-mat-toolbar toolbarKey="{{config?.toolbarKey || 'editToolBar'}}">
        </narik-mat-toolbar>
        <mat-card>
            <form *ngIf="form" [formGroup]="form" novalidate #formElement>
                <narik-dynamic-form
                    [layoutGap]="config?.options?.layoutGap || 10"
                    [columnsCount]="config?.options?.columnsCount || 1"
                    [groupFields]="config?.options?.groupFields"
                    [activeTabGuard]="config?.options?.activeTabGuard != false"
                    [activeAutoFocus]="config?.options?.activeAutoFocus != false"
                    [defaultFocusField]="config?.options?.defaultFocusField"
                    [model]="currentEntity"
                    [fields]="fields"
                    [form]="form"
                ></narik-dynamic-form>
            </form>
        </mat-card>
    </narik-busy-indicator>

    narik-section in layout replaces with ng-template in html based on narik-section key.

Be careful : The process of creating view from base layouts takes places at build time, not runtime.

Implementing layout in Angular

You can see more complete examples in these Narik samples and starters

narik-material-starter
narik-material-demo
narik-devextreme-demo
narik-devextreme-starter