# Storybooking Custom ControlValueAccessor Form Controls

Sometimes it is really neat to implement custom form controls by implementing the ControlValueAccessor interface.

We do wanna be able to develop and test them in isolation and there is some small amount of setup needed to make them play along with Storybook. This is really about making a proper environment for these controls when they are spun up in isolation by storybook.

Make sure storybook knows about FormsModule. In your <something>.component.stories.ts file

// custom-form-control.component.stories.ts

export default {
  title: 'CustomFormControlComponent',
  component: CustomFormControlComponent,
  decorators: [
    moduleMetadata({
      imports: [FormsModule], 👈
    })
  ],
} as Meta<CustomFormControlComponent>;

We also need to tell storybook about ngModelChange. Add ngModel and ngModelChange to props. We keep previous args by spreading them.

// custom-form-control.component.stories.ts

const Template: Story<CustomFormControlComponent> = (args: CustomFormControlComponent) => ({
    component: CustomFormControlComponent,
    props: {
        ...args, 👈
        ngModel: 'Type anything', 👈
        ngModelChange: action('ngModelChange') 👈
    },
})

If addon-actions is not in your package.json already, install the package as a dev dependency.

npm install -D @storybook/addon-actions

And break the habit of default undefined implementations of onTouched and onChange in your component so it may have a life outside an Angular form. Also, please consider having a typed value instead of any. This is just an example.

  // 😬 stop this nonsense
  onTouched!: () => void;
  onChange!: (value: any) => void;
  
  // 😇 provide an empty default implementation instead
  onChange = (value: any) => {};
  onTouched = () => {};
Last updated: April 15th 2024