#7 Every Good Form Needs a Textarea
Friday, July 20, 2018
In this article we will be adding a new control to our library namely a textarea control. We will make the job of adding new controls infinitely easier by making use of the mixin capabilities of both pug and scss.
Parts
- Part 14: Pipes to the Rescue
- Part 13: Rest of the Examples
- Part 12: Checkbox Example
- Part 11: Adding a Radio Button
- Part 10: Adding a Radio Group
- Part 9: Adding a Select
- Part 8: Adding a Checkbox
- Part 7: Adding a Textarea
- Part 6: Highlighting with Prismjs
- Part 5: Form Snippets Manager
- Part 4: Accessing Form Data
- Part 3: Angular Form Group Interop
- Part 2: The Form Group
- Part 1: Forms Project Creation
Specifying the control value
-
WebUi
-
Source
-
forms
-
controls
-
input
- xc-input.component.ts
- xc-form-control.ts
-
input
- xc-form-group.component.ts
-
controls
-
forms
-
Source
We are going to start by making a small change to the definition for our form control base class by making it a generic class where we must specify the type of value the form control will take (a). My main reason, at least for right now, in doing this is to specify in code my expectation for the input control we have already created. That expectation is that although an html input make contain a boolean value, as in the case for a checkbox, our input will be used for html elements that contain a string value. With that change made we have to update the class declaration for the input (b) and provide a type to our query list in the form group (c).
xc-form-control.ts (1)
export abstract class XcFormControl<TValue> extends ... {
public get value() { return this.formControlInternal.value as TValue; }
public set value(value: TValue) { this.formControlInternal.setValue(value); }
}
xc-input.component.ts (1)
...
export class XcInputComponent extends XcFormControl<string> {
...
}
xc-form-group.component.ts (1)
...
export class XcFormGroupComponent implements ... {
@ContentChildren(XcFormControl) private readonly _formControls: QueryList<XcFormControl<any>> = null;
...
}
Why are we using pug instead of html?
-
WebUi
-
Source
-
forms
-
controls
-
input
- xc-input.component.pug
- xc-input.component.scss
- controls.mixins.pug
- controls.mixins.scss
-
input
-
controls
-
forms
-
Source
Before we add a new control we are going to make use of the fact that we are using pug instead just html for our component templates. To do this we are just going to create a mixin file (d) that will, you guessed it, contain the mixins that we will use in all of our control templates. With the mixins created we can simply include them in our input template as shown in (e).
controls.mixins.pug (1)
mixin formControlGroup
div.xc-form-control-group(*ngIf="tGroup", [formGroup]="tGroup")
label(*ngIf="tLabel") {{tLabel}}
block
mixin input
input([type]="tType", [formControlName]="tName")
xc-input.component.pug (1)
include ../controls.mixins.pug
+formControlGroup
+input
Although we really do not have any styling that we would like to share between our controls we will in the future. So we will just do the same thing that we did with our pug templates and create a mixins file for our scss as well (f). And once again we will include the mixin within our style file for our input component (g).
controls.mixins.scss (1)
@mixin formControlGroup {
.xc-form-control-group {
}
}
xc-input.component.scss (1)
@import "../controls.mixins.scss";
@include formControlGroup;
Welcome to the party textarea
-
WebUi
-
Source
-
forms
-
controls
-
textarea
- xc-textarea.component.pug
- xc-textarea.component.scss
- xc-textarea.component.ts
- controls.mixins.pug
-
textarea
-
examples
- form-examples.module.ts
-
controls
-
forms
-
Source
We are going to start by adding a textarea control since it is just about the same as the input we have already created. The first thing we will do is create a mixin for the textarea (h).
controls.mixins.pug (2)
...
mixin textarea
textarea([formControlName]="tName")
To create our textarea control it is now just a simple matter of creating the template (i), the styling (j) and the component definition (k).
xc-textarea.component.pug (1)
include ../controls.mixins.pug
+formControlGroup
+textarea
xc-textarea.component.scss (1)
@import "../controls.mixins.scss";
@include formControlGroup;
xc-textarea.component.ts (1)
import { forwardRef, Component } from "@angular/core";
import { XcFormControl } from "../xc-form-control";
@Component({
selector: "xc-textarea",
template: require("./xc-textarea.component.pug"),
styles: [require("./xc-textarea.component.scss")],
providers: [{ provide: XcFormControl, useExisting: forwardRef(() => XcTextAreaComponent) }]
})
export class XcTextAreaComponent extends XcFormControl<string> {
protected get tagName() { return "xc-textarea"; }
}
Of course we can't forget to add an export for our textarea to the controls index file (l). While we are at it we might as well add a declaration for our textarea control to the form examples module definition (m).
controls.index.ts (1)
...
export * from "./textarea/xc-textarea.component";
form-examples.module.ts (1)
...
import {
..., XcTextAreaComponent
} from "./form-examples.index";
...
@NgModule({
...,
declarations: [
...,
XcTextAreaComponent
],
...
})
export class FormExamplesModule { }
platformBrowserDynamic().bootstrapModule(FormExamplesModule);
Always a correction to be made
-
WebUi
-
Source
-
forms
-
examples
-
basic
- basic-example.component.pug
- basic-example.component.scss
-
basic
-
examples
-
forms
-
Source
Before we add the textarea to our basic example we need to make some adjustments. We will start by changing the css class inside our form group from 'controls' to 'control-row' (n). As the previous name implies it was going to be the container for all of the controls but that would require us to update our grid layout every time we added a control. While this is not impossible it is annoying so instead we will simple wrap each of our controls with the 'control-row' div. The next change is not super important but there is no reason for anything not related to a form control to be contained within our form group so we just need to outdent the hr and the following div. With that done we need to update our style file (o).
basic-example.component.pug (1)
div.example-container
div.example-content
...
div.controls-template-component
xc-form-group(... )
div.control-row <!-- <-- Renamed from controls-->
hr <!-- <-- Outdent by one level -->
div.template-component <!-- <-- Outdent by one level -->
...
basic-example.component.scss (1)
...
.control-row { /*<-- Renamed from controls*/
...
}
...
The more the mixins the merrier
-
WebUi
-
Source
-
forms
-
examples
-
basic
- basic-example.component.pug
- examples.mixins.pug
-
basic
-
examples
-
forms
-
Source
Of course every time we add a control to our example it will have the same general shape in the html so it's time to add another mixin (p). And next up is updating our example template to make use of the new mixin (q).
examples.mixins.pug (1)
mixin controlRow(key)
div.control-row
div.control
block
div.template
label Template
pre
code.language-pug {{tGetSnippetTemplate("#{key}")}}
div.component
label Component
pre
code.language-typescript {{tGetSnippetComponent("#{key}")}}
basic-example.component.pug (2)
include ../examples.mixins.pug
div.example-container
div.example-content
...
div.controls-template-component
xc-form-group(... )
+controlRow("input")
xc-input(label="Input", name="input" )
...
Finally we have a textarea control
-
WebUi
-
Source
-
forms
-
examples
-
basic
- basic-example.component.pug
- basic-example.component.ts
-
basic
-
examples
-
forms
-
Source
With all of that done it is a simple matter to add the textarea to our example template (r). Of course to make everything work right we need to add a snippet to our snippet manager (s). And voila we have a textarea control.
basic-example.component.pug (3)
...
div.example-container
div.example-content
...
div.controls-template-component
xc-form-group(... )
...
+controlRow("textarea")
xc-textarea(label="Textarea", name="textarea" )
...
basic-example.component.ts (1)
...
export class BasicExampleComponent implements ... {
...
private readonly _snippets = new SnippetsManager([
...,
["textarea", { template: 'xc-textarea(label="Textarea", name="textarea" )' }]
]);
...
}