Advertisement

#5 Form Snippets Manager

<p> In this article we will layout the left hand side of our interface as well as provide some styling. The format and layout that we are using will be used multiple times so to make it easier to implement for other examples we will create a snippets manager that we can reuse latter on. </p>

Correction and small addition

  • WebUi
    • Source
      • forms
        • examples
          • basic
            • basic-example.component.scss

We are going to start by make a correction for the previous video. In that video I incorrectly used the .controls class in the basic example component's scss file instead of the correct .controls-template-component as shown in (a). While we are at it we are also going to add the overflow: auto; as well.

basic-example.component.scss (1)

...
.controls-template-component { // <-- Renamed from .controls
    grid-area: controls;
    padding: $gutter;
    overflow: auto; // <-- Added
}
(a) We need to make a correction from the previous video by changing the .controls class to .controls-template-component.

Time to stub out the component and template

  • WebUi
    • Source
      • forms
        • examples
          • basic
            • basic-example.component.pug
            • basic-example.component.scss
            • basic-example.component.ts

As I have stated previously we are going to display some code snippets to the user to show how to use our components. Both in their templates as well as any configuration contained within the component if any. We start by creating a copy of methods that will return the component and template snippets (b). Next we need to update our template to display the information (c). Lastly we need to add some styling (d).

basic-example.component.ts (1)

...
export class BasicExampleComponent {
    ...
    private tGetSnippetComponent = (key: string) => {
        return `component ${key}`;
    }

    private tGetSnippetTemplate = (key: string) => {
        return `template ${key}`;
    }
}
(b) Stubbing out the methods that will return our component and template snippets.

basic-example.component.pug (1)

