matestack-ui-core 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|