store icon indicating copy to clipboard operation
store copied to clipboard

Action scaffolder

Open zewa666 opened this issue 6 years ago • 9 comments

One of the things that eats up a lot of time is the creation of action stubs for new entites.

Imagine your state is like this:

interface Item {
  name: string;
}

interface State {
  items: Item[];
}

now in order to have the most common CRUD - R operations you'd need three actions. What if instead, aurelia store would also act as a cli at the same time, helping you creating those action stubs on the fly.

You can try out a basic sample by installing:

$ npm install git://github.com/aurelia/store.git#action-scaffold

$ aurelia-store entityName entityType entityList stateType idName

I'd be interested to hear what you think about it and what a nice collection of actions boilerplates could be.

EDIT: Make sure to visit this discourse topic for further discussions

zewa666 avatar Oct 27 '18 19:10 zewa666

This sounds awesome. You're right about the time consuming nature of new stubs for actions. I really like this feature.

Vheissu avatar Oct 27 '18 20:10 Vheissu

I ended up with some action generator like this for my own project.

khuongduybui avatar Oct 28 '18 21:10 khuongduybui

@khuongduybui oh wow I'm glad somebody else thought about this already as well. Can you share some more insights in what you're generating, or the overall process?

What I'd be looking in the final end is the following:

  • Templates for both JS and TS
  • A set of minimal base scaffolds (e.g crud for lists)
  • Easily extendable with custom templates (+ registering custom attributes for the CLI)
  • Extensibility via other npm packages (an interface where they can easily tie into the CLI) for community work
  • A VSCode extension providing aurelia-store snippets for actions (might fit https://github.com/aurelia/vscode-extension)

zewa666 avatar Oct 29 '18 06:10 zewa666

My situation is a little different, so I didn't elaborate. I use https://feathersjs.com/, so my "store" is a map of models.

{
  "user": Object,
  "users": [Object],
  "projects": [Object],
  [...]
}

I have a factory to "connect" a Feathers model with the part of the store.

The factory creates simple setFoos action that replace state.foos with the new provided foos, a setFooAt action that replaces foo with a specific id inside state.foos, and a removeFooAt action that takes foo with a specific id from state.foos.

The factory also creates listeners for Feathers model CRUD events and invokes the actions as appropriate.

My code, besides the listeners, never touches my actions directly (except for my login / logout code that use the actions for state.user). I call feathers to update my models directly in the database, then wait until feathers events to go back to the listeners that updates my Aurelia store.

khuongduybui avatar Oct 30 '18 15:10 khuongduybui

OK I see, cool idea actually to automate that part

zewa666 avatar Oct 30 '18 21:10 zewa666

I like this idea. Scaffolding helps with boiler plate code exhaustion and is great for speedy development, especially with crud-like apps where you find yourself doing the same thing over and over, just in a different app.

I work with a lot of forms and I have used https://github.com/erikras/redux-form with aurelia (hacking out the react parts), but more recently I used https://github.com/final-form/final-form with aurelia to handle all my crud behavior since it is framework agnostic. The Redux-Form library might give you some more boilerplate action ideas.

This may be a little off topic, but I would be interested in an extension/plugin to the store that handles CRUD changes, maybe as a long term option in addition to scaffolding. I am in the early stages of my first application with this library and am trying to solve this issue by separating this type of logic (push, slice, change etc.) into its own feature (custom element/custom attribute) in hopes that the feature could be used as a plugin, although right now it is dependent on final-form because it handles a lot of boiler plate events 😃

Update: FYI, a little easier to navigate docs to see their actions if you are interested: https://redux-form.com/7.4.2/docs/api/actioncreators.md/

jmzagorski avatar Oct 31 '18 18:10 jmzagorski

@jmzagorski I would love to know more about your form implementation. The two way binding and reference problem in Javascript has meant I've seen numerous solutions for working with forms and sometimes issues have arisen because someone directly bound to a reference to a property from the subscriber and mutated the data.

I know the documentation makes mention of this and to shallow clone, but I would love to see an official Aurelia plugin or way of working with forms. Understandably, this is an issue that plagues all state management solutions and working with form binding (especially given Aurelia is two way binding by default for form value binding).

Vheissu avatar Nov 02 '18 10:11 Vheissu

@Vheissu In my last application I had to deal with a ton of forms and the redux-form library approach worked pretty well. I created one custom element that had a form element and listened for all the children element change events.

<template class="redux-form">
  <form change.delegate="changeHandler($event)" ref="$form">
    <slot></slot>
  </form>
</template>

This custom element emitted a redux change action in response to the change event and that action had the form name, field name from the element emitting the event and the new value. I had to do extra work to convert the string value type if the change event was from the DOM and not my custom event by inspecting the element ( e.g.<input type="number">). With this approach you have to be disciplined to use one-way and have the right attributes or element to identify the value type (e.g number, boolean etc)

My strategy this time around is a bit different since I am introducing a custom attribute and using final-form. final-form will handled an inner form state separate from the users actual state and the user can bind to the changing value(s) or subscribe to events to emit actions and change their state. To handle the bind issue I can

  1. Either throw an error in the custom attribute when the user does not use one-way binding
  2. Or have the client pass the value from their state's property to my custom attribute @bindable prop instead of using aurelia value.bind, checked.bind etc.

Still thinking this through.

jmzagorski avatar Nov 05 '18 03:11 jmzagorski

I was lately working a lot with Gherkin/Cucumber and was diving into scaffolding associated unit tests. Thats what brought me to the idea of scaffolding actions. But this is just one way to tackle forms. Having a dedicated approach via a subplugin or even part of the store would definitely make sense as it would go beyond the scope of creating actions.

zewa666 avatar Nov 05 '18 06:11 zewa666