Create a custom form builder to help convert forms to Vue
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
- [-] Changelog entry
- Not needed, developer facing change
-
Documentation (if required) - Would consider adding this to the developer docs once we nail down the implementation
-
Code review guidelines -
Merge request performance guidelines -
Style guides - [-] Database guides
-
Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process. - [-] Tested in all supported browsers
- [-] Informed Infrastructure department of a default or new setting change, if applicable per definition of done
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