Skip to content

Commit 2ae8508

Browse files
committed
docs, cleanup
1 parent f59cb58 commit 2ae8508

File tree

4 files changed

+423
-28
lines changed

4 files changed

+423
-28
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
<img src="docs/assets/github-hero-2.png" alt="Rimmel.js" style="text-align: start; max-width: 100%;">
1+
<img src="docs/assets/github-hero-3.png" alt="Rimmel.js" style="text-align: start; max-width: 100%;">
22

33
[![npm](https://img.shields.io/npm/v/rimmel.svg)](https://www.npmjs.com/package/rimmel)
4+
![license](https://img.shields.io/npm/l/@reactivehtml/rimmel)
5+
![NPM Downloads](https://img.shields.io/npm/d18m/rimmel)
6+
![Discord](https://img.shields.io/discord/1392597664184537230)
7+
48

59
Rimmel is a powerful, fast and lightweight JavaScript UI library for creating web applications with event streams.
610

@@ -11,6 +15,10 @@ If you are new to reactive streams, there is a [3m crash-course](https://medium.
1115

1216
If you are new to the functional/reactive programming mindset, this [interactive tutorial](https://reactivex.io/learnrx/) may be an especially useful introduction to map/reduce in the reactive world.
1317

18+
If you come from Angular, check out [this page](./docs/migrating/angular.md)<br>
19+
If you come from React, check out [this page](./docs/migrating/react.md)<br>
20+
21+
1422
## Hello World 👋🌏🏖️😎
1523
Let's jump straight in. The "Hello World" for reactive user interfaces is the classic click counter: one button, you click it, ze counts it.
1624

@@ -780,6 +788,10 @@ Come say hi, there's a good amount of work to do and the roadmap is below.
780788
- i18n (leverage sink pipelines to make localisation trivial)
781789
- Native Observables — exciting challenges for the upcoming standard
782790

791+
# Chat?
792+
793+
Find us [on Discord](https://discord.com/channels/1392597664184537230)
794+
783795
# Building, testing, hacking 🪓
784796
To work with Rimmel locally, check it out then either with bun or other runtimes:
785797

@@ -796,3 +808,5 @@ npm run kitchen-sink
796808

797809
Or you can just run it with one click [on StackBlitz](https://stackblitz.com/~/github.com/ReactiveHTML/rimmel?file=examples/kitchen-sink/index.ts&startScript=kitchen-sink)
798810

811+
Built on 🌎 with ✍️, 🧠 and a bit of 🤖
812+

docs/migrating/angular.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Rimmel for Angular Developers
2+
3+
If you're coming from Angular, this page will help you understand key concepts, similarities and differences to help you grasp the right mindset
4+
5+
## Templates, Tagged Templates
6+
Angular supports both static strings or tagged templates.
7+
However, all expreessions are part of the templates themselves and evaluated internally by Angular's template parser.
8+
9+
With Rimmel you use its `rml` template tag and all expressions must use the standard `${}` if you want to insert some.
10+
11+
Template expressions follow the standard JavaScript semantics and scope, so if you want to reference a variable, a promise, a stream, those need to be in lexical scope.
12+
13+
```js
14+
const randomNum = Math.random();
15+
16+
document.body.innerHTML = rml`
17+
<div>This is a random number: ${randomNum}</div>
18+
`;
19+
```
20+
21+
## Class methods, properties => Sink Expressions
22+
In Angular you define helper methods, functions or state as members of the given component class.
23+
24+
In Rimmel you don't need classes. Literally anything that's in scope can be used in a template.
25+
26+
```js
27+
// A plain value
28+
const randomNum = Math.random();
29+
30+
// A Promise
31+
const randomPromise = fetch('/api/random').then(r=>r.json());
32+
33+
// An Observable Stream
34+
const randomStream = fromEvent(document, 'click').pipe(
35+
map(e=>e.clientX)
36+
);
37+
38+
target.innerHTML = rml`
39+
<div>A number: ${randomNum}</div>
40+
<div>A promise: ${randomPromise}</div>
41+
<div>A stream: ${randomStream}</div>
42+
`;
43+
```
44+
45+
46+
## Event Handlers => Implicit binding
47+
In Angular event handlers are defined according to its custom DSL: `(click)=handler()`.
48+
49+
In Rimmel your event handlers can be strings (like in plain Vanilla), references to functions or Observable Streams.
50+
51+
```ts
52+
// A handler function
53+
const handlerFn = (e: Event) => {
54+
doSomethingWith(e);
55+
}
56+
57+
// An Observable Stream
58+
const handlerStream = new Subject<Event>().pipe(
59+
map(e => somethingWith(e))
60+
);
61+
62+
const RimmelComponent = () => rml`
63+
<!-- a handler string -->
64+
<button onclick="alert('clicked')">click</button>
65+
66+
<!-- a handler function -->
67+
<button onclick="${handlerFn}">click</button>
68+
69+
<!-- a handler stream -->
70+
<button onclick="${handlerStream}">click</button>
71+
`;
72+
```
73+
74+
## Async Pipes => Implicit binding
75+
In Angular you have the Async Pipe which gives you a way to sink the content of a stream.
76+
77+
In Rimmel you don't need to specify it. By default, any Promise or Observable can be sinked directly anywhere in the DOM, including content, classes, CSS styles, attributes, event handlers.
78+
79+
```js
80+
// An Observable Stream
81+
const red = Promise.resolve('red');
82+
83+
const RimmelComponent = () => rml`
84+
<div style="color: ${red};" class="big ${red}">
85+
${red}
86+
</div>
87+
`;
88+
```
89+
90+
Rimmel doesn't stop here. You can go to extremes by sinking whole objects by means of Attribute Mixins:
91+
92+
```js
93+
const Draggable = () => {
94+
const drag = ... the logic
95+
return {
96+
onmousedown: drag,
97+
class: 'draggable',
98+
}
99+
};
100+
101+
const RimmelComponent = () => rml`
102+
<div ...${Draggable()}>
103+
this is draggable
104+
</div>
105+
`;
106+
```
107+
108+
You can find a running example [here](https://stackblitz.com/edit/rimmel-draggable).
109+
110+
111+
## Signals, NgRx => RxJS
112+
If you want to use RxJS for state in Angular, you either have to use external libraries such as NgRx or convert your streams to Signals.
113+
114+
In Rimmel you don't need any of that as Observables, Subjects, BehaviorSubjects are supported out of the box and you don't need Signals either.
115+
116+
Observables, contrarily to some narrative, are perfectly capable of handling state thanks to the extensive support offered by Rimmel's templating system.
117+
118+
## Change Detection => Fine-Grained Updates
119+
Change Detection is Angular's strategy to make non-reactive variables work as if they were reactive.
120+
121+
In Rimmel, state and transition are always implicitly embedded in streams, whether promises or observables, so whenever they emit a value, that's just "sinked" into the DOM directly.
122+
This is often referred-to as fine-grained updates which make any change detection strategy totally unnecessary.
123+
124+
## Build Tools
125+
Angular diverged significantly from HTML and JavaScript standard semantics so it's heavily dependent on transpilation and build tools.
126+
127+
Rimmel's syntax is 100% pure standard JavaScript, so it doesn't require any build tools, but you are absolutely free to use any build tools of your choice.
128+
129+
## Lifecycle Events => nothing
130+
Angular provides a number of lifecycle events such as `ngOnInit` to help initialise/cleanup components.
131+
This is central in imperative programming but comes with an extended bug surface in case you forget to perform some key setup or cleanup actions.
132+
133+
Rimmel provides declarative patterns to perform all operations you would normally perform inside lifecycle event handlers, such as setting up events, sinks, initial values, etc.
134+
135+
## Subscriptions/Unsubscriptions => Implicit stream binding
136+
In Angular you're responsible for most subscriptions and unsubscriptions of the reactive streams you use.
137+
138+
In Rimmel all subscriptions/unsubscriptions are managed by the library. In fact you never have to make calls to `subscribe`/`unsubscribe` in your components.
139+
140+
## Dependency Injection => Extensible Effects or Messagebus
141+
Angular makes extensive use of its own DI system to help separating components and dependencies.
142+
143+
There are other patterns that can achieve the same result:
144+
- A messagebus like the one that from [the-observable-plugin-system](https://github.com/ReactiveHTML/the-observable-plugin-system), which lets you connect your modules purely via messages, making it very easy to replace them in unit tests.
145+
- Extensible Effects is a pattern in Functional Programming in which effects are only declared by a component, but executed separately.
146+
147+
## Services => Extensible Effect Handlers
148+
Angular services are singletons, available everywhere, that perform various side effects or manage global state.
149+
150+
For simple, non-enterprise applications you can create singleton services and import them directly, but this becomes an anti-pattern the more your application grows.
151+

0 commit comments

Comments
 (0)