Skip to content

Commit 1964b41

Browse files
committed
implemented nested form support, specs and docs missing
1 parent 4824176 commit 1964b41

File tree

13 files changed

+150
-8
lines changed

13 files changed

+150
-8
lines changed

lib/matestack/ui/core.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ module VueJs
4949
require "#{vue_js_base_path}/components/form/checkbox"
5050
require "#{vue_js_base_path}/components/form/radio"
5151
require "#{vue_js_base_path}/components/form/select"
52+
require "#{vue_js_base_path}/components/form/fields_for_remove_item"
5253
require "#{vue_js_base_path}/components/collection/helper"
5354
require "#{vue_js_base_path}/components/collection/content"
5455
require "#{vue_js_base_path}/components/collection/filter"

lib/matestack/ui/vue_js/components.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ def matestack_form(text=nil, options=nil, &block)
3131
Matestack::Ui::VueJs::Components::Form::Form.(text, options, &block)
3232
end
3333

34+
def form_fields_for(text=nil, options=nil, &block)
35+
# in order to provide a more intuitiv API while calling the default
36+
# form, we transform the arguments a bit:
37+
options[:for] = text
38+
options[:fields_for] = options.delete(:key)
39+
text = nil
40+
Matestack::Ui::VueJs::Components::Form::Form.(text, options, &block)
41+
end
42+
43+
def form_fields_for_remove_item(text=nil, options=nil, &block)
44+
Matestack::Ui::VueJs::Components::Form::FieldsForRemoveItem.(text, options, &block)
45+
end
46+
3447
def form_input(text=nil, options=nil, &block)
3548
Matestack::Ui::VueJs::Components::Form::Input.(text, options, &block)
3649
end

lib/matestack/ui/vue_js/components/form/base.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def v_model_type(item=nil)
9595
item.is_a?(Integer) ? 'v-model.number' : 'v-model'
9696
end
9797
end
98-
98+
9999
# set value-type "Integer" for all numeric init values or options
100100
def value_type(item=nil)
101101
if item.nil?
@@ -164,4 +164,4 @@ def render_errors
164164
end
165165
end
166166
end
167-
end
167+
end

