@rhtml/experiments v0.0.134
@rhtml/experiments
Experimental Components based on Monadic approach of building html blocks.
Declarative way of defining webcomponent via html itself.
In the future no javascript will present inside html only Functional components representing program behaviour
Monadic webcomponents are binded to following rules:
- Monadadic
webcomponentsare like containers with scoped logic inside Operatorsare empty pure components representing program settings- Monad container is creating composable logic based on operators provided inside his
slot - Monad container will clean logic when composition is finished (removing operators from DOM Tree)
- Rest of the program
Installation
npm i @rhtml/experimentsUsage
import '@rhtml/experiments';Declarative webcomponent creation via html only
<r-component>
<r-selector>r-pesho</r-selector>
<r-props>
<r-prop key="pesho" type="Number"></r-prop>
<r-prop key="pesho2" type="Boolean"></r-prop>
<r-prop key="pesho3" type="String"></r-prop>
</r-props>
<r-render .state=${(s: RPeshoComponent) => html`
${s.pesho} | ${s.pesho2} | ${s.pesho3}
`}>
</r-render>
</r-component>
<r-pesho pesho="oh my god" pesho2="az sym pesho2" pesho3="az sym pesho3"></r-pesho>Logic Map
<r-component>- Monad container<r-selector>- Operator forr-component<r-template>- Operator representingtemplatefor thewebcomponent<r-props>- Monadic container storingpropertiesof thewebcomponent<r-prop>- Monad working only insider-propsmonad<r-key>- Operator working only insider-propmonad<r-value>- Operator working only insider-propmonad
We can represent this as a tree from top to bottom
r-component(Monad)(Stores logic for working withr-selector,r-template,r-props)r-selector(Operator working withr-componentmonad)r-template(Operator working withr-componentmonad)r-props(Monad) (Stores logic forr-propmonad)r-prop(Monad)(Stores logic forr-keyandr-valuecomposition)r-key(Operator working insider-propmonad)r-value(Operator working insider-propmonad)
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ┌───────────────┐ │
│ │ `r-component` │ │
│ │ ┌───────────┐ │ │
│ │ │ Monad │ │ - Stores logic for working with `r-selector`, `r-template`, `r-props` │
│ │ └───────────┘ │ │
│ └───────────────┘ │
│ | │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ `r-selector` │ │ `r-template` │ │ `r-props` │ │
│ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │
│ │ │ Operator │ │ │ │ Operator │ │ │ │ Monad │ │ - Components working with `r-component` monad │
│ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ - `r-props` Stores logic for `r-prop` monad │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
│ | │
│ ┌───────────────┐ │
│ │ `r-prop` │ │
│ │ ┌───────────┐ │ │
│ │ │ Monad │ │ - Monad working with `r-props` monad │
│ │ └───────────┘ │ │
│ └───────────────┘ │
│ | │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ `r-key` │ │ `r-value` │ │
│ │ ┌───────────┐ │ │ ┌───────────┐ │ │
│ │ │ Operator │ │ │ │ Operator │ │ -Operators working inside `r-prop` |
│ │ └───────────┘ │ │ └───────────┘ │ │
│ └───────────────┘ └───────────────┘ │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘High order web components created via html
r-render > statewill be executed everytime when property is changedStateof the component can be changed withsetStatefunction exposed on second argumentsetStatefunction will trigger change detection only on properties defined insider-propsasr-propwithkeyandtype- There is a single predfined property called
loadingand it is set by default totrue. This property is important since it can be used to show loading information when long running job is executed
<r-component>
<r-selector>r-counter</r-selector>
<r-props>
<r-prop key="value" type="Number"></r-prop>
</r-props>
<r-render .state=${({ value, loading }, setState) => html`
<button @click=${() => setState({ value: value + value, loading: false })}>
Increment
</button>
<r-if .exp=${loading}>
Loading...
</r-if>
<p>${value}</p>
`}>
</r-render>
</r-component>Can be used as follow:
<r-counter value="5"></r-counter>Webcomponent as a Service
Important aspect of usage is that components as a service are fire and forget what does that mean ?
- When component is initialized component will be
self destroyedwiththis.remove() - Once initialized inside the
DOM Treeservice componentwill firerunmethod and is no longer needed inside the dom tree - The
runcommand is executed and ourrunningjob stay alive even after component is self removed - The idea to component be removed once initialized is in order to remove unused component from the
DOMsince we use it only for getting our data from backend(for example)
User service
import { Component } from '@rxdi/lit-html';
import { LitServiceElement } from '@rhtml/experiments';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
@Component({
selector: 'user-service'
})
export class UserService extends LitServiceElement {
getUserById(id: number) {
return of({id, name: 'Kristyian Tachev'}).pipe(delay(2000)).toPromise()
}
}What is LitServiceElement ?
- A generic class defining 2 simple metods
runandOnUpdateFirst
import { LitElement, property } from '@rxdi/lit-html';
export class LitServiceElement<T = {}> extends LitElement {
@property({ type: Object })
run: (self: T) => void = () => null;
OnUpdateFirst() {
this.remove();
this.run.call(this);
}
}- After that we can use our
webcomponent servicelike so:
html`
<user-service .run=${async function(this: UserService) {
const user = await this.getUserById(userId);
console.log(user);
}}
></user-service>
`Real world example
interface IUser {
id: number;
name: string;
}
interface IState {
userId: number;
user: IUser;
}<r-part>
<r-state .value=${{ loading: true, userId: 1, user: {} }}></r-state>
<r-render .state=${({ userId, loading, user }: IState, setState: (state: IState) => void) => html`
<user-service .run=${async function(this: UserService) {
setState({
userId,
user: await this.getUserById(userId),
loading: false
});
}}
></user-service>
<r-if .exp=${loading}>
Loading
</r-if>
<r-if .exp=${!loading}>
<p>User id: ${user.id}</p>
<p>User name: ${user.name}</p>
</r-if>
`}
>
</r-render>
</r-part>Hydrating component so it will become web-component
import { Hydrate } from '@rhtml/experiments';
import { UserService } from './user.service'; /* used only for typing purposes will not be included in bundle */
const UserProfile = html`
<r-component>
<r-selector>user-profile</r-selector>
<r-props>
<r-prop key="userId" type="String"></r-prop>
</r-props>
<r-render .state=${({ loading, userId, user = {} }: IState, setState: (s: IState) => void) => html`
<user-service .run=${async function(this: UserService) {
setState({
userId,
user: await this.getUserById(userId),
loading: false
});
}}
></user-service>
<r-if .exp=${loading}>
Loading...
</r-if>
<r-if .exp=${!loading}>
<p>User id: ${user.id}</p>
<p>User name: ${user.name}</p>
</r-if>
`}>
</r-render>
</r-component>
`;
Hydrate(UserProfile);
export declare class UserProfileComponent extends LitElement {
userId: string;
}
declare global {
interface HTMLElementTagNameMap {
'user-profile': UserProfileComponent;
}
} <user-profile userId="100000"></user-profile>9 months ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago