Skip to content

Create a custom form builder to help convert forms to Vue

Peter Hegman requested to merge peterhegman/create-vue-form-builder into master

What does this MR do?

Use case for this change

You have a HAML form that needs some extra functionality added to it. This could be a datepicker, a dropdown that loads projects or groups, etc. Instead of using older jQuery plugins (such as select2 or pikaday) it is better to use Vue. The tricky thing is Rails uses form helpers to generate the form inputs with a name attribute that matches the backend. There is no way to generate the name attribute without actually generating the form input. This MR creates a custom form builder that exposes a field_attributes method. This method returns { name: 'user[email]', id: 'user_email', value: 'foo@bar.com' } which can then be passed to the Vue application via a data attribute. This makes it easier to convert a form from HAML to Vue.

Based on rails documentation here: https://guides.rubyonrails.org/form_helpers.html#customizing-form-builders

Example usage

form.html.haml

= form_for user, builder: FormBuilders::VueFormBuilder do |form|
  .js-user-form{ data: { fields: { email: form.field_attributes(:email) }.to_json } }

index.js

import Vue from 'vue';
import UserForm from './components/user_form.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';

export const initUserForm = () => {
  const el = document.querySelector('.js-user-form');

  if (!el) {
    return null;
  }

  const { fields = {} } = el.dataset;

  return new Vue({
    el,
    render(h) {
      return h(UserForm, {
        props: {
          fields: convertObjectPropsToCamelCase(JSON.parse(fields)),
        },
      });
    },
  });
};

user_form.vue

<script>
import { GlFormGroup, GlFormInput, GlButton } from '@gitlab/ui';

export default {
  name: 'UserForm',
  components: { GlFormGroup, GlFormInput, GlButton },
  props: {
    fields: {
      type: Object,
      required: true,
    },
  },
  methods: {
    fieldsetId(id) {
      // eslint-disable-next-line @gitlab/require-i18n-strings
      return `${id}-fieldset`;
    },
  },
};
</script>

<template>
  <div>
    <gl-form-group :id="fieldsetId(fields.email.id)" :label-for="fields.email.id" :label="__('Email')">
      <gl-form-input
        :id="fields.email.id"
        :name="fields.email.name"
        :value="fields.email.value"
        type="email"
        size="lg"
      />
    </gl-form-group>

    <gl-button
      type="submit"
      category="primary"
      variant="confirm"
      >{{ __('Update') }}</gl-button
    >
  </div>
</template>

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • Label as security and @ mention @gitlab-com/gl-security/appsec
  • The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • Security reports checked/validated by a reviewer from the AppSec team
Edited by Peter Hegman

Merge request reports

Loading