Skip to content

Commit 79094fc

Browse files
authored
Merge pull request #209 from icebob/grouping
Grouping fields
2 parents 5552079 + 2f29943 commit 79094fc

File tree

8 files changed

+194
-52
lines changed

8 files changed

+194
-52
lines changed

dev/grouping/app.vue

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<template lang="html">
2+
<form>
3+
<vue-form-generator :schema="schema" :model="model" :options="formOptions" tag="section"></vue-form-generator>
4+
<pre>{{ model }}</pre>
5+
</form>
6+
</template>
7+
8+
<script>
9+
import Vue from "vue";
10+
11+
export default {
12+
data () {
13+
return {
14+
model: {
15+
name: 'Brian Blessed',
16+
17+
others: {
18+
more: "More",
19+
things: "Things"
20+
},
21+
single: 'blah'
22+
},
23+
24+
schema: {
25+
groups:[{
26+
legend: "Contact Details",
27+
fields: [
28+
{
29+
type: "input",
30+
inputType: "text",
31+
label: "Name",
32+
model: "name"
33+
},
34+
{
35+
type: "input",
36+
inputType: "email",
37+
label: "Email",
38+
model: "email"
39+
}
40+
]
41+
},{
42+
legend: "Other Details",
43+
fields: [
44+
{
45+
type: "input",
46+
inputType: "text",
47+
label: "More",
48+
model: "others.more"
49+
},
50+
{
51+
type: "input",
52+
inputType: "text",
53+
label: "Things",
54+
model: "others.things"
55+
}
56+
]
57+
}],
58+
fields: [
59+
{
60+
type: "input",
61+
inputType: "text",
62+
label: "Single field (without group)",
63+
model: "single"
64+
}
65+
]
66+
},
67+
68+
formOptions: {
69+
fieldIdPrefix: 'frm1-'
70+
}
71+
}
72+
},
73+
74+
created() {
75+
window.app = this;
76+
}
77+
}
78+
</script>

dev/grouping/index.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>vue-form-generator multiple forms demo</title>
6+
</head>
7+
<body>
8+
<div class="container-fluid"></div>
9+
<div id="app"></div>
10+
<script src="/grouping.js"></script>
11+
</body>
12+
</html>

dev/grouping/main.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Vue from "vue";
2+
import VueFormGenerator from "../../src";
3+
Vue.use(VueFormGenerator);
4+
5+
import App from './app.vue';
6+
7+
new Vue({
8+
...App
9+
}).$mount('#app');

src/fields/abstractField.js

+4-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { get as objGet, each, isFunction, isString, isArray } from "lodash";
22
import validators from "../utils/validators";
3+
import { slugifyFormID } from "../utils/schema";
34

