[work in progress] feature/xstate/angular
This is a very early implementation that already does its job. It's not ready for a merge yet, however, I'd like to start review with @Andarist asap so that it's easier later on. There might be some setup stuff (or some conventions?) I'm missing, most probably.
usage
export class AppComponent {
#injector = inject(Injector);
toggleActor = injectActor(toggleMachine, undefined, {
injector: this.#injector
});
constructor() {
this.toggleActor.ref.start();
}
handleToggleClick() {
this.toggleActor.send({ type: 'toggle' });
}
}
Nowadays, this is the standard approach in angular to get dependencies straight into a component (or another service):
#injector = inject(Injector);
toggleActor = injectActor(toggleMachine, undefined, {
injector: this.#injector
});
injecting the Injector looks odd, but that's actually expected, since there's a whole hierarchy of injectors, so you might get lots of different injectors when needed. And passing the injector via parameter is also a standard approach in libraries (either your call is within, so called, injection context and our adapter would get the injector itself, or one could pass it explicitly via param). All standard.
requirements
As @Andarist mentioned, there has to be a way to allow components having multiple state machines.
angular v17
The adpater exposes a signal-based API (the new Angular standard API for managing synchronous state).
interface InjectedActor<TLogic extends AnyActorLogic> {
snapshot: Signal<SnapshotFrom<TLogic>>; // signal
send: ActorRefFrom<TLogic>['send'];
ref: ActorRefFrom<TLogic>;
}
It resembles react-based adapter's API.
cleanups
the most important part is actually this: https://github.com/ducin/xstate/blob/feature/xstate/angular/packages/xstate-angular/src/injectActor.ts#L50-L53 all it does is it grabs destroyRef (an angular kinda-equivalent to useEffect return-callback) from a certain injector. Also, pretty standard.
questions
- wouldn't it make sense for the adapter to automatically
start()the machine? Wouldn't it be the most common scenario to immediately start the machine (without the need to do that explicitly)?
further work
I want to finish the following before considering the PR ready:
- cleanups (e.g. comments), obviously
- some more examples (working on it)
- analyzing possibility to provide globally available machines (could be achieved by explicitly creating a service and making it available in certain areas of application, so it's an optional feature)
⚠️ No Changeset found
Latest commit: 504c2964e9dd1eea2ed900919b6737fe09afcb54
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
@Andarist what should we do with this? Angular v17 requires node v18+. Going back to v16 makes little sense for the adapter since there's no stable signal support, and without the signal support the adaption would be significantly smaller (it's the new standard).
error @angular/[email protected]: The engine "node" is incompatible with this module. Expected version "^18.13.0 || >=20.9.0". Got "16.18.1"
error Found incompatible module.
@Andarist what should we do with this?
This might have already been addressed in the meantime. Could you sync with main? Its CI is already using node 20