lib/matestack/ui/vue_js/components/form/checkbox_mixin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ const formCheckboxMixin = {
6464
}, 1);
6565
},
6666
inputChanged: function (key) {
67+
if (this.$parent.isNestedForm){
68+
this.$parent.data["_destroy"] = false;
69+
}
6770
this.$parent.resetErrors(key);
6871
this.$parent.$forceUpdate();
6972
this.$forceUpdate();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module Matestack
2+
module Ui
3+
module VueJs
4+
module Components
5+
module Form
6+
class FieldsForRemoveItem < Matestack::Ui::Component
7+
8+
def response
9+
a class: 'matestack-ui-core-form-fields-for-remove-item', "@click.prevent": "removeItem()" do
10+
yield if block_given?
11+
end
12+
end
13+
14+
end
15+
end
16+
end
17+
end
18+
end
19+
end

lib/matestack/ui/vue_js/components/form/form.js

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ const componentDef = {
1111
return {
1212
data: {},
1313
errors: {},
14-
loading: false
14+
nestedForms: {},
15+
loading: false,
16+
isNestedForm: false,
17+
hideNestedForm: false,
18+
positionInNestedForm: 0
1519
};
1620
},
1721
methods: {
@@ -54,6 +58,27 @@ const componentDef = {
5458
}
5559
}
5660
},
61+
setErrors: function(errors){
62+
this.errors = errors;
63+
},
64+
setErrorKey: function(key, value){
65+
Vue.set(this.errors, key, value);
66+
},
67+
setNestedFormsError: function(errors){
68+
let self = this;
69+
Object.keys(errors).forEach(function(errorKey){
70+
if (errorKey.includes(".")){
71+
let childErrorKey = errorKey.split(".")[1]
72+
let childModelName = errorKey.split(".")[0].split("[")[0]
73+
let childModelIndex = errorKey.split(".")[0].split("[")[1].split("]")[0]
74+
self.nestedForms[childModelName+"_attributes"][childModelIndex].setErrorKey(childErrorKey, errors[errorKey])
75+
}
76+
})
77+
},
78+
removeItem: function(){
79+
Vue.set(this.data, "_destroy", true)
80+
this.hideNestedForm = true;
81+
},
5782
initValues: function () {
5883
let self = this;
5984
let data = {};
@@ -73,6 +98,34 @@ const componentDef = {
7398
if (key.startsWith("checkbox-component")) {
7499
self.$refs[key].initialize()
75100
}
101+
if (key.startsWith("matestack-form-fields-for")) {
102+
self.$refs[key].initializeNestedForm()
103+
}
104+
}
105+
},
106+
initializeNestedForm(){
107+
const self = this;
108+
self.isNestedForm = true;
109+
if (this.props["fields_for"] != undefined) {
110+
this.data = {}
111+
this.initValues()
112+
if(this.$parent.data[this.props["fields_for"]] == undefined){
113+
this.$parent.data[this.props["fields_for"]] = [];
114+
}
115+
this.$parent.data[this.props["fields_for"]].push(this.data);
116+
117+
if(this.$parent.nestedForms[this.props["fields_for"]] == undefined){
118+
this.$parent.nestedForms[this.props["fields_for"]] = [];
119+
}
120+
let existingItemsCount = this.$parent.nestedForms[this.props["fields_for"]].length
121+
this.$parent.nestedForms[this.props["fields_for"]].push(this);
122+
this.positionInNestedForm = existingItemsCount
123+
124+
//without the timeout it's somehow not working
125+
setTimeout(function () {
126+
self.$parent.$forceUpdate();
127+
self.$forceUpdate();
128+
}, 1);
76129
}
77130
},
78131
shouldResetFormOnSuccessfulSubmit() {
@@ -93,6 +146,9 @@ const componentDef = {
93146
},
94147
perform: function(){
95148
const self = this
149+
if (self.props["fields_for"] != null) {
150+
return;
151+
}
96152
var form = self.$el.tagName == 'FORM' ? self.$el : self.$el.querySelector('form');
97153
if(form.checkValidity()){
98154
self.loading = true;
@@ -211,7 +267,9 @@ const componentDef = {
211267
.catch(function (error) {
212268
self.loading = false;
213269
if (error.response && error.response.data && error.response.data.errors) {
214-
self.errors = error.response.data.errors;
270+
// self.errors = error.response.data.errors;
271+
self.setErrors(error.response.data.errors);
272+
self.setNestedFormsError(error.response.data.errors);
215273
}
216274
if (self.props["failure"] != undefined && self.props["failure"]["emit"] != undefined) {
217275
matestackEventHub.$emit(self.props["failure"]["emit"], error.response.data);

lib/matestack/ui/vue_js/components/form/form.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class Form < Matestack::Ui::VueJs::Vue
77
vue_name 'matestack-ui-core-form'
88

99
optional :for, :path, :success, :failure, :multipart, :emit, :delay, :errors
10+
optional :fields_for, :reject_blank
1011

1112
# setup form context to allow child components like inputs to access the form configuration
1213
def initialize(html_tag = nil, text = nil, options = {}, &block)
@@ -16,9 +17,21 @@ def initialize(html_tag = nil, text = nil, options = {}, &block)
1617
Matestack::Ui::VueJs::Components::Form::Context.form_context = previous_form_context
1718
end
1819

20+
def component_id
21+
"matestack-form-fields-for-#{context.fields_for}-#{SecureRandom.hex}" if context.fields_for
22+
end
23+
1924
def response
20-
form attributes do
21-
yield
25+
if context.fields_for
26+
div class: "matestack-form-fields-for", "v-show": "hideNestedForm != true" do
27+
form_input key: context.for&.class&.primary_key, type: :hidden # required for existing model mapping
28+
form_input key: :_destroy, type: :hidden, init: true if context.reject_blank == true
29+
yield
30+
end
31+
else
32+
form attributes do
33+
yield
34+
end
2235
end
2336
end
2437

@@ -40,6 +53,8 @@ def vue_props
4053
multipart: !!ctx.multipart,
4154
emit: ctx.emit,
4255
delay: ctx.delay,
56+
fields_for: ctx.fields_for,
57+
new_instance_on_demand: ctx.new_instance_on_demand
4358
}
4459
end
4560

@@ -61,4 +76,4 @@ def form_method
6176
end
6277
end
6378
end
64-
end
79+
end

lib/matestack/ui/vue_js/components/form/input_mixin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ const formInputMixin = {
3939
}
4040
},
4141
inputChanged: function (key) {
42+
if (this.$parent.isNestedForm){
43+
this.$parent.data["_destroy"] = false;
44+
}
4245
this.$parent.resetErrors(key);
4346
this.$parent.$forceUpdate();
4447
this.$forceUpdate();

lib/matestack/ui/vue_js/components/form/radio_mixin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ const formRadioMixin = {
4646
}, 1);
4747
},
4848
inputChanged: function (key) {
49+
if (this.$parent.isNestedForm){
50+
this.$parent.data["_destroy"] = false;
51+
}
4952
this.$parent.resetErrors(key);
5053
this.$parent.$forceUpdate();
5154
this.$forceUpdate();

lib/matestack/ui/vue_js/components/form/select_mixin.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ const formSelectMixin = {
4242
}, 1);
4343
},
4444
inputChanged: function (key) {
45+
if (this.$parent.isNestedForm){
46+
this.$parent.data["_destroy"] = false;
47+
}
4548
this.$parent.resetErrors(key);
4649
this.$parent.$forceUpdate();
4750
this.$forceUpdate();

0 commit comments

Comments
 (0)