45
function convertValidator(validator) {
56
if (isString(validator)) {
@@ -17,6 +18,7 @@ export default {
1718
props: [
1819
"model",
1920
"schema",
21+
"formOptions",
2022
"disabled"
2123
],
2224

@@ -163,29 +165,8 @@ export default {
163165
},
164166

165167
getFieldID(schema) {
166-
// Try to get a reasonable default id from the schema,
167-
// then slugify it.
168-
if (typeof schema.id !== "undefined") {
169-
// If an ID's been explicitly set, use it unchanged
170-
return schema.id;
171-
} else {
172-
// Return the slugified version of either:
173-
return (schema.inputName || schema.label || schema.model)
174-
// NB: This is a very simple, conservative, slugify function,
175-
// avoiding extra dependencies.
176-
.toString()
177-
.trim()
178-
.toLowerCase()
179-
// Spaces & underscores to dashes
180-
.replace(/ |_/g, "-")
181-
// Multiple dashes to one
182-
.replace(/-{2,}/g, "-")
183-
// Remove leading & trailing dashes
184-
.replace(/^-+|-+$/g, "")
185-
// Remove anything that isn't a (English/ASCII) letter, number or dash.
186-
.replace(/([^a-zA-Z0-9-]+)/g, "")
187-
;
188-
}
168+
const idPrefix = this.formOptions && this.formOptions.fieldIdPrefix ? this.formOptions.fieldIdPrefix : "";
169+
return slugifyFormID(schema, idPrefix);
189170
}
190171

191172
}

src/formGenerator.vue

+39-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template lang="pug">
2-
div
3-
fieldset.vue-form-generator(v-if='schema != null', :is='tag')
2+
div.vue-form-generator(v-if='schema != null')
3+
fieldset(v-if="schema.fields", :is='tag')
44
template(v-for='field in fields')
55
.form-group(v-if='fieldVisible(field)', :class='getFieldRowClasses(field)')
66
label(v-if="fieldTypeHasLabel(field)", :for="getFieldID(field)")
@@ -9,18 +9,36 @@ div
99
i.icon
1010
.helpText(v-html='field.help')
1111
.field-wrap
12-
component(:is='getFieldType(field)', :disabled='fieldDisabled(field)', :model='model', :schema.sync='field', @model-updated='modelUpdated', @validated="onFieldValidated")
12+
component(:is='getFieldType(field)', :disabled='fieldDisabled(field)', :model='model', :schema='field', :formOptions='options', @model-updated='modelUpdated', @validated="onFieldValidated")
1313
.buttons(v-if='buttonVisibility(field)')
1414
button(v-for='btn in field.buttons', @click='buttonClickHandler(btn, field)', :class='btn.classes') {{ btn.label }}
1515
.hint(v-if='field.hint') {{ field.hint }}
1616
.errors.help-block(v-if='fieldErrors(field).length > 0')
1717
span(v-for='(error, index) in fieldErrors(field)', track-by='index') {{ error }}
18+
19+
template(v-for='group in groups')
20+
fieldset
21+
legend(v-if='group.legend') {{ group.legend }}
22+
template(v-for='field in group.fields')
23+
.form-group(v-if='fieldVisible(field)', :class='getFieldRowClasses(field)')
24+
label(v-if="fieldTypeHasLabel(field)", :for="getFieldID(field)")
25+
| {{ field.label }}
26+
span.help(v-if='field.help')
27+
i.icon
28+
.helpText(v-html='field.help')
29+
.field-wrap
30+
component(:is='getFieldType(field)', :disabled='fieldDisabled(field)', :model='model', :schema='field', :formOptions='options',@model-updated='modelUpdated', @validated="onFieldValidated")
31+
.buttons(v-if='buttonVisibility(field)')
32+
button(v-for='btn in field.buttons', @click='buttonClickHandler(btn, field)', :class='btn.classes') {{ btn.label }}
33+
.hint(v-if='field.hint') {{ field.hint }}
34+
.errors.help-block(v-if='fieldErrors(field).length > 0')
35+
span(v-for='(error, index) in fieldErrors(field)', track-by='index') {{ error }}
1836
</template>
1937

2038
<script>
2139
// import Vue from "vue";
2240
import {each, isFunction, isNil, isArray, isString} from "lodash";
23-
import getFieldID from "./fields/abstractField";
41+
import { slugifyFormID } from "./utils/schema";
2442
2543
// Load all fields from '../fields' folder
2644
let fieldComponents = {};
@@ -46,8 +64,6 @@ div
4664
export default {
4765
components: fieldComponents,
4866
49-
mixins: [ getFieldID ],
50-
5167
props: {
5268
schema: Object,
5369
@@ -93,13 +109,23 @@ div
93109
computed: {
94110
fields() {
95111
let res = [];
96-
if (this.schema) {
112+
if (this.schema && this.schema.fields) {
97113
each(this.schema.fields, (field) => {
98114
if (!this.multiple || field.multi === true)
99115
res.push(field);
100116
});
101117
}
102118
119+
return res;
120+
},
121+
groups() {
122+
let res = [];
123+
if (this.schema && this.schema.groups) {
124+
each(this.schema.groups, (group) => {
125+
res.push(group);
126+
});
127+
}
128+
103129
return res;
104130
}
105131
},
@@ -315,7 +341,12 @@ div
315341
fieldErrors(field) {
316342
let res = this.errors.filter(e => e.field == field);
317343
return res.map(item => item.error);
318-
}
344+
},
345+
346+
getFieldID(schema) {
347+
const idPrefix = this.options && this.options.fieldIdPrefix ? this.options.fieldIdPrefix : "";
348+
return slugifyFormID(schema, idPrefix);
349+
}
319350
}
320351
};
321352

src/utils/schema.js

+26
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,30 @@ module.exports.mergeMultiObjectFields = function(schema, objs) {
5252
});
5353

5454
return model;
55+
};
56+
57+
module.exports.slugifyFormID = function(schema, prefix = "") {
58+
// Try to get a reasonable default id from the schema,
59+
// then slugify it.
60+
if (typeof schema.id !== "undefined") {
61+
// If an ID's been explicitly set, use it unchanged
62+
return prefix + schema.id;
63+
} else {
64+
// Return the slugified version of either:
65+
return prefix + (schema.inputName || schema.label || schema.model)
66+
// NB: This is a very simple, conservative, slugify function,
67+
// avoiding extra dependencies.
68+
.toString()
69+
.trim()
70+
.toLowerCase()
71+
// Spaces & underscores to dashes
72+
.replace(/ |_/g, "-")
73+
// Multiple dashes to one
74+
.replace(/-{2,}/g, "-")
75+
// Remove leading & trailing dashes
76+
.replace(/^-+|-+$/g, "")
77+
// Remove anything that isn't a (English/ASCII) letter, number or dash.
78+
.replace(/([^a-zA-Z0-9-]+)/g, "")
79+
;
80+
}
5581
};

