DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for RxComp a modern ES6 alternative to Angular component framework
Luca Zampetti
Luca Zampetti

Posted on

RxComp a modern ES6 alternative to Angular component framework

In this blog post we'll talk about RxComp a small Javascript component library built on top of RxJS and still in beta stage, developed as a simple alternative to Angular 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, 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 declarative syntax and component modularity, up to the point we decided to implement all those features into a compact ES6 javascript library.

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 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.

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 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.

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 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, 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 package. It brings form validation and Reactive Forms.

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 declarative syntax but you just want to go Vanilla, RxComp component library come in useful. Built on top of RxJS it mimics the Angular declarative syntax with a bundle size of 5.45Kb gzipped or 16.9Kb minified.

repository

demo

demo source

codepen

Any feedback would be much appreciated!


Bootstrapping Module

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

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

<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

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

Top comments (4)

Collapse
 
kosich profile image
Kostia Palchyk • Edited on

Hi, Luca! Nice project!

I'm currently working on a similar path: a framework that combines Rx with JSX.
(so it's kinda more react-ish)

The JSX part allows it to have any data/template on the stream, e.g.:

<div>
{
  interval(1000).pipe( map(x => <span>{ x } seconds passed</span>) )
}
</div>

If you'd want to read more, check out this intro article:
dev.to/kosich/recks-rxjs-based-fra...

Uhh, I envy your Readme and the TODO-list example β€” well done! :)

Also, if you want a fast integration with a router β€” check out router5 router5.js.org/
I've been looking at it myself. It has declarative config and Rx Observable output.
Might suit your framework's module structure well.

Great work!
Keep going!

Collapse
 
actarian profile image
Luca Zampetti

@Kostia Thank You! This is really interesting, I like the neat syntax of the jsx template.

I will dive soon on preact or react.

I've just readed your article and found it very inspiring! ^ ^

I think we both like rxjs and want to use it as the way to handle component state!

And thank you for the router5 tip too!

Collapse
 
kosich profile image
Kostia Palchyk

I think we both like rxjs

We do :)

want to use it as the way to handle component state!

State aaand side-effects with async events management and auto cancellation!
I see, you have "Automatic Subscription / Unsubscription" and "OnPush Strategy" -- great features!

(I'm actually in the middle of writing an article about state-management :) )

Also, if you're looking for a SSOT storage with Observable API:
In the scope of the framework project, I've created this:
github.com/recksjs/redogs
It's a redux-like storage with redux-observable interface and a TS support.

preact or react

From what I've seen, preact has the same as react. Same API / feature set, just has less run-time error-proof handling logic.

People often find JSX a blocker, cause it's violating MV* principle. Yet, once you use a ng-repeat or ng-if in your template β€” you realize that you're adding logic to the View. Since you're adding logic to the View -- why not adding it using JS? You're still developing the View, just using tools you know for years.

I've tried React after a couple of years of Angular (1,2+). And found JSX awesome.
The frustrating part was the data-flow: you can't directly refer to a component child and call a method on it. Rather it's about passing callbacks around and updating parent state to update the child. Aaaand, obviously, Rx is not really native to React ;)

Anyhow, I wish you to have an interesting journey with RxComp and the rest!
Cya!

Thread Thread
 
actarian profile image
Luca Zampetti

People often find JSX a blocker, cause it's violating MV* principle. Yet, once you use a ng-repeat or ng-if in your template β€” you realize that you're adding logic to the View. Since you're adding logic to the View -- why not adding it using JS? You're still developing the View, just using tools you know for years.

Yep, I agree, still I think it's not matter of who's better, every tool has its pros and cons and you have to find what is best suited for the job. I like the idea to learn as many tools as possible and to take from each one its best.

Thank you again for all the hints! )

πŸ‘‹ New to DEV?

Head over to our Welcome Thread and tell us a bit about yourself!