div.example-container
    div.example-content
        ...
        div.controls-template-component
            xc-form-group(#basicFormGroup="", name="Basic Form Group" )
                div.controls
                    div.control
                        xc-input(label="Input", name="input" )
                    div.template
                        label Template
                        pre
                            code.language-pug {{tGetSnippetTemplate("input")}}
                    div.component
                        label Component
                        pre
                            code.language-typescript {{tGetSnippetComponent("input")}}
(c) Updating our template to display the information.

basic-example.component.scss (2)

...
.controls {
    display: grid;
    grid-template-columns: auto 1fr 1fr;
    grid-template-areas: "control template component";
    grid-column-gap: $gutter;

    .control {
        grid-area: control;
        width: 18.75em;
    }

    .component {
        grid-area: component;
    }

    .template {
        grid-area: template;
    }
}
(d) Styling the control and component/template snippets.

Enter the snippet manager

  • WebUi
    • Source
      • forms
        • examples
          • basic
            • basic-example.component.ts
        • forms.index.ts
        • snippets-manager.ts

The displaying of these snippets will be core feature of the all of the example components that we create. Because of that we will create a class that will help us create and serve up the snippets (e). Next let us add the snippets manager to our forms index (f) for easy importing. And with the snippets manager created we can update our basic example to make use of it (g).

Advertisement

snippets-manager.ts (1)

type Snippet = { component?: string; template: string; };

export class SnippetsManager {
    private readonly _snippets: Map<string, Snippet> = null;

    constructor(snippets: Array<[string, Snippet]>) {
        this._snippets = new Map(snippets);
    }

    public getComponent = (key: string) => {
        return this._snippets.get(key).component || "// none required";
    }

    public getTemplate = (key: string) => {
        return this._snippets.get(key).template;
    }
}
(e) The snippets manager that will help us create and serve up our component and template snippets.

forms.index.ts (1)

...
export * from "./snippets-manager";
...
(f) Adding an export statement to our forms index so that we can easily import the snippets manager to our example components.

basic-example.component.ts (2)

...
import { SnippetsManager, ... } from "../../forms.index";
...
export class BasicExampleComponent {
    ...
    private _snippets = new SnippetsManager([
        ["input", { template: 'xc-input(label="Input", name="input")' }]
    ]);
    ...
    private tGetSnippetComponent = (key: string) => {
        return this._snippets.getComponent(key);
    }

    private tGetSnippetTemplate = (key: string) => {
        return this._snippets.getTemplate(key);
    }
}
(g) Time to use the snippets manager to display the information for our one input control.

Display the overall template and component

  • WebUi
    • Source
      • forms
        • examples
          • basic
            • basic-example.component.pug
            • basic-example.component.scss
            • basic-example.component.ts

In addition to showing the component configuration and template for each individual control we are also going to show and overall component configuration and template. So just as before we will start by stubbing out the functionality within our basic example component (h). Next we will update our template (i) and add some styling (j).

basic-example.component.ts (3)

...
export class BasicExampleComponent {
    ...
    private get tComponent() {
        return `import { Component, ViewChild } from "@angular/core";

import { XcFormGroupComponent } from "../../forms.index";
@Component({
    selector: "basic-example",
    template: require("./basic-example.component.pug"),
    styles: [require("./basic-example.component.scss")]
})
export class BasicExampleComponent {
    @ViewChild("basicFormGroup") private readonly _form: XcFormGroupComponent = null;
/// To be added
}`;
    }
    ...
    private get tTemplate() {
        return `xc-form-group.form-group(#basicFormGroup="", name="Basic Form Group" )
/// To be added`;
    }
    ...
}
(h) Stubbing out the getters for the overall component configuration and template.

basic-example.component.pug (2)

div.example-container
    div.example-content
        ...
        div.controls-template-component
            xc-form-group(#basicFormGroup="", name="Basic Form Group" )
                ...
                hr
                div.template-component
                    div.template
                        label Template
                        pre
                            code.language-pug {{tTemplate}}
                    div.component
                        label Component
                        pre
                            code.language-typescript {{tComponent}}
(i) Adjusting the template to display the new information.

basic-example.component.scss (3)

...
.template-component {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "template component";
    grid-column-gap: $gutter;

    .component {
        grid-area: component;
    }

    .template {
        grid-area: template;
    }
}
(j) Adding a bit of styling.

Help us out again snippets manager

  • WebUi
    • Source
      • forms
        • examples
          • basic
            • basic-example.component.ts
        • snippets-manager.ts

Of course the last thing to do is to modify our snippets manager to provide us the component configuration and templates for the individual controls so that we can include them in the overall information (k). With the changes made to our snippets manager we can update our basic example component to take advantage of the actual information and not the temporary information that we used previously (l).

snippets-manager.ts (2)

import { of, from, range } from "rxjs";
import { filter, map, mergeMap, reduce, takeWhile } from "rxjs/operators";
...
export class SnippetsManager {
    ...
    public snippetsToComponent = (indent: number = 1) => {
        return this.snippetsToCode(indent, (s: Snippet) => s.component);
    }

    public snippetsToTemplate = (indent: number = 1) => {
        return this.snippetsToCode(indent, (s: Snippet) => s.template);
    }

    private snippetsToCode = (indent: number, f: (s: Snippet) => string) => {
        let res: string;
        from(this._snippets)
            .pipe(
                map(x => this.indent(indent, f(x[1]))),
                filter(x => typeof x !== "undefined" && x !== null),
                reduce((acc, x) => `${acc}${x}\n`, "")
            )
            .subscribe(x => res = x);
        return res.replace(/\n$/, "");
    }

    private indent = (indent: number, str: string) => {
        if (typeof str === "undefined" || str === null) {
            return null;
        }

        let space: string;
        range(0, indent)
            .pipe(
                takeWhile(x => x < indent),
                reduce((acc: string, _: number) => `${acc}    `, "")
            )
            .subscribe((x: string) => space = x);

        let res: string;
        of(str)
            .pipe(
                mergeMap((x: string) => from(x.split(/\n/))),
                reduce((acc: string, x: string) => `${acc}${space}${x}\n`, ""),
            )
            .subscribe(x => res = x);
        return res.replace(/\n$/, "");
    }
}
(k) Updating our snippets manager so that it will provide the information that we need to include with our overall component configuration and template.

basic-example.component.ts (4)

...
export class BasicExampleComponent {
    ...
    private get tComponent() {
        return `import { Component, ViewChild } from "@angular/core";

import { XcFormGroupComponent } from "../../forms.index";
@Component({
    selector: "basic-example",
    template: require("./basic-example.component.pug"),
    styles: [require("./basic-example.component.scss")]
})
export class BasicExampleComponent {
    @ViewChild("basicFormGroup") private readonly _form: XcFormGroupComponent = null;
    ${this._snippets.snippetsToComponent()}
}`;
    }
    ...
    private get tTemplate() {
        return `xc-form-group.form-group(#basicFormGroup="", name="Basic Form Group" )
    ${this._snippets.snippetsToTemplate()}`;
    }
    ...
}
(l) Time to include the acutal information in our component.
Exciton Interactive LLC
Advertisement