test/unit/specs/VueFormGenerator.spec.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ function createFormGenerator(schema = {}, model = null, options, multiple) {
3535
describe("VueFormGenerator.vue", () => {
3636

3737
describe("with empty schema", () => {
38-
let schema = {};
38+
let schema = {
39+
fields: []
40+
};
3941

4042
beforeEach( () => {
4143
createFormGenerator(schema);
@@ -49,13 +51,15 @@ describe("VueFormGenerator.vue", () => {
4951
});
5052

5153
describe("with empty schema and custom tag", () => {
52-
let schema = {};
54+
let schema = {
55+
fields: []
56+
};
5357

5458
beforeEach( () => {
5559
let elm = document.createElement("div");
5660
vm = new Vue({
5761
// eslint-disable-next-line quotes
58-
template: `<vue-form-generator :schema="schema" ref="form" tag="div"></vue-form-generator>`,
62+
template: `<vue-form-generator :schema="schema" ref="form" tag="section"></vue-form-generator>`,
5963
data: {
6064
schema
6165
}
@@ -66,9 +70,9 @@ describe("VueFormGenerator.vue", () => {
6670
return [el, vm];
6771
});
6872

69-
it("should be create fieldset", () => {
73+
it("should be create custom tag", () => {
7074
expect(vm.$el).to.be.exist;
71-
expect(el.getElementsByTagName("div")).to.be.length(1);
75+
expect(el.getElementsByTagName("section")).to.be.length(1);
7276
});
7377

7478
});

webpack.dev.config.js

+17-16
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,24 @@ var loaders = [
1717
test: /\.json$/,
1818
loader: 'json'
1919
},
20-
{
21-
test: /\.(woff2?|svg)$/,
22-
loader: "url"
23-
//loader: "url?limit=10000"
20+
{
21+
test: /\.(woff2?|svg)$/,
22+
loader: "url"
23+
//loader: "url?limit=10000"
2424
},
25-
{
26-
test: /\.(ttf|eot)$/,
27-
loader: "url"
25+
{
26+
test: /\.(ttf|eot)$/,
27+
loader: "url"
2828
}
2929
];
3030

3131
module.exports = {
3232
devtool: "source-map",
33-
33+
3434
entry: {
3535
full: path.resolve("dev", "full", "main.js"),
3636
mselect: path.resolve("dev", "multiselect", "main.js"),
37+
grouping: path.resolve("dev", "grouping", "main.js"),
3738
checklist: path.resolve("dev", "checklist", "main.js")
3839
},
3940

@@ -43,14 +44,14 @@ module.exports = {
4344
publicPath: "/"
4445
},
4546

46-
plugins: [
47-
new webpack.DefinePlugin({
48-
"process.env": {
49-
NODE_ENV: JSON.stringify("development"),
50-
FULL_BUNDLE: true
51-
}
52-
}),
53-
],
47+
plugins: [
48+
new webpack.DefinePlugin({
49+
"process.env": {
50+
NODE_ENV: JSON.stringify("development"),
51+
FULL_BUNDLE: true
52+
}
53+
}),
54+
],
5455

5556
module: {
5657
loaders

0 commit comments

Comments
 (0)