RxComp a modern ES6 alternative to Angular component framework
Tags: rxcomp, rxjs, angular, es6
Published: 2019-12-26 21:51:04

In this blog post we'll talk about [RxComp](https://github.com/actarian/rxcomp) a small Javascript component library built on top of [RxJS](https://rxjs.dev/) and still in beta stage, developed as a simple alternative to [Angular](https://angular.io/) framework to use in all those occasions where angular is challenging to apply.

---

## Introduction

Working as a developer, I find many occasions in which for team reasons we can't implement a complex framework like [Angular](https://angular.io/), say for the use of javascript code non-compliant with angular, say for the decision to use a more Vanilla like approach to the project.  

In all those moments we are still missing the power and flexibility of the [Angular](https://angular.io/) declarative syntax and component modularity, up to the point we decided to implement all those features into a compact ES6 javascript library.  

![](https://images.unsplash.com/photo-1505238680356-667803448bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=1650&q=80)

## The goals of the RxComp component library  
Below is a list of what we consider to be the main outcomes of the library  
* Advantages of the Template Declarative Syntax
* Easy implementation
* Modularity
* Extensibility

## What to consider out of scope  
We don't want to rebuild another SPA framework; there is plenty that does an amazing job and cover every needs. For this reason, every feature listed below is considered out of scope  
* ~~Routing~~
* ~~HttpClient~~
* ~~Server Side Rendering~~
* ~~Dependency Injection~~
* ~~Change Detection~~
* ~~Two-Way Data Binding~~
* ~~Zone~~

---

## So what is included in the library?

### Modules
CoreModule is shipped with the main [RxComp](https://github.com/actarian/rxcomp) lib.  
It includes declarations of all the basic directives, structural directives like `*if` and `*for` and Dom directives like `ClassDirective`, `EventDirective`, `InnerHtmlDirective` and `StyleDirective`.  
You can also extend modules through `imports` and `exports` meta tags.  

```js
const declarations = [
    ClassDirective,
    EventDirective,
    ForStructure,
    IfStructure,
    InnerHtmlDirective,
    StyleDirective,
    JsonPipe
];
export default class CoreModule extends Module {}
CoreModule.meta = {
    declarations: [
        ...declarations
    ],
    exports: [
        ...declarations
    ]
};
```
*The CoreModule class declarations and exports*

### Components
Components define areas of responsibility in the user interface. In the MVC paradigm, they could be viewed as controllers.  

The library automatically setup for you these component properties through meta tags:

**selector**  
The CSS selector that identifies the component or directive inside the Dom or inside a template and triggers the instantiation of the component or directive.  

**inputs**  
It is possible to pass input values to components through HTML node attributes, just define yours named inputs as a meta collection and values will be automatically passed with OnPush change detection to the component.  

**outputs**  
As for inputs, outputs are defined as a named meta collection and automatically generated as [RxJS](https://rxjs.dev/) Subjects for output event emitting.  

**hosts**  
You can define hosts as a key / Factory collection that will be auto resolved in the component parent hierarchy.  

**template**  
Even if the lib is designed to work with inline static template syntax, is also possible to define the component's HTML template through the template meta tag.  

```js
TodoItemComponent.meta = {
  selector: '[todo-item-component]',
  inputs: ['item'],
  outputs: ['toggle', 'remove'],
  hosts: {
    host: HostFactory
  },
  template: /* html */ `
    <button type="button" (click)="onToggle(item)">
      <div class="date" [innerHTML]="item.date | date"></div>
      <div class="title" [innerHTML]="item.name"></div>
    </button>
    <button type="button" (click)="onRemove(item)">
      <i class="icon--remove"></i>
    </button>
    `,
};
```
*Example of Component meta tags definition*

### Pipes
Pipes just work as [Angular](https://angular.io/) pipes version, they are static named functions used to transform values through declarative syntax.  

### OnPush Strategy Change Detection
There is no binding change detection on [RxComp](https://github.com/actarian/rxcomp), the only way to update values of a component and all its child instances is through OnPush change detection.  

Just fire `this.pushChanges();` method on a component when updating values is needed.  

### FormModule
FormModule is an extension of the core lib available as a separate [npm](https://nodejs.org) package. It brings form validation and Reactive Forms.  

```js
export default class FormComponent extends Component {

    onInit() {
        const form = new FormGroup({
            firstName: null,
            lastName: null,
            email: null,
        }, RequiredValidator());

        form.changes$.subscribe((changes) => {
            this.pushChanges();
        });

        this.form = form;
    }

}
```
*Use of the FormModule in a component*

---

## In Conclusion  
If you like [Angular](https://angular.io/) declarative syntax but you just want to go Vanilla, [RxComp](https://github.com/actarian/rxcomp) component library come in useful. Built on top of [RxJS](https://rxjs.dev/) it mimics the [Angular](https://angular.io/) declarative syntax with a bundle size of 5.45Kb gzipped or 16.9Kb minified.  

[repository](https://github.com/actarian/rxcomp)  
[demo](https://actarian.github.io/rxcomp-todomvc)  
[demo source](https://github.com/actarian/rxcomp-todomvc)  
[codepen](https://codepen.io/actarian/pen/QWWRZON?editors=0010)  

Any feedback would be much appreciated!

---

#### Bootstrapping Module
```js
import {
    Browser,
    CoreModule,
    Module
} from 'rxcomp';

export default class AppModule extends Module {}

AppModule.meta = {
    imports: [
        CoreModule
    ],
    declarations: [
        TodoItemComponent,
        DatePipe,
    ],
    bootstrap: AppComponent,
};

Browser.bootstrap(AppModule);
```
*Bootstrapping the AppModule*

#### Component Definition
```js
export default class TodoItemComponent extends Component {

  onChanges(changes) {
    this.color = color(changes.item.id);
  }

  onToggle($event) {
    this.toggle.next($event);
  }

  onRemove($event) {
    this.remove.next($event);
  }

}

TodoItemComponent.meta = {
  selector: '[todo-item-component]',
  inputs: ['item'],
  outputs: ['toggle', 'remove'],
  hosts: {
    host: HostFactory
  },
  template: /* html */ `
      <button type="button" (click)="onToggle(item)">
        <div class="date" [innerHTML]="item.date | date"></div>
        <div class="title" [innerHTML]="item.name"></div>
      </button>
      <button type="button" (click)="onRemove(item)">
        <i class="icon--remove"></i>
      </button>
    `,
};
```
*Example of Component definition*

#### Declarative Syntax
```html
<li *for="let item of items" todo-item-component [item]="item" (toggle)="onToggleItem($event)" (remove)="onRemoveItem($event)" [class]="{ done: item.done }" [style]="{ background: background }">
    <button type="button" (click)="onToggle(item)">
        <div class="date" [innerHTML]="item.date | date : 'en-US' : { month: 'short', day: '2-digit', year: 'numeric' }"></div>
        <div class="title" [innerHTML]="item.name"></div>
    </button>
    <button type="button" (click)="onRemove(item)">
        <i class="icon--remove"></i>
    </button>
</li>
```
*Example of the template declarative syntax*

#### LifeCycle Hooks
```js
class MyComponent extends Component {

    /** 
     * MyComponent instance has been initialized
     * we can add custom properties to the instance
     */
    onInit() {
        // ...
    }

    /** 
     * OnPush Strategy update method has been called
     * we receive changes from parent instance
     */
    onChanges(changes) {
        // ...
    }

    /** 
     * Current instance template has been parsed
     * we can handle child html nodes
     */
    onView() {
        // ...
    }

    /** 
     * MyComponent instance has been destroyed
     * we can dispose all custom created references
     */
    onDestroy() {
        // ...
    }

}
```
*LifeCycle Hooks*