Thoughts On Creating Forms using Angular Pipes
Tags: angular, typescript, javascript, webdev
Published: 2019-06-30 16:01:44

---
title: Thoughts On Creating Forms using Angular Pipes
published: true
description: Implementating my toughts of creating forms with pipes
tags: angular, typescript, javascript, webdev
cover_image: https://cdn-images-1.medium.com/max/1200/1*TzXMqzV-YQ-PtJw5gwrpKA.png
---


While working on my last Angular project. I can’t help but notice how unease I felt with lots of HTML input tags spanning through the component template code I was working on. “There should be a more neat way to abstract these input tags”, I murmured.

Afterward, I decided to draft some syntax of my idea of what the abstraction should look like. Aha! What could be the perfect tool for the job? **Pipes**, holy smokes yes! pipes.

Angular pipes provide a very sleek abstraction, a right tool for the abstraction quest. So I came up with the pipe syntax below, neat uh?

> Quick Disclaimer: I was just trying to experiment.

![A draft of pipe syntax to be used for pipe forms.](https://cdn-images-1.medium.com/max/800/1*X4_9NZTOIYM6tQbnQ1wlSQ.png)

Now we have a valid pipe syntax that contains sufficient parameters needed to create an input tag. It’s time we write a pipe that transforms our syntax to a **pseudo** HTML tag. The pipe will return a result which contains an input tag syntax wrapped in double square brackets.

![Example of what pipe-form pipe will return with the provided input.](https://cdn-images-1.medium.com/max/800/1*p6nGiqzmSt807GVrNLl8OQ.png)

*Here is what the code pipe code looks like.* **pipeform.pipe.ts**
```ts
import {
  Pipe,
  PipeTransform
} from '@angular/core';

@Pipe({
  name: 'PipeForm'
})
export class FormPipe implements PipeTransform {
  // transforms the pipe input and returns a string following this format `[[<input/>]]`
  transform(elem: string, type: string, options ? : object): string {
    // declare output container..
    let out: string;
    // switch between the type if element we want to create
    switch (elem) {
      case 'input':
        // case input tag, 
        out = `[[<input type="${type}"`;
        // loop through the options parameter and format it into the out variable like HTML attributes.
        Object.entries(options).forEach((value: string[]) => {
          out += ` ${value[0]}="${value[1]}"`;
        });
        break;
    }
    // append the final, ending string.
    out += '/>]]';
    // we done here ;-)
    return out;
  }

}
```


Yes! That works but just returns a dummy string right? Finally, I then realized that I needed some sort of **container** which I can then use to parse the result returned by the pipe and create the actual input elements then inject them into the **DOM** using **Renderer2**. So I needed to update my initial syntax. To use pipe-forms, you have to wrap it inside the **ngx-pipeform** component which will act as a renderer for the pipe-form. So the updated syntax looks like👇

![The updated syntax for pipe-form, here ngx-pipeform wraps the pipeform-input pipe.](https://cdn-images-1.medium.com/max/800/1*bRF1VnCBbWrprVc4CuIctw.png)

Now let’s create the component that will serve as a wrapper for pipe-forms. Here is a brief on how it works.

1. It takes the result returned by **pipeform pipe**, then parses it.
2. Creates the input tag elements and then injects them into the **DOM**.

*Here is what the code looks like.* **pipeform.component.ts**
```ts
import {
  Component,
  ViewContainerRef,
  AfterViewInit,
  Renderer2
} from '@angular/core';

@Component({
  selector: 'ngx-pipeform',
  template: `<ng-content></ng-content>`,
})
export class PipeformComponent implements AfterViewInit {

  constructor(private viewRef: ViewContainerRef, private rd: Renderer2) {}

  ngAfterViewInit(): void {
    // after view init, lets get things done..
    // filter node type of text..
    // if text matches pipeform syntax, replace it with the input tag
    // the create the element and inject it into the dom with Renderer2.
    // lets travel through the DOM..
    this.recurseDomChildren(this.viewRef.element.nativeElement);
  }

  recurseDomChildren(start) {
    let nodes;
    if (start.childNodes) {
      nodes = start.childNodes;
      this.loopNodeChildren(nodes);
    }
  }

  loopNodeChildren(nodes) {
    let node;
    for (let i = 0; i < nodes.length; i++) {
      node = nodes[i];
      // try to parse each node..
      this.pipeFormParse(node);
      if (node.childNodes) {
        this.recurseDomChildren(node);
      }
    }
  }

  pipeFormParse(node) {
    // if the content of this node is a text node
    if (node.nodeType === 3) {
      // get its text content
      const textContent = node.textContent;
      // match the occurence of the pipe-form syntax, if found return an array of the result.
      const pipeForms = textContent.match(/\[\[(.*?)]]/gi);
      if (pipeForms) {
        // strip the double square brackets from all of the results.
        const readyElements = pipeForms.map(item => item.split('[[')[1].split(']]')[0]);
        // create a div container with Renderer2
        let elem = this.rd.createElement('div');
        // insert the prepaired input tag into the div.
        elem.innerHTML = readyElements.join(' ');
        // replace this current node with the new div node we just created.
        node.parentElement.replaceChild(elem, node);
      }
    }
  }
}
```
Cheers! It works 😂🍻, but there are still a lot of improvements, questions, and comments we need to go over. I will leave it for you to decide.
### Example hosted on Stackblitz
{% stackblitz angular-pipe-form %}
Live demo on Stackblitz - [https://angular-pipe-form.stackblitz.io/](https://angular-pipe-form.stackblitz.io/) Feel free to check the source code [https://stackblitz.com/edit/angular-pipe-form](https://stackblitz.com/edit/angular-pipe-form)


### My Questions
1. It works, but does it worth the effort?
2. What does it cost, I mean performance in the production environment?
3. Does the syntax look a bit nicer or cleaner?

### Improvements
1. Styling the pipe-forms.
2. Making it work with Angular Models, I haven’t attempted this.
3. Form validations, please!
4. Maybe a nice optimization of the DOM traversing logics.

### Final Verdict
Very well 😉, My aim here is just to see if we can use pipes to create forms. Yes, we can! But is it a good approach? Isn’t this out of context of what pipes meant for? Amigo, I don’t know, was just trying out new ways to get things done. Express your thoughts in words as comments below.

Till next time! Peace out.💜