ember-statechart-component v6.1.2
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.
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
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
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
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago