ember-statechart-component v7.1.0
ember-statechart-component
Use XState Machines as components.
Installation
ember install ember-statechart-component
# or
npm install ember-statechart-component
# or
yarn add ember-statechart-component
To be able to use XState state.matches
method in our templates,
we will first need ember-source@4.5+
or a HelperManager for
handling vanilla functions.
ember-functions-as-helper-polyfill
provides one:
ember install ember-functions-as-helper-polyfill
# or
npm install ember-functions-as-helper-polyfill
# or
yarn add ember-functions-as-helper-polyfill
In app/app.js / app/app.ts, a one time setup function will need to be called so that the ComponentManager is registered.
import Application from '@ember/application';
import config from 'ember-app/config/environment';
import loadInitializers from 'ember-load-initializers';
import Resolver from 'ember-resolver';
import { setupComponentMachines } 'ember-statechart-component';
export default class App extends Application {
modulePrefix = config.modulePrefix;
podModulePrefix = config.podModulePrefix;
Resolver = Resolver;
}
loadInitializers(App, config.modulePrefix);
setupComponentMachines();
Usage
Example with Ember Octane
// app/components/toggle.js
import { createMachine } from 'xstate';
export default createMachine({
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } },
},
});
Usage:
<Toggle as |state send|>
{{state.value}}
<button {{on 'click' (fn send 'TOGGLE')}}>
Toggle
</button>
</Toggle>
The default template for every createMachine(..)
is
{{yield this.state this.send}}
but that can be overriden to suit your needs by defining your own template.
The this
is an instance of the XState Interpreter
Accessing EmberJS Services
// app/components/authenticated-toggle.js
import { getService } from 'ember-statechart-component';
import { createMachine } from 'xstate';
export default createMachine({
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: [
{
target: 'active',
cond: 'isAuthenticated',
},
{ actions: ['notify'] },
],
},
},
active: { on: { TOGGLE: 'inactive' } },
},
}, {
actions: {
notify: (ctx) => {
getService(ctx, 'toasts').notify('You must be logged in');
},
},
guards: {
isAuthenticated: (ctx) => getService(ctx, 'session').isAuthenticated,
},
});
Usage:
<AuthenticatedToggle as |state send|>
{{state.value}}
<button {{on 'click' (fn send 'TOGGLE')}}>
Toggle
</button>
</AuthenticatedToggle>
Matching States
<Toggle as |state send|>
{{#if (state.matches 'inactive')}}
The inactive state
{{else if (state.matches 'active')}}
The active state
{{else}}
Unknown state
{{/if}}
<button {{on 'click' (fn send 'TOGGLE')}}>
Toggle
</button>
</Toggle>
Glint
Having type checking with these state machines requires a wrapper function.
// app/components/my-component.ts
import { createMachine } from 'xstate';
import { asComponent } from 'ember-statechart-component/glint';
export const machine = createMachine(/* ... */);
export default asComponent(machine);
or, if you want 0 runtime cost there is a more verbose, type-only option:
// app/components/my-component.ts
import { createMachine } from 'xstate';
import type { MachineComponent } from 'ember-statechart-component/glint';
export const machine = createMachine(/* ... */);
export default machine as unknown as MachineComponent<typeof machine>;
API
@config
This argument allows you to pass a MachineConfig for actions, services, guards, etc.
Usage:
// app/components/toggle.js
import { createMachine, assign } from 'xstate';
export default createMachine({
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: {
on: {
TOGGLE: {
target: 'inactive',
actions: ['toggleIsOn']
}
}
},
},
});
<Toggle
@config={{hash
actions=(hash
toggleIsOn=@onRoomIlluminated
)
}}
as |state send|>
<button {{on 'click' (fn send 'TOGGLE')}}>
Toggle
</button>
</Toggle>
@context
Sets the initial context. The current value of the context can then be accessed via state.context
.
Usage:
// app/components/toggle.js
import { createMachine, assign } from 'xstate';
export default createMachine({
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: {
target: 'active',
actions: ['increaseCounter']
}
}
},
active: {
on: {
TOGGLE: {
target: 'inactive',
actions: ['increaseCounter']
}
}
},
},
}, {
actions: {
increaseCounter: assign({
counter: (context) => context.counter + 1
})
}
});
<Toggle @context=(hash counter=0) as |state send|>
<button {{on 'click' (fn send 'TOGGLE')}}>
Toggle
</button>
<p>
Toggled: {{state.context.counter}} times.
</p>
</Toggle>
@state
The machine will use @state
as the initial state.
Any changes to this argument
are not automatically propagated to the machine.
An ARGS_UPDATE
event (see details below) is sent instead.
What happens if any of the passed args change?
An event will be sent to the machine for you, ARGS_UPDATE
, along
with all named arguments used to invoke the component.
Compatibility
- ember-source v3.28+
- typescript v4.4+
- ember-auto-import v2+
- A browser that supports Proxy
- Glint 0.8.3+
- Note that updates to glint support will not be covered by this library's adherance to SemVer. All glint-related updates will be bugfixes until Glint is declared stable.
Contributing
See the Contributing guide for details.
License
This project is licensed under the MIT License.
10 months ago
10 months ago
3 years ago
3 years ago
3 years ago
3 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