Skip to content

Commit 054c467

Browse files
committed
Merge branch 'nh/form-uploads-for-multipart-form-data' into develop
2 parents b3795cf + 122545d commit 054c467

File tree

17 files changed

+774
-237
lines changed

17 files changed

+774
-237
lines changed
Lines changed: 165 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,124 @@
1-
import Vue from 'vue/dist/vue.esm'
2-
import Vuex from 'vuex'
3-
import axios from 'axios'
1+
import Vue from "vue/dist/vue.esm";
2+
import Vuex from "vuex";
3+
import axios from "axios";
44

5-
import matestackEventHub from '../js/event-hub'
6-
import componentMixin from '../component/component'
5+
import matestackEventHub from "../js/event-hub";
6+
import componentMixin from "../component/component";
77

88
const componentDef = {
99
mixins: [componentMixin],
10-
data: function(){
10+
data: function () {
1111
return {
1212
data: {},
1313
showInlineForm: false,
14-
errors: {}
15-
}
14+
errors: {},
15+
};
1616
},
1717
methods: {
18-
initDataKey: function(key, initValue){
18+
initDataKey: function (key, initValue) {
1919
this.data[key] = initValue;
2020
},
21-
inputChanged: function(key){
22-
this.resetErrors(key)
21+
inputChanged: function (key) {
22+
this.resetErrors(key);
2323
},
24-
updateFormValue: function(key, value){
24+
updateFormValue: function (key, value) {
2525
this.data[key] = value;
2626
},
27-
resetErrors: function(key){
28-
if (this.errors[key]){
27+
resetErrors: function (key) {
28+
if (this.errors[key]) {
2929
this.errors[key] = null;
3030
}
3131
},
32-
launchInlineForm: function(key, value){
32+
launchInlineForm: function (key, value) {
3333
this.showInlineForm = true;
3434
this.data[key] = value;
3535
const self = this;
3636
setTimeout(function () {
3737
self.$refs.inlineinput.focus();
3838
}, 300);
3939
},
40-
closeInlineForm: function(){
40+
closeInlineForm: function () {
4141
this.showInlineForm = false;
4242
},
43-
setProps: function(flat, newVal){
44-
for(var i in flat){
45-
if((typeof flat[i] === "object") && !(flat[i] instanceof Array)){
46-
setProps(flat[i], newVal);
47-
return;
48-
} else {
49-
flat[i] = newVal;
50-
}
43+
setProps: function (flat, newVal) {
44+
for (var i in flat) {
45+
if (flat[i] instanceof File){
46+
flat[i] = newVal;
47+
this.$refs["input."+i].value = newVal
48+
} else if (flat[i] instanceof Array) {
49+
flat[i] = newVal
50+
this.$refs["input."+i].value = newVal
51+
} else if (typeof flat[i] === "object" && !(flat[i] instanceof Array)) {
52+
setProps(flat[i], newVal);
53+
return;
54+
} else {
55+
flat[i] = newVal;
56+
}
57+
}
58+
},
59+
filesAdded: function (key) {
60+
const dataTransfer = event.dataTransfer || event.target;
61+
const files = dataTransfer.files;
62+
if (event.target.attributes.multiple) {
63+
this.data[key] = [];
64+
for (let index in files) {
65+
if (files[index] instanceof File) {
66+
this.data[key].push(files[index]);
67+
}
68+
}
69+
} else {
70+
this.data[key] = files[0];
5171
}
5272
},
53-
initValues: function(){
73+
initValues: function () {
5474
let self = this;
55-
let data = {}
75+
let data = {};
5676
for (let key in self.$refs) {
57-
let initValue = self.$refs[key]["attributes"]["init-value"]
58-
let valueType = self.$refs[key]["attributes"]["value-type"]
77+
let initValue = self.$refs[key]["attributes"]["init-value"];
78+
let valueType = self.$refs[key]["attributes"]["value-type"];
5979

60-
if (key.startsWith("input.")){
61-
if(initValue){
62-
data[key.replace('input.', '')] = initValue["value"]
63-
}else{
64-
data[key.replace('input.', '')] = null
80+
if (key.startsWith("input.")) {
81+
if (initValue) {
82+
data[key.replace("input.", "")] = initValue["value"];
83+
} else {
84+
data[key.replace("input.", "")] = null;
6585
}
6686
}
67-
if (key.startsWith("select.")){
68-
if (key.startsWith("select.multiple.")){
69-
if(initValue){
70-
data[key.replace('select.multiple.', '')] = JSON.parse(initValue["value"])
71-
}else{
72-
data[key.replace('select.multiple.', '')] = []
87+
if (key.startsWith("select.")) {
88+
if (key.startsWith("select.multiple.")) {
89+
if (initValue) {
90+
data[key.replace("select.multiple.", "")] = JSON.parse(initValue["value"]);
91+
} else {
92+
data[key.replace("select.multiple.", "")] = [];
7393
}
74-
}else{
75-
if(initValue){
76-
if(valueType && valueType["value"] == "Integer")
77-
data[key.replace('select.', '')] = parseInt(initValue["value"])
78-
else{
79-
data[key.replace('select.', '')] = initValue["value"]
94+
} else {
95+
if (initValue) {
96+
if (valueType && valueType["value"] == "Integer") data[key.replace("select.", "")] = parseInt(initValue["value"]);
97+
else {
98+
data[key.replace("select.", "")] = initValue["value"];
8099
}
81-
}else{
82-
data[key.replace('select.', '')] = null
100+
} else {
101+
data[key.replace("select.", "")] = null;
83102
}
84103
}
85-
86104
}
87105
}
88106
self.data = data;
89107
},
90108
shouldResetFormOnSuccessfulSubmit() {
91-
const self = this
92-
if (self.componentConfig['success'] != undefined && self.componentConfig['success']['reset'] != undefined) {
93-
return self.componentConfig['success']['reset']
109+
const self = this;
110+
if (self.componentConfig["success"] != undefined && self.componentConfig["success"]["reset"] != undefined) {
111+
return self.componentConfig["success"]["reset"];
94112
} else {
95-
return self.shouldResetFormOnSuccessfulSubmitByDefault()
113+
return self.shouldResetFormOnSuccessfulSubmitByDefault();
96114
}
97115
},
98116
shouldResetFormOnSuccessfulSubmitByDefault() {
99-
const self = this
117+
const self = this;
100118
if (self.componentConfig["method"] == "put") {
101-
return false
119+
return false;
102120
} else {
103-
return true
121+
return true;
104122
}
105123
},
106124
perform: function(){
@@ -117,69 +135,99 @@ const componentDef = {
117135
}
118136
},
119137
sendRequest: function(){
120-
const self = this
121-
let payload = {}
122-
payload[self.componentConfig["for"]] = self.data
123-
axios({
124-
method: self.componentConfig["method"],
125-
url: self.componentConfig["submit_path"],
126-
data: payload,
127-
headers: {
128-
'X-CSRF-Token': document.getElementsByName("csrf-token")[0].getAttribute('content')
129-
}
130-
})
131-
.then(function(response){
132-
if (self.componentConfig["success"] != undefined && self.componentConfig["success"]["emit"] != undefined) {
133-
matestackEventHub.$emit(self.componentConfig["success"]["emit"], response.data);
134-
}
135-
if (self.componentConfig["success"] != undefined
136-
&& self.componentConfig["success"]["transition"] != undefined
137-
&& (
138-
self.componentConfig["success"]["transition"]["follow_response"] == undefined
139-
||
140-
self.componentConfig["success"]["transition"]["follow_response"] === false
141-
)
142-
&& self.$store != undefined
143-
) {
144-
let path = self.componentConfig["success"]["transition"]["path"]
145-
self.$store.dispatch('navigateTo', {url: path, backwards: false})
146-
return;
147-
}
148-
if (self.componentConfig["success"] != undefined
149-
&& self.componentConfig["success"]["transition"] != undefined
150-
&& self.componentConfig["success"]["transition"]["follow_response"] === true
151-
&& self.$store != undefined
152-
) {
153-
let path = response.data["transition_to"] || response.request.responseURL
154-
self.$store.dispatch('navigateTo', {url: path, backwards: false})
155-
return;
156-
}
157-
if (self.shouldResetFormOnSuccessfulSubmit())
158-
{
159-
self.setProps(self.data, null);
160-
self.initValues();
161-
}
162-
self.showInlineForm = false;
163-
})
164-
.catch(function(error){
165-
if(error.response && error.response.data && error.response.data.errors){
166-
self.errors = error.response.data.errors;
167-
}
168-
if (self.componentConfig["failure"] != undefined && self.componentConfig["failure"]["emit"] != undefined) {
169-
matestackEventHub.$emit(self.componentConfig["failure"]["emit"], error.response.data);
170-
}
171-
if (self.componentConfig["failure"] != undefined && self.componentConfig["failure"]["transition"] != undefined && self.$store != undefined) {
172-
let path = self.componentConfig["failure"]["transition"]["path"]
173-
self.$store.dispatch('navigateTo', {url: path, backwards: false})
138+
const self = this;
139+
let payload = {};
140+
payload[self.componentConfig["for"]] = self.data;
141+
let axios_config = {};
142+
if (self.componentConfig["multipart"] == true ) {
143+
let form_data = new FormData();
144+
for (let key in self.data) {
145+
if (key.endsWith("[]")) {
146+
for (let i in self.data[key]) {
147+
let file = self.data[key][i];
148+
form_data.append(self.componentConfig["for"] + "[" + key.slice(0, -2) + "][]", file);
149+
}
150+
} else {
151+
if (self.data[key] != null){
152+
form_data.append(self.componentConfig["for"] + "[" + key + "]", self.data[key]);
153+
}
154+
}
174155
}
175-
})
176-
}
156+
axios_config = {
157+
method: self.componentConfig["method"],
158+
url: self.componentConfig["submit_path"],
159+
data: form_data,
160+
headers: {
161+
"X-CSRF-Token": document.getElementsByName("csrf-token")[0].getAttribute("content"),
162+
"Content-Type": "multipart/form-data",
163+
},
164+
};
165+
} else {
166+
axios_config = {
167+
method: self.componentConfig["method"],
168+
url: self.componentConfig["submit_path"],
169+
data: payload,
170+
headers: {
171+
"X-CSRF-Token": document.getElementsByName("csrf-token")[0].getAttribute("content"),
172+
"Content-Type": "application/json",
173+
},
174+
};
175+
}
176+
axios(axios_config)
177+
.then(function (response) {
178+
if (self.componentConfig["success"] != undefined && self.componentConfig["success"]["emit"] != undefined) {
179+
matestackEventHub.$emit(self.componentConfig["success"]["emit"], response.data);
180+
}
181+
if (
182+
self.componentConfig["success"] != undefined &&
183+
self.componentConfig["success"]["transition"] != undefined &&
184+
(self.componentConfig["success"]["transition"]["follow_response"] == undefined ||
185+
self.componentConfig["success"]["transition"]["follow_response"] === false) &&
186+
self.$store != undefined
187+
) {
188+
let path = self.componentConfig["success"]["transition"]["path"];
189+
self.$store.dispatch("navigateTo", { url: path, backwards: false });
190+
return;
191+
}
192+
if (
193+
self.componentConfig["success"] != undefined &&
194+
self.componentConfig["success"]["transition"] != undefined &&
195+
self.componentConfig["success"]["transition"]["follow_response"] === true &&
196+
self.$store != undefined
197+
) {
198+
let path = response.data["transition_to"] || response.request.responseURL;
199+
self.$store.dispatch("navigateTo", { url: path, backwards: false });
200+
return;
201+
}
202+
if (self.shouldResetFormOnSuccessfulSubmit()) {
203+
self.setProps(self.data, null);
204+
self.initValues();
205+
}
206+
self.showInlineForm = false;
207+
})
208+
.catch(function (error) {
209+
if (error.response && error.response.data && error.response.data.errors) {
210+
self.errors = error.response.data.errors;
211+
}
212+
if (self.componentConfig["failure"] != undefined && self.componentConfig["failure"]["emit"] != undefined) {
213+
matestackEventHub.$emit(self.componentConfig["failure"]["emit"], error.response.data);
214+
}
215+
if (
216+
self.componentConfig["failure"] != undefined &&
217+
self.componentConfig["failure"]["transition"] != undefined &&
218+
self.$store != undefined
219+
) {
220+
let path = self.componentConfig["failure"]["transition"]["path"];
221+
self.$store.dispatch("navigateTo", { url: path, backwards: false });
222+
}
223+
});
224+
},
225+
},
226+
mounted: function () {
227+
this.initValues();
177228
},
178-
mounted: function(){
179-
this.initValues()
180-
}
181-
}
229+
};
182230

183-
let component = Vue.component('matestack-ui-core-form', componentDef)
231+
let component = Vue.component("matestack-ui-core-form", componentDef);
184232

185-
export default componentDef
233+
export default componentDef;

app/concepts/matestack/ui/core/form/form.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def setup
99
@component_config[:submit_path] = submit_path
1010
@component_config[:method] = options[:method]
1111
@component_config[:success] = options[:success]
12+
@component_config[:multipart] = options[:multipart] == true
1213
unless options[:success].nil?
1314
unless options[:success][:transition].nil?
1415
@component_config[:success][:transition][:path] = transition_path options[:success]

app/concepts/matestack/ui/core/form/input/input.haml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@
3333
step: options[:step],
3434
list: options[:list] }
3535

36+
- if [:file].include?(type)
37+
%input{ @tag_attributes,
38+
type: type,
39+
"@change": "inputChanged(\"#{attr_key}\"); filesAdded('#{attr_key}')",
40+
ref: "input.#{attr_key}",
41+
placeholder: placeholder,
42+
multiple: options[:multiple] }
43+
3644
%span{ class: "errors", "v-if": error_key }
3745
%span{ class: "error", "v-for": "error in #{error_key}" }
38-
{{ error }}
46+
{{ error }}

app/concepts/matestack/ui/core/form/input/input.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ def input_wrapper
4545

4646
def attr_key
4747
if input_wrapper.nil?
48-
return key.to_s
48+
return "#{key.to_s}#{'[]' if options[:multiple]}"
4949
else
50-
return "#{input_wrapper}.#{key.to_s}"
50+
return "#{input_wrapper}.#{key.to_s}#{'[]' if options[:multiple]}"
5151
end
5252
end
5353

0 commit comments

Comments
 (0)