matestack-ui-core 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/lib/matestack/ui/core.rb +2 -0
- data/lib/matestack/ui/core/base.rb +8 -0
- data/lib/matestack/ui/core/page.rb +0 -6
- data/lib/matestack/ui/core/version.rb +1 -1
- data/lib/matestack/ui/vue_js/components.rb +17 -0
- data/lib/matestack/ui/vue_js/components/collection/filter.js +4 -3
- data/lib/matestack/ui/vue_js/components/form/base.rb +9 -5
- data/lib/matestack/ui/vue_js/components/form/checkbox.rb +8 -8
- data/lib/matestack/ui/vue_js/components/form/checkbox_mixin.js +15 -28
- data/lib/matestack/ui/vue_js/components/form/fields_for_add_item.rb +35 -0
- data/lib/matestack/ui/vue_js/components/form/fields_for_remove_item.rb +19 -0
- data/lib/matestack/ui/vue_js/components/form/form.js +232 -33
- data/lib/matestack/ui/vue_js/components/form/form.rb +24 -4
- data/lib/matestack/ui/vue_js/components/form/input.rb +7 -2
- data/lib/matestack/ui/vue_js/components/form/input_mixin.js +9 -14
- data/lib/matestack/ui/vue_js/components/form/radio.rb +5 -5
- data/lib/matestack/ui/vue_js/components/form/radio_mixin.js +11 -21
- data/lib/matestack/ui/vue_js/components/form/select.rb +4 -4
- data/lib/matestack/ui/vue_js/components/form/select_mixin.js +11 -17
- data/lib/matestack/ui/vue_js/components/form/textarea.rb +2 -2
- data/lib/matestack/ui/vue_js/components/form/textarea_mixin.js +7 -14
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86a4a5afb13f1c3a954fe71db9220e734654eb451c329858d3f890ee9db148e4
|
4
|
+
data.tar.gz: 1035ccf0e2f972d9ad916bbd681059713a8e5c47dfee992d0ad1eefab453f345
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 362ded6f509688c84767a98241d7c5f5ff6d4d102196a42fee213b421e966c05ac229d0f0ed732c1939135c79da816dcc07f262502d1fc409e8808a53817cdf9
|
7
|
+
data.tar.gz: 4eb1ec667b7efa39e44fc7a853f898d45db1b2d9d2060a2900a24a5aa738f16fd4a20e225c5bb8c1c7c2cb5717596fa11845be82b2581c151a28048356b52c72
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
# matestack-ui-core | UI in pure Ruby
|
10
10
|
|
11
11
|
----
|
12
|
-
Version 2.0.0 was released on the 12th of April and proudly presented at RailsConf.
|
12
|
+
Version 2.0.0 was released on the 12th of April and proudly presented at RailsConf.
|
13
13
|
|
14
14
|
Most important changes:
|
15
15
|
|
@@ -19,6 +19,8 @@ Most important changes:
|
|
19
19
|
- Improved core code readability/maintainability
|
20
20
|
----
|
21
21
|
|
22
|
+
[<img src="https://img.youtube.com/vi/bwsVgCb97v0/0.jpg" width="350">](https://www.youtube.com/watch?v=bwsVgCb97v0)
|
23
|
+
|
22
24
|
Boost your productivity & easily create component based web UIs in pure Ruby.
|
23
25
|
Reactivity included if desired.
|
24
26
|
|
@@ -26,8 +28,6 @@ Reactivity included if desired.
|
|
26
28
|
|
27
29
|
You end up writing 50% less code while increasing productivity, maintainability and developer happiness. Work with pure Ruby. If necessary, extend with pure JavaScript. No Opal involved.
|
28
30
|
|
29
|
-
[<img src="https://img.youtube.com/vi/Mue5gs6Wtq4/0.jpg" width="350">](https://www.youtube.com/watch?v=Mue5gs6Wtq4)
|
30
|
-
|
31
31
|
The main goals are:
|
32
32
|
|
33
33
|
- More maintainable UI code, using a component-based structure written in Ruby
|
@@ -66,7 +66,7 @@ Documentation can be found [here](https://docs.matestack.io)
|
|
66
66
|
|
67
67
|
## Getting started
|
68
68
|
|
69
|
-
A getting started guide can be found [here](https://docs.matestack.io/getting-started/
|
69
|
+
A getting started guide can be found [here](https://docs.matestack.io/getting-started/quick-start)
|
70
70
|
|
71
71
|
## Changelog
|
72
72
|
|
data/lib/matestack/ui/core.rb
CHANGED
@@ -49,6 +49,8 @@ require "#{vue_js_base_path}/components/form/textarea"
|
|
49
49
|
require "#{vue_js_base_path}/components/form/checkbox"
|
50
50
|
require "#{vue_js_base_path}/components/form/radio"
|
51
51
|
require "#{vue_js_base_path}/components/form/select"
|
52
|
+
require "#{vue_js_base_path}/components/form/fields_for_remove_item"
|
53
|
+
require "#{vue_js_base_path}/components/form/fields_for_add_item"
|
52
54
|
require "#{vue_js_base_path}/components/collection/helper"
|
53
55
|
require "#{vue_js_base_path}/components/collection/content"
|
54
56
|
require "#{vue_js_base_path}/components/collection/filter"
|
@@ -12,6 +12,8 @@ module Matestack
|
|
12
12
|
attr_accessor :html_tag, :text, :options, :parent, :escape, :bind_to_parent
|
13
13
|
|
14
14
|
def initialize(html_tag = nil, text = nil, options = {}, &block)
|
15
|
+
return unless render?
|
16
|
+
|
15
17
|
self.bind_to_parent = ([:without_parent].include?(html_tag) ? false : true)
|
16
18
|
self.slots = self.options.delete(:slots) if self.options
|
17
19
|
# extract_options(text, options) is called in properties
|
@@ -26,6 +28,12 @@ module Matestack
|
|
26
28
|
self
|
27
29
|
end
|
28
30
|
|
31
|
+
# can be optionally overwritten in subclass
|
32
|
+
# in order to conditionally render the component
|
33
|
+
def render?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
29
37
|
# check if text is given and set text and options accordingly
|
30
38
|
def extract_options(text, options)
|
31
39
|
if text.is_a? Hash
|
@@ -46,13 +46,7 @@ module Matestack
|
|
46
46
|
def component_attributes
|
47
47
|
{
|
48
48
|
is: 'matestack-ui-core-page-content',
|
49
|
-
ref: 'some-id',
|
50
49
|
':params': params.to_json,
|
51
|
-
':component-config': {
|
52
|
-
#TODO Remove?!
|
53
|
-
show_on: 'a-event',
|
54
|
-
hide_on: 'test'
|
55
|
-
}.to_json,
|
56
50
|
'inline-template': true
|
57
51
|
}
|
58
52
|
end
|
@@ -31,6 +31,23 @@ module Matestack
|
|
31
31
|
Matestack::Ui::VueJs::Components::Form::Form.(text, options, &block)
|
32
32
|
end
|
33
33
|
|
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
|
+
|
47
|
+
def form_fields_for_add_item(text=nil, options=nil, &block)
|
48
|
+
Matestack::Ui::VueJs::Components::Form::FieldsForAddItem.(text, options, &block)
|
49
|
+
end
|
50
|
+
|
34
51
|
def form_input(text=nil, options=nil, &block)
|
35
52
|
Matestack::Ui::VueJs::Components::Form::Input.(text, options, &block)
|
36
53
|
end
|
@@ -11,7 +11,9 @@ const componentDef = {
|
|
11
11
|
var url;
|
12
12
|
var filter = this.data
|
13
13
|
for (var key in this.data) {
|
14
|
-
|
14
|
+
if(this.data[key] != null){
|
15
|
+
url = queryParamsHelper.updateQueryParams(this.props["id"] + "-filter-" + key, JSON.stringify(this.data[key]), url)
|
16
|
+
}
|
15
17
|
}
|
16
18
|
url = queryParamsHelper.updateQueryParams(this.props["id"] + "-offset", 0, url)
|
17
19
|
window.history.pushState({matestackApp: true, url: url}, null, url);
|
@@ -22,7 +24,6 @@ const componentDef = {
|
|
22
24
|
for (var key in this.data) {
|
23
25
|
url = queryParamsHelper.updateQueryParams(this.props["id"] + "-filter-" + key, null, url)
|
24
26
|
this.data[key] = null;
|
25
|
-
this.$forceUpdate();
|
26
27
|
}
|
27
28
|
this.initValues();
|
28
29
|
window.history.pushState({matestackApp: true, url: url}, null, url);
|
@@ -34,7 +35,7 @@ const componentDef = {
|
|
34
35
|
var queryParamsObject = queryParamsHelper.queryParamsToObject()
|
35
36
|
Object.keys(queryParamsObject).forEach(function(key){
|
36
37
|
if (key.startsWith(self.props["id"] + "-filter-")){
|
37
|
-
self.data[key.replace(self.props["id"] + "-filter-", "")] = queryParamsObject[key]
|
38
|
+
self.data[key.replace(self.props["id"] + "-filter-", "")] = JSON.parse(queryParamsObject[key])
|
38
39
|
}
|
39
40
|
})
|
40
41
|
}
|
@@ -34,7 +34,11 @@ module Matestack
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def id
|
37
|
-
ctx.id
|
37
|
+
if ctx.id.present?
|
38
|
+
"'#{ctx.id}'"
|
39
|
+
else
|
40
|
+
"'#{key}'+$parent.nestedFormRuntimeId"
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
def multiple
|
@@ -50,12 +54,12 @@ module Matestack
|
|
50
54
|
def attributes
|
51
55
|
(options || {}).merge({
|
52
56
|
ref: "input.#{attribute_key}",
|
53
|
-
id: id,
|
57
|
+
":id": id,
|
54
58
|
type: ctx.type,
|
55
59
|
multiple: ctx.multiple,
|
56
60
|
placeholder: ctx.placeholder,
|
57
61
|
'@change': change_event,
|
58
|
-
'init-value': init_value
|
62
|
+
'init-value': init_value,
|
59
63
|
'v-bind:class': "{ '#{input_error_class}': #{error_key} }",
|
60
64
|
}).tap do |attrs|
|
61
65
|
attrs[:"#{v_model_type}"] = input_key unless type == :file
|
@@ -95,7 +99,7 @@ module Matestack
|
|
95
99
|
item.is_a?(Integer) ? 'v-model.number' : 'v-model'
|
96
100
|
end
|
97
101
|
end
|
98
|
-
|
102
|
+
|
99
103
|
# set value-type "Integer" for all numeric init values or options
|
100
104
|
def value_type(item=nil)
|
101
105
|
if item.nil?
|
@@ -164,4 +168,4 @@ module Matestack
|
|
164
168
|
end
|
165
169
|
end
|
166
170
|
end
|
167
|
-
end
|
171
|
+
end
|
@@ -37,13 +37,13 @@ module Matestack
|
|
37
37
|
def render_checkbox_options
|
38
38
|
checkbox_options.to_a.each do |item|
|
39
39
|
input checkbox_attributes(item)
|
40
|
-
label item_label(item), for: item_id(item)
|
40
|
+
label item_label(item), ":for": item_id(item)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
def checkbox_attributes(item)
|
45
45
|
{
|
46
|
-
id: item_id(item),
|
46
|
+
":id": item_id(item),
|
47
47
|
type: :checkbox,
|
48
48
|
name: item_label(item),
|
49
49
|
value: item_value(item),
|
@@ -57,9 +57,9 @@ module Matestack
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def render_true_false_checkbox
|
60
|
-
input true_false_checkbox_attributes.merge(type: :hidden, id: nil, value: 0)
|
61
|
-
input true_false_checkbox_attributes.merge(type: :checkbox, id: item_id(1))
|
62
|
-
label input_label, for: item_id(1) if input_label
|
60
|
+
input true_false_checkbox_attributes.merge(type: :hidden, ":id": nil, value: 0)
|
61
|
+
input true_false_checkbox_attributes.merge(type: :checkbox, ":id": item_id(1))
|
62
|
+
label input_label, ":for": item_id(1) if input_label
|
63
63
|
end
|
64
64
|
|
65
65
|
def true_false_checkbox_attributes
|
@@ -88,13 +88,13 @@ module Matestack
|
|
88
88
|
def item_value(item)
|
89
89
|
item.is_a?(Array) ? item.last : item
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
def item_label(item)
|
93
93
|
item.is_a?(Array) ? item.first : item
|
94
94
|
end
|
95
95
|
|
96
96
|
def item_id(item)
|
97
|
-
"#{id}_#{item_value(item).to_s.gsub(" ", '_')}"
|
97
|
+
"#{id}+'_#{item_value(item).to_s.gsub(" ", '_')}'"
|
98
98
|
end
|
99
99
|
|
100
100
|
end
|
@@ -102,4 +102,4 @@ module Matestack
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
105
|
-
end
|
105
|
+
end
|
@@ -10,71 +10,58 @@ const formCheckboxMixin = {
|
|
10
10
|
|
11
11
|
if (key.startsWith("select.")) {
|
12
12
|
if (key.startsWith("select.multiple.")) {
|
13
|
+
self.$set(self.$parent.data, key.replace("select.multiple.", ""), null)
|
13
14
|
if (initValue) {
|
14
|
-
|
15
|
-
Object.assign(self.$parent.data, data);
|
15
|
+
self.setValue(JSON.parse(initValue["value"]));
|
16
16
|
self.afterInitialize(JSON.parse(initValue["value"]))
|
17
17
|
} else {
|
18
|
-
|
19
|
-
|
20
|
-
self.afterInitialize([])
|
18
|
+
self.setValue([]);
|
19
|
+
self.afterInitialize([]);
|
21
20
|
}
|
22
21
|
} else {
|
22
|
+
self.$set(self.$parent.data, key.replace("select.", ""), null)
|
23
23
|
if (initValue) {
|
24
24
|
if (valueType && valueType["value"] == "Integer") {
|
25
|
-
|
26
|
-
Object.assign(self.$parent.data, data);
|
25
|
+
self.setValue(parseInt(initValue["value"]));
|
27
26
|
self.afterInitialize(parseInt(initValue["value"]))
|
28
27
|
} else {
|
29
|
-
|
30
|
-
data[key.replace("select.", "")] = initValue["value"];
|
31
|
-
Object.assign(self.$parent.data, data);
|
28
|
+
self.setValue(initValue["value"]);
|
32
29
|
self.afterInitialize(initValue["value"])
|
33
30
|
}
|
34
31
|
} else {
|
35
|
-
|
36
|
-
Object.assign(self.$parent.data, data);
|
32
|
+
self.setValue(null);
|
37
33
|
self.afterInitialize(null)
|
38
34
|
}
|
39
35
|
}
|
40
36
|
} else {
|
37
|
+
self.$set(self.$parent.data, key.replace("input.", ""), null)
|
41
38
|
if (initValue) {
|
42
39
|
if(initValue["value"] === "true"){
|
43
|
-
|
44
|
-
Object.assign(self.$parent.data, data);
|
40
|
+
self.setValue(true);
|
45
41
|
self.afterInitialize(true)
|
46
42
|
}
|
47
43
|
if(initValue["value"] === "false"){
|
48
|
-
|
49
|
-
Object.assign(self.$parent.data, data);
|
44
|
+
self.setValue(false);
|
50
45
|
self.afterInitialize(false)
|
51
46
|
}
|
52
47
|
} else {
|
53
|
-
|
54
|
-
Object.assign(self.$parent.data, data);
|
48
|
+
self.setValue(null);
|
55
49
|
self.afterInitialize(null)
|
56
50
|
}
|
57
51
|
}
|
58
52
|
}
|
59
|
-
|
60
|
-
//without the timeout it's somehow not working
|
61
|
-
setTimeout(function () {
|
62
|
-
self.$parent.$forceUpdate();
|
63
|
-
self.$forceUpdate();
|
64
|
-
}, 1);
|
65
53
|
},
|
66
54
|
inputChanged: function (key) {
|
55
|
+
if (this.$parent.isNestedForm){
|
56
|
+
this.$parent.data["_destroy"] = false;
|
57
|
+
}
|
67
58
|
this.$parent.resetErrors(key);
|
68
|
-
this.$parent.$forceUpdate();
|
69
|
-
this.$forceUpdate();
|
70
59
|
},
|
71
60
|
afterInitialize: function(value){
|
72
61
|
// can be used in the main component for further initialization steps
|
73
62
|
},
|
74
63
|
setValue: function (value){
|
75
64
|
this.$parent.data[this.props["key"]] = value
|
76
|
-
this.$parent.$forceUpdate();
|
77
|
-
this.$forceUpdate();
|
78
65
|
}
|
79
66
|
}
|
80
67
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Matestack
|
2
|
+
module Ui
|
3
|
+
module VueJs
|
4
|
+
module Components
|
5
|
+
module Form
|
6
|
+
class FieldsForAddItem < Matestack::Ui::Component
|
7
|
+
|
8
|
+
required :key
|
9
|
+
|
10
|
+
required :prototype
|
11
|
+
|
12
|
+
attr_accessor :prototype_template_json
|
13
|
+
|
14
|
+
def create_children(&block)
|
15
|
+
# first render prototype_template_json
|
16
|
+
self.prototype_template_json = context.prototype.call().to_json
|
17
|
+
# delete from children in order not to render the prototype
|
18
|
+
self.children.shift
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def response
|
23
|
+
div id: "prototype-template-for-#{context.key}", "v-pre": true, data: { ":template": self.prototype_template_json }
|
24
|
+
Matestack::Ui::Core::Base.new('v-runtime-template', ':template': "nestedFormRuntimeTemplates['#{context.key}']")
|
25
|
+
a class: 'matestack-ui-core-form-fields-for-add-item', "@click.prevent": "addItem('#{context.key}')" do
|
26
|
+
yield if block_given?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -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
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import Vue from "vue/dist/vue.esm";
|
2
2
|
import Vuex from "vuex";
|
3
|
+
import VRuntimeTemplate from "v-runtime-template"
|
4
|
+
|
3
5
|
import axios from "axios";
|
4
6
|
|
5
7
|
import matestackEventHub from "../../event_hub";
|
@@ -11,7 +13,15 @@ const componentDef = {
|
|
11
13
|
return {
|
12
14
|
data: {},
|
13
15
|
errors: {},
|
14
|
-
loading: false
|
16
|
+
loading: false,
|
17
|
+
nestedForms: {},
|
18
|
+
isNestedForm: false,
|
19
|
+
hideNestedForm: false,
|
20
|
+
nestedFormRuntimeTemplates: {},
|
21
|
+
nestedFormRuntimeTemplateDomElements: {},
|
22
|
+
deletedNestedForms: {},
|
23
|
+
nestedFormRuntimeId: "",
|
24
|
+
nestedFormServerErrorIndex: "",
|
15
25
|
};
|
16
26
|
},
|
17
27
|
methods: {
|
@@ -32,31 +42,123 @@ const componentDef = {
|
|
32
42
|
},
|
33
43
|
resetErrors: function (key) {
|
34
44
|
if (this.errors[key]) {
|
35
|
-
this.errors[key]
|
45
|
+
delete this.errors[key];
|
46
|
+
Vue.set(this.errors);
|
47
|
+
}
|
48
|
+
if (this.isNestedForm){
|
49
|
+
var serverErrorKey = this.props["fields_for"].replace("_attributes", "")+"["+this.nestedFormServerErrorIndex+"]."+key
|
50
|
+
if (this.$parent.errors[serverErrorKey]) {
|
51
|
+
delete this.$parent.errors[serverErrorKey];
|
52
|
+
Vue.set(this.$parent.errors);
|
53
|
+
}
|
36
54
|
}
|
37
55
|
},
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
setErrors: function(errors){
|
57
|
+
this.errors = errors;
|
58
|
+
},
|
59
|
+
setNestedFormServerErrorIndex: function(value){
|
60
|
+
this.nestedFormServerErrorIndex = value;
|
61
|
+
},
|
62
|
+
setErrorKey: function(key, value){
|
63
|
+
Vue.set(this.errors, key, value);
|
64
|
+
},
|
65
|
+
flushErrors: function(key, value){
|
66
|
+
this.errors = {};
|
67
|
+
},
|
68
|
+
setNestedFormsError: function(errors){
|
69
|
+
let self = this;
|
70
|
+
Object.keys(errors).forEach(function(errorKey){
|
71
|
+
if (errorKey.includes(".")){
|
72
|
+
let childErrorKey = errorKey.split(".")[1]
|
73
|
+
let childModelName = errorKey.split(".")[0].split("[")[0]
|
74
|
+
let childModelIndex = errorKey.split(".")[0].split("[")[1].split("]")[0]
|
75
|
+
let mappedChildModelIndex = self.mapToNestedForms(parseInt(childModelIndex), childModelName+"_attributes")
|
76
|
+
self.nestedForms[childModelName+"_attributes"][mappedChildModelIndex].setNestedFormServerErrorIndex(parseInt(childModelIndex))
|
77
|
+
self.nestedForms[childModelName+"_attributes"][mappedChildModelIndex].setErrorKey(childErrorKey, errors[errorKey])
|
78
|
+
}
|
79
|
+
})
|
80
|
+
},
|
81
|
+
mapToNestedForms: function(serverIndex, nestedFormKey){
|
82
|
+
var primaryKey;
|
83
|
+
if(this.props["primary_key"] != undefined){
|
84
|
+
primaryKey = this.props["primary_key"];
|
85
|
+
}else{
|
86
|
+
primaryKey = "id";
|
87
|
+
}
|
88
|
+
|
89
|
+
var formIdMap = []
|
90
|
+
var childModelKey = 0;
|
91
|
+
while(this.data[nestedFormKey].length > childModelKey){
|
92
|
+
var ignore = this.data[nestedFormKey][childModelKey]["_destroy"] == true && this.data[nestedFormKey][childModelKey][primaryKey] == null
|
93
|
+
if(!ignore){
|
94
|
+
formIdMap.push(childModelKey)
|
95
|
+
}
|
96
|
+
childModelKey++;
|
97
|
+
}
|
98
|
+
|
99
|
+
return formIdMap[serverIndex];
|
100
|
+
},
|
101
|
+
resetNestedForms: function(){
|
102
|
+
var self = this;
|
103
|
+
Object.keys(self.nestedForms).forEach(function(childModelKey){
|
104
|
+
self.nestedForms[childModelKey].forEach(function(nestedFormInstance){
|
105
|
+
if(nestedFormInstance.data["_destroy"] == true){
|
106
|
+
var destroyed = true;
|
49
107
|
}
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
108
|
+
nestedFormInstance.initValues()
|
109
|
+
console.log(nestedFormInstance.data)
|
110
|
+
Vue.set(nestedFormInstance.data)
|
111
|
+
if(destroyed){
|
112
|
+
nestedFormInstance.hideNestedForm = true
|
113
|
+
Vue.set(nestedFormInstance.data, "_destroy", true)
|
114
|
+
}
|
115
|
+
})
|
116
|
+
})
|
117
|
+
},
|
118
|
+
removeItem: function(){
|
119
|
+
Vue.set(this.data, "_destroy", true)
|
120
|
+
this.hideNestedForm = true;
|
121
|
+
var id = parseInt(this.nestedFormRuntimeId.replace("_"+this.props["fields_for"]+"_child_", ""));
|
122
|
+
this.$parent.deletedNestedForms[this.props["fields_for"]].push(id);
|
123
|
+
var serverErrorKey = this.props["fields_for"].replace("_attributes", "")+"["+this.nestedFormServerErrorIndex+"]."
|
124
|
+
var self = this;
|
125
|
+
Object.keys(self.$parent.errors).forEach(function(errorKey){
|
126
|
+
if (errorKey.lastIndexOf(serverErrorKey, 0) == 0) {
|
127
|
+
delete self.$parent.errors[errorKey];
|
128
|
+
Vue.set(self.$parent.errors)
|
54
129
|
}
|
130
|
+
});
|
131
|
+
},
|
132
|
+
addItem: function(key){
|
133
|
+
var templateString = JSON.parse(this.$el.querySelector('#prototype-template-for-'+key).dataset[":template"])
|
134
|
+
if (this.nestedFormRuntimeTemplateDomElements[key] == null){
|
135
|
+
var dom_elem = document.createElement('div')
|
136
|
+
dom_elem.innerHTML = templateString
|
137
|
+
var existingItemsCount;
|
138
|
+
if (this.nestedForms[key] == undefined){
|
139
|
+
existingItemsCount = 0
|
140
|
+
}else{
|
141
|
+
existingItemsCount = this.nestedForms[key].length
|
142
|
+
}
|
143
|
+
dom_elem.querySelector('.matestack-form-fields-for').id = key+"_child_"+existingItemsCount
|
144
|
+
Vue.set(this.nestedFormRuntimeTemplateDomElements, key, dom_elem)
|
145
|
+
Vue.set(this.nestedFormRuntimeTemplates, key, this.nestedFormRuntimeTemplateDomElements[key].outerHTML)
|
146
|
+
}else{
|
147
|
+
var dom_elem = document.createElement('div')
|
148
|
+
dom_elem.innerHTML = templateString
|
149
|
+
var existingItemsCount = this.nestedForms[key].length
|
150
|
+
dom_elem.querySelector('.matestack-form-fields-for').id = key+"_child_"+existingItemsCount
|
151
|
+
this.nestedFormRuntimeTemplateDomElements[key].insertAdjacentHTML(
|
152
|
+
'beforeend',
|
153
|
+
dom_elem.innerHTML
|
154
|
+
)
|
155
|
+
Vue.set(this.nestedFormRuntimeTemplates, key, this.nestedFormRuntimeTemplateDomElements[key].outerHTML)
|
55
156
|
}
|
56
157
|
},
|
57
158
|
initValues: function () {
|
58
159
|
let self = this;
|
59
160
|
let data = {};
|
161
|
+
|
60
162
|
for (let key in self.$refs) {
|
61
163
|
if (key.startsWith("input-component")) {
|
62
164
|
self.$refs[key].initialize()
|
@@ -75,6 +177,7 @@ const componentDef = {
|
|
75
177
|
}
|
76
178
|
}
|
77
179
|
},
|
180
|
+
|
78
181
|
shouldResetFormOnSuccessfulSubmit() {
|
79
182
|
const self = this;
|
80
183
|
if (self.props["success"] != undefined && self.props["success"]["reset"] != undefined) {
|
@@ -93,6 +196,9 @@ const componentDef = {
|
|
93
196
|
},
|
94
197
|
perform: function(){
|
95
198
|
const self = this
|
199
|
+
if (self.props["fields_for"] != null) {
|
200
|
+
return;
|
201
|
+
}
|
96
202
|
var form = self.$el.tagName == 'FORM' ? self.$el : self.$el.querySelector('form');
|
97
203
|
if(form.checkValidity()){
|
98
204
|
self.loading = true;
|
@@ -110,29 +216,55 @@ const componentDef = {
|
|
110
216
|
matestackEventHub.$emit('static_form_errors');
|
111
217
|
}
|
112
218
|
},
|
219
|
+
transformToFormData: function (formData, dataNode, parentKey=null) {
|
220
|
+
var self = this;
|
221
|
+
for (let key in dataNode) {
|
222
|
+
if (key.endsWith("[]")) {
|
223
|
+
for (let i in dataNode[key]) {
|
224
|
+
let file = dataNode[key][i];
|
225
|
+
if (parentKey != null) {
|
226
|
+
formData.append(self.props["for"] + parentKey + "[" + key.slice(0, -2) + "][]", file);
|
227
|
+
} else {
|
228
|
+
formData.append(self.props["for"] + "[" + key.slice(0, -2) + "][]", file);
|
229
|
+
}
|
230
|
+
}
|
231
|
+
} else {
|
232
|
+
if (Array.isArray(dataNode[key])){
|
233
|
+
dataNode[key].forEach(function(item, index){
|
234
|
+
if (parentKey != null) {
|
235
|
+
let _key = parentKey + "[" + key + "]" + "[]";
|
236
|
+
formData = self.transformToFormData(formData, item, _key)
|
237
|
+
} else {
|
238
|
+
let _key = "[" + key + "]" + "[]";
|
239
|
+
formData = self.transformToFormData(formData, item, _key)
|
240
|
+
}
|
241
|
+
})
|
242
|
+
} else {
|
243
|
+
if (dataNode[key] != null){
|
244
|
+
if (parentKey != null) {
|
245
|
+
formData.append(self.props["for"] + parentKey + "[" + key + "]", dataNode[key]);
|
246
|
+
} else {
|
247
|
+
formData.append(self.props["for"] + "[" + key + "]", dataNode[key]);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
return formData;
|
255
|
+
},
|
113
256
|
sendRequest: function(){
|
114
257
|
const self = this;
|
115
258
|
let payload = {};
|
116
259
|
payload[self.props["for"]] = self.data;
|
117
260
|
let axios_config = {};
|
118
261
|
if (self.props["multipart"] == true ) {
|
119
|
-
let
|
120
|
-
|
121
|
-
if (key.endsWith("[]")) {
|
122
|
-
for (let i in self.data[key]) {
|
123
|
-
let file = self.data[key][i];
|
124
|
-
form_data.append(self.props["for"] + "[" + key.slice(0, -2) + "][]", file);
|
125
|
-
}
|
126
|
-
} else {
|
127
|
-
if (self.data[key] != null){
|
128
|
-
form_data.append(self.props["for"] + "[" + key + "]", self.data[key]);
|
129
|
-
}
|
130
|
-
}
|
131
|
-
}
|
262
|
+
let formData = new FormData();
|
263
|
+
formData = this.transformToFormData(formData, this.data)
|
132
264
|
axios_config = {
|
133
265
|
method: self.props["method"],
|
134
266
|
url: self.props["submit_path"],
|
135
|
-
data:
|
267
|
+
data: formData,
|
136
268
|
headers: {
|
137
269
|
"X-CSRF-Token": document.getElementsByName("csrf-token")[0].getAttribute("content"),
|
138
270
|
"Content-Type": "multipart/form-data",
|
@@ -202,16 +334,20 @@ const componentDef = {
|
|
202
334
|
return;
|
203
335
|
}
|
204
336
|
|
337
|
+
self.flushErrors();
|
338
|
+
|
205
339
|
if (self.shouldResetFormOnSuccessfulSubmit())
|
206
340
|
{
|
207
|
-
self.setProps(self.data, null);
|
208
341
|
self.initValues();
|
342
|
+
self.resetNestedForms();
|
209
343
|
}
|
210
344
|
})
|
211
345
|
.catch(function (error) {
|
212
346
|
self.loading = false;
|
213
347
|
if (error.response && error.response.data && error.response.data.errors) {
|
214
348
|
self.errors = error.response.data.errors;
|
349
|
+
self.setErrors(error.response.data.errors);
|
350
|
+
self.setNestedFormsError(error.response.data.errors);
|
215
351
|
}
|
216
352
|
if (self.props["failure"] != undefined && self.props["failure"]["emit"] != undefined) {
|
217
353
|
matestackEventHub.$emit(self.props["failure"]["emit"], error.response.data);
|
@@ -266,8 +402,71 @@ const componentDef = {
|
|
266
402
|
},
|
267
403
|
},
|
268
404
|
mounted: function () {
|
269
|
-
this
|
405
|
+
var self = this;
|
406
|
+
if (this.props["fields_for"] != undefined) {
|
407
|
+
this.isNestedForm = true;
|
408
|
+
|
409
|
+
this.data = { "_destroy": false };
|
410
|
+
|
411
|
+
//initialize nestedForm data in parent form if required
|
412
|
+
if(this.$parent.data[this.props["fields_for"]] == undefined){
|
413
|
+
this.$parent.data[this.props["fields_for"]] = [];
|
414
|
+
}
|
415
|
+
if(this.$parent.nestedForms[this.props["fields_for"]] == undefined){
|
416
|
+
this.$parent.nestedForms[this.props["fields_for"]] = [];
|
417
|
+
}
|
418
|
+
if(this.$parent.deletedNestedForms[this.props["fields_for"]] == undefined){
|
419
|
+
this.$parent.deletedNestedForms[this.props["fields_for"]] = []
|
420
|
+
}
|
421
|
+
|
422
|
+
var id = parseInt(self.$el.id.replace(this.props["fields_for"]+"_child_", ""));
|
423
|
+
|
424
|
+
//setup data binding for serverside rendered nested forms
|
425
|
+
if (isNaN(id)){
|
426
|
+
id = this.$parent.nestedForms[this.props["fields_for"]].length
|
427
|
+
this.nestedFormRuntimeId = "_"+this.props["fields_for"]+"_child_"+id
|
428
|
+
this.$el.id = this.props["fields_for"]+"_child_"+id
|
429
|
+
this.initValues()
|
430
|
+
this.$parent.data[this.props["fields_for"]].push(this.data);
|
431
|
+
this.$parent.nestedForms[this.props["fields_for"]].push(this);
|
432
|
+
}
|
433
|
+
|
434
|
+
//setup data binding for runtime nested forms (dynamic add via v-runtime-template)
|
435
|
+
if (!isNaN(id)){
|
436
|
+
this.nestedFormRuntimeId = "_"+this.props["fields_for"]+"_child_"+id
|
437
|
+
if(this.$parent.data[this.props["fields_for"]][id] == undefined){
|
438
|
+
//new runtime form
|
439
|
+
this.initValues()
|
440
|
+
this.$parent.data[this.props["fields_for"]].push(this.data);
|
441
|
+
this.$parent.nestedForms[this.props["fields_for"]].push(this);
|
442
|
+
}else{
|
443
|
+
//retreive state for existing runtime form (after remount for example)
|
444
|
+
this.data = this.$parent.data[this.props["fields_for"]][id]
|
445
|
+
if (this.data["_destroy"] == true){
|
446
|
+
this.hideNestedForm = true;
|
447
|
+
}
|
448
|
+
this.$parent.nestedForms[this.props["fields_for"]][id] = this;
|
449
|
+
Object.keys(this.$parent.errors).forEach(function(errorKey){
|
450
|
+
if (errorKey.includes(".")){
|
451
|
+
let childErrorKey = errorKey.split(".")[1]
|
452
|
+
let childModelName = errorKey.split(".")[0].split("[")[0]
|
453
|
+
let childModelIndex = errorKey.split(".")[0].split("[")[1].split("]")[0]
|
454
|
+
let mappedChildModelIndex = self.$parent.mapToNestedForms(parseInt(childModelIndex), childModelName+"_attributes")
|
455
|
+
if(childModelName+"_attributes" == self.props["fields_for"] && mappedChildModelIndex == id){
|
456
|
+
self.setNestedFormServerErrorIndex(parseInt(childModelIndex))
|
457
|
+
self.setErrorKey(childErrorKey, self.$parent.errors[errorKey])
|
458
|
+
}
|
459
|
+
}
|
460
|
+
})
|
461
|
+
}
|
462
|
+
}
|
463
|
+
} else {
|
464
|
+
this.initValues();
|
465
|
+
}
|
270
466
|
},
|
467
|
+
components: {
|
468
|
+
VRuntimeTemplate: VRuntimeTemplate
|
469
|
+
}
|
271
470
|
};
|
272
471
|
|
273
472
|
let component = Vue.component("matestack-ui-core-form", componentDef);
|
@@ -7,6 +7,9 @@ module Matestack
|
|
7
7
|
vue_name 'matestack-ui-core-form'
|
8
8
|
|
9
9
|
optional :for, :path, :success, :failure, :multipart, :emit, :delay, :errors
|
10
|
+
optional :fields_for, :reject_blank
|
11
|
+
|
12
|
+
attr_accessor :prototype_template
|
10
13
|
|
11
14
|
# setup form context to allow child components like inputs to access the form configuration
|
12
15
|
def initialize(html_tag = nil, text = nil, options = {}, &block)
|
@@ -16,9 +19,21 @@ module Matestack
|
|
16
19
|
Matestack::Ui::VueJs::Components::Form::Context.form_context = previous_form_context
|
17
20
|
end
|
18
21
|
|
22
|
+
def component_id
|
23
|
+
"matestack-form-fields-for-#{context.fields_for}-#{SecureRandom.hex}" if context.fields_for
|
24
|
+
end
|
25
|
+
|
19
26
|
def response
|
20
|
-
|
21
|
-
|
27
|
+
if context.fields_for
|
28
|
+
div class: "matestack-form-fields-for", "v-show": "hideNestedForm != true", id: options[:id] do
|
29
|
+
form_input key: context.for&.class&.primary_key, type: :hidden # required for existing model mapping
|
30
|
+
form_input key: :_destroy, type: :hidden, init: true if context.reject_blank == true
|
31
|
+
yield
|
32
|
+
end
|
33
|
+
else
|
34
|
+
form attributes do
|
35
|
+
yield
|
36
|
+
end
|
22
37
|
end
|
23
38
|
end
|
24
39
|
|
@@ -40,6 +55,8 @@ module Matestack
|
|
40
55
|
multipart: !!ctx.multipart,
|
41
56
|
emit: ctx.emit,
|
42
57
|
delay: ctx.delay,
|
58
|
+
fields_for: ctx.fields_for,
|
59
|
+
primary_key: for_object_primary_key
|
43
60
|
}
|
44
61
|
end
|
45
62
|
|
@@ -52,13 +69,16 @@ module Matestack
|
|
52
69
|
@for_option ||= ctx.for
|
53
70
|
end
|
54
71
|
|
72
|
+
def for_object_primary_key
|
73
|
+
context.for&.class&.primary_key rescue nil
|
74
|
+
end
|
75
|
+
|
55
76
|
def form_method
|
56
77
|
@form_method ||= options.delete(:method)
|
57
78
|
end
|
58
|
-
|
59
79
|
end
|
60
80
|
end
|
61
81
|
end
|
62
82
|
end
|
63
83
|
end
|
64
|
-
end
|
84
|
+
end
|
@@ -8,7 +8,7 @@ module Matestack
|
|
8
8
|
|
9
9
|
def response
|
10
10
|
div class: 'matestack-ui-core-form-input' do
|
11
|
-
label input_label, for: id if input_label
|
11
|
+
label input_label, ":for": id if input_label
|
12
12
|
input input_attributes
|
13
13
|
render_errors
|
14
14
|
end
|
@@ -22,6 +22,11 @@ module Matestack
|
|
22
22
|
attributes
|
23
23
|
end
|
24
24
|
|
25
|
+
def init_value
|
26
|
+
return nil if ctx.type.to_s == "file"
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
25
30
|
def vue_props
|
26
31
|
{
|
27
32
|
init_value: init_value,
|
@@ -34,4 +39,4 @@ module Matestack
|
|
34
39
|
end
|
35
40
|
end
|
36
41
|
end
|
37
|
-
end
|
42
|
+
end
|
@@ -7,22 +7,17 @@ const formInputMixin = {
|
|
7
7
|
for (let key in this.$refs) {
|
8
8
|
let initValue = this.$refs[key]["attributes"]["init-value"];
|
9
9
|
|
10
|
+
self.$set(self.$parent.data, key.replace("input.", ""), null)
|
11
|
+
|
10
12
|
if (initValue) {
|
11
|
-
|
12
|
-
Object.assign(self.$parent.data, data);
|
13
|
+
self.setValue(initValue["value"])
|
13
14
|
self.afterInitialize(initValue["value"])
|
14
15
|
} else {
|
15
|
-
|
16
|
-
Object.assign(self.$parent.data, data);
|
16
|
+
self.setValue(null)
|
17
17
|
self.afterInitialize(null)
|
18
18
|
}
|
19
19
|
}
|
20
20
|
|
21
|
-
//without the timeout it's somehow not working
|
22
|
-
setTimeout(function () {
|
23
|
-
self.$parent.$forceUpdate();
|
24
|
-
self.$forceUpdate();
|
25
|
-
}, 1);
|
26
21
|
},
|
27
22
|
filesAdded: function (key) {
|
28
23
|
const dataTransfer = event.dataTransfer || event.target;
|
@@ -39,17 +34,17 @@ const formInputMixin = {
|
|
39
34
|
}
|
40
35
|
},
|
41
36
|
inputChanged: function (key) {
|
37
|
+
if (this.$parent.isNestedForm){
|
38
|
+
this.$parent.data["_destroy"] = false;
|
39
|
+
}
|
42
40
|
this.$parent.resetErrors(key);
|
43
|
-
|
44
|
-
this.$forceUpdate();
|
41
|
+
|
45
42
|
},
|
46
43
|
afterInitialize: function(value){
|
47
44
|
// can be used in the main component for further initialization steps
|
48
45
|
},
|
49
46
|
setValue: function (value){
|
50
|
-
this.$parent.data[this.props["key"]] = value
|
51
|
-
this.$parent.$forceUpdate();
|
52
|
-
this.$forceUpdate();
|
47
|
+
this.$parent.data[this.props["key"]] = value;
|
53
48
|
}
|
54
49
|
}
|
55
50
|
|
@@ -16,7 +16,7 @@ module Matestack
|
|
16
16
|
def render_options
|
17
17
|
radio_options.to_a.each do |item|
|
18
18
|
input radio_attributes(item)
|
19
|
-
label item_label(item), for: item_id(item)
|
19
|
+
label item_label(item), ":for": item_id(item)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -33,7 +33,7 @@ module Matestack
|
|
33
33
|
|
34
34
|
def radio_attributes(item)
|
35
35
|
attributes.merge({
|
36
|
-
id: item_id(item),
|
36
|
+
":id": item_id(item),
|
37
37
|
name: item_name(item),
|
38
38
|
value: item_value(item),
|
39
39
|
type: :radio,
|
@@ -51,13 +51,13 @@ module Matestack
|
|
51
51
|
def item_value(item)
|
52
52
|
item.is_a?(Array) ? item.last : item
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
def item_label(item)
|
56
56
|
item.is_a?(Array) ? item.first : item
|
57
57
|
end
|
58
58
|
|
59
59
|
def item_id(item)
|
60
|
-
"#{id || key}_#{item_value(item)}"
|
60
|
+
"#{id || key}+'_#{item_value(item)}'"
|
61
61
|
end
|
62
62
|
|
63
63
|
def item_name(item)
|
@@ -73,4 +73,4 @@ module Matestack
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
|
-
end
|
76
|
+
end
|
@@ -10,53 +10,43 @@ const formRadioMixin = {
|
|
10
10
|
|
11
11
|
if (key.startsWith("select.")) {
|
12
12
|
if (key.startsWith("select.multiple.")) {
|
13
|
+
self.$set(self.$parent.data, key.replace("select.multiple.", ""), null)
|
13
14
|
if (initValue) {
|
14
|
-
|
15
|
-
Object.assign(self.$parent.data, data);
|
15
|
+
self.setValue(JSON.parse(initValue["value"]));
|
16
16
|
self.afterInitialize(JSON.parse(initValue["value"]))
|
17
17
|
} else {
|
18
|
-
|
19
|
-
|
20
|
-
self.afterInitialize([])
|
18
|
+
self.setValue([]);
|
19
|
+
self.afterInitialize([]);
|
21
20
|
}
|
22
21
|
} else {
|
22
|
+
self.$set(self.$parent.data, key.replace("select.", ""), null)
|
23
23
|
if (initValue) {
|
24
24
|
if (valueType && valueType["value"] == "Integer") {
|
25
|
-
|
26
|
-
Object.assign(self.$parent.data, data);
|
25
|
+
self.setValue(parseInt(initValue["value"]));
|
27
26
|
self.afterInitialize(parseInt(initValue["value"]))
|
28
27
|
} else {
|
29
|
-
|
30
|
-
Object.assign(self.$parent.data, data);
|
28
|
+
self.setValue(initValue["value"]);
|
31
29
|
self.afterInitialize(initValue["value"])
|
32
30
|
}
|
33
31
|
} else {
|
34
|
-
|
35
|
-
Object.assign(self.$parent.data, data);
|
32
|
+
self.setValue(null);
|
36
33
|
self.afterInitialize(null)
|
37
34
|
}
|
38
35
|
}
|
39
36
|
}
|
40
37
|
}
|
41
|
-
|
42
|
-
//without the timeout it's somehow not working
|
43
|
-
setTimeout(function () {
|
44
|
-
self.$parent.$forceUpdate();
|
45
|
-
self.$forceUpdate();
|
46
|
-
}, 1);
|
47
38
|
},
|
48
39
|
inputChanged: function (key) {
|
40
|
+
if (this.$parent.isNestedForm){
|
41
|
+
this.$parent.data["_destroy"] = false;
|
42
|
+
}
|
49
43
|
this.$parent.resetErrors(key);
|
50
|
-
this.$parent.$forceUpdate();
|
51
|
-
this.$forceUpdate();
|
52
44
|
},
|
53
45
|
afterInitialize: function(value){
|
54
46
|
// can be used in the main component for further initialization steps
|
55
47
|
},
|
56
48
|
setValue: function (value){
|
57
49
|
this.$parent.data[this.props["key"]] = value
|
58
|
-
this.$parent.$forceUpdate();
|
59
|
-
this.$forceUpdate();
|
60
50
|
}
|
61
51
|
}
|
62
52
|
|
@@ -8,7 +8,7 @@ module Matestack
|
|
8
8
|
|
9
9
|
def response
|
10
10
|
div class: 'matestack-ui-core-form-select' do
|
11
|
-
label input_label, for: id if input_label
|
11
|
+
label input_label, ":for": id if input_label
|
12
12
|
select select_attributes do
|
13
13
|
render_options
|
14
14
|
end
|
@@ -39,7 +39,7 @@ module Matestack
|
|
39
39
|
def select_attributes
|
40
40
|
attributes.merge({
|
41
41
|
multiple: multiple,
|
42
|
-
id: id,
|
42
|
+
":id": id,
|
43
43
|
ref: "select#{'.multiple' if multiple}.#{key}",
|
44
44
|
'value-type': value_type(select_options.first),
|
45
45
|
'init-value': init_value,
|
@@ -57,7 +57,7 @@ module Matestack
|
|
57
57
|
def item_value(item)
|
58
58
|
item.is_a?(Array) ? item.last : item
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
def item_label(item)
|
62
62
|
item.is_a?(Array) ? item.first : item
|
63
63
|
end
|
@@ -85,4 +85,4 @@ module Matestack
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
88
|
-
end
|
88
|
+
end
|
@@ -10,49 +10,43 @@ const formSelectMixin = {
|
|
10
10
|
|
11
11
|
if (key.startsWith("select.")) {
|
12
12
|
if (key.startsWith("select.multiple.")) {
|
13
|
+
self.$set(self.$parent.data, key.replace("select.multiple.", ""), null)
|
13
14
|
if (initValue) {
|
14
|
-
|
15
|
+
self.setValue(JSON.parse(initValue["value"]));
|
15
16
|
self.afterInitialize(JSON.parse(initValue["value"]))
|
16
17
|
} else {
|
17
|
-
|
18
|
-
self.afterInitialize([])
|
18
|
+
self.setValue([]);
|
19
|
+
self.afterInitialize([]);
|
19
20
|
}
|
20
21
|
} else {
|
22
|
+
self.$set(self.$parent.data, key.replace("select.", ""), null)
|
21
23
|
if (initValue) {
|
22
24
|
if (valueType && valueType["value"] == "Integer") {
|
23
|
-
|
25
|
+
self.setValue(parseInt(initValue["value"]));
|
24
26
|
self.afterInitialize(parseInt(initValue["value"]))
|
25
27
|
} else {
|
26
|
-
|
28
|
+
self.setValue(initValue["value"]);
|
27
29
|
self.afterInitialize(initValue["value"])
|
28
30
|
}
|
29
31
|
} else {
|
30
|
-
|
32
|
+
self.setValue(null);
|
31
33
|
self.afterInitialize(null)
|
32
34
|
}
|
33
35
|
}
|
34
36
|
}
|
35
37
|
}
|
36
|
-
|
37
|
-
//without the timeout it's somehow not working
|
38
|
-
setTimeout(function () {
|
39
|
-
Object.assign(self.$parent.data, data);
|
40
|
-
self.$parent.$forceUpdate();
|
41
|
-
self.$forceUpdate();
|
42
|
-
}, 1);
|
43
38
|
},
|
44
39
|
inputChanged: function (key) {
|
40
|
+
if (this.$parent.isNestedForm){
|
41
|
+
this.$parent.data["_destroy"] = false;
|
42
|
+
}
|
45
43
|
this.$parent.resetErrors(key);
|
46
|
-
this.$parent.$forceUpdate();
|
47
|
-
this.$forceUpdate();
|
48
44
|
},
|
49
45
|
afterInitialize: function(value){
|
50
46
|
// can be used in the main component for further initialization steps
|
51
47
|
},
|
52
48
|
setValue: function (value){
|
53
49
|
this.$parent.data[this.props["key"]] = value
|
54
|
-
this.$parent.$forceUpdate();
|
55
|
-
this.$forceUpdate();
|
56
50
|
}
|
57
51
|
}
|
58
52
|
|
@@ -8,7 +8,7 @@ module Matestack
|
|
8
8
|
|
9
9
|
def response
|
10
10
|
div class: 'matestack-ui-core-form-textarea' do
|
11
|
-
label input_label, for: id if input_label
|
11
|
+
label input_label, ":for": id if input_label
|
12
12
|
textarea textarea_attributes
|
13
13
|
render_errors
|
14
14
|
end
|
@@ -34,4 +34,4 @@ module Matestack
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
-
end
|
37
|
+
end
|
@@ -7,35 +7,28 @@ const formTextareaMixin = {
|
|
7
7
|
for (let key in this.$refs) {
|
8
8
|
let initValue = this.$refs[key]["attributes"]["init-value"];
|
9
9
|
|
10
|
+
self.$set(self.$parent.data, key.replace("input.", ""), null)
|
11
|
+
|
10
12
|
if (initValue) {
|
11
|
-
|
12
|
-
Object.assign(self.$parent.data, data);
|
13
|
+
self.setValue(initValue["value"])
|
13
14
|
self.afterInitialize(initValue["value"])
|
14
15
|
} else {
|
15
|
-
|
16
|
-
Object.assign(self.$parent.data, data);
|
16
|
+
self.setValue(null)
|
17
17
|
self.afterInitialize(null)
|
18
18
|
}
|
19
19
|
}
|
20
|
-
|
21
|
-
//without the timeout it's somehow not working
|
22
|
-
setTimeout(function () {
|
23
|
-
self.$parent.$forceUpdate();
|
24
|
-
self.$forceUpdate();
|
25
|
-
}, 1);
|
26
20
|
},
|
27
21
|
inputChanged: function (key) {
|
22
|
+
if (this.$parent.isNestedForm){
|
23
|
+
this.$parent.data["_destroy"] = false;
|
24
|
+
}
|
28
25
|
this.$parent.resetErrors(key);
|
29
|
-
this.$parent.$forceUpdate();
|
30
|
-
this.$forceUpdate();
|
31
26
|
},
|
32
27
|
afterInitialize: function(value){
|
33
28
|
// can be used in the main component for further initialization steps
|
34
29
|
},
|
35
30
|
setValue: function (value){
|
36
31
|
this.$parent.data[this.props["key"]] = value
|
37
|
-
this.$parent.$forceUpdate();
|
38
|
-
this.$forceUpdate();
|
39
32
|
}
|
40
33
|
}
|
41
34
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matestack-ui-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Jabari
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-06-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -82,6 +82,8 @@ files:
|
|
82
82
|
- lib/matestack/ui/vue_js/components/form/checkbox.rb
|
83
83
|
- lib/matestack/ui/vue_js/components/form/checkbox_mixin.js
|
84
84
|
- lib/matestack/ui/vue_js/components/form/context.rb
|
85
|
+
- lib/matestack/ui/vue_js/components/form/fields_for_add_item.rb
|
86
|
+
- lib/matestack/ui/vue_js/components/form/fields_for_remove_item.rb
|
85
87
|
- lib/matestack/ui/vue_js/components/form/form.js
|
86
88
|
- lib/matestack/ui/vue_js/components/form/form.rb
|
87
89
|
- lib/matestack/ui/vue_js/components/form/input.js
|