import Vue from “vue/dist/vue.esm”; import Vuex from “vuex”; import VRuntimeTemplate from “v-runtime-template”
import axios from “axios”;
import matestackEventHub from “../../event_hub”; import componentMixin from “../mixin”;
const componentDef = {
mixins: [componentMixin], data: function () { return { data: {}, errors: {}, loading: false, nestedForms: {}, isNestedForm: false, hideNestedForm: false, nestedFormRuntimeTemplates: {}, nestedFormRuntimeTemplateDomElements: {}, deletedNestedForms: {}, nestedFormRuntimeId: "", nestedFormServerErrorIndex: "", }; }, methods: { initDataKey: function (key, initValue) { this.data[key] = initValue; }, updateFormValue: function (key, value) { this.data[key] = value; }, hasErrors: function(){ //https://stackoverflow.com/a/27709663/13886137 for (var key in this.errors) { if (this.errors[key] !== null && this.errors[key] != ""){ return true; } } return false; }, resetErrors: function (key) { if (this.errors[key]) { delete this.errors[key]; Vue.set(this.errors); } if (this.isNestedForm){ var serverErrorKey = this.props["fields_for"].replace("_attributes", "")+"["+this.nestedFormServerErrorIndex+"]."+key if (this.$parent.errors[serverErrorKey]) { delete this.$parent.errors[serverErrorKey]; Vue.set(this.$parent.errors); } } }, setErrors: function(errors){ this.errors = errors; }, setNestedFormServerErrorIndex: function(value){ this.nestedFormServerErrorIndex = value; }, setErrorKey: function(key, value){ Vue.set(this.errors, key, value); }, flushErrors: function(key, value){ this.errors = {}; }, setNestedFormsError: function(errors){ let self = this; Object.keys(errors).forEach(function(errorKey){ if (errorKey.includes(".")){ let childErrorKey = errorKey.split(".")[1] let childModelName = errorKey.split(".")[0].split("[")[0] let childModelIndex = errorKey.split(".")[0].split("[")[1].split("]")[0] let mappedChildModelIndex = self.mapToNestedForms(parseInt(childModelIndex), childModelName+"_attributes") self.nestedForms[childModelName+"_attributes"][mappedChildModelIndex].setNestedFormServerErrorIndex(parseInt(childModelIndex)) self.nestedForms[childModelName+"_attributes"][mappedChildModelIndex].setErrorKey(childErrorKey, errors[errorKey]) } }) }, mapToNestedForms: function(serverIndex, nestedFormKey){ var primaryKey; if(this.props["primary_key"] != undefined){ primaryKey = this.props["primary_key"]; }else{ primaryKey = "id"; } var formIdMap = [] var childModelKey = 0; while(this.data[nestedFormKey].length > childModelKey){ var ignore = this.data[nestedFormKey][childModelKey]["_destroy"] == true && this.data[nestedFormKey][childModelKey][primaryKey] == null if(!ignore){ formIdMap.push(childModelKey) } childModelKey++; } return formIdMap[serverIndex]; }, resetNestedForms: function(){ var self = this; Object.keys(self.nestedForms).forEach(function(childModelKey){ self.nestedForms[childModelKey].forEach(function(nestedFormInstance){ if(nestedFormInstance.data["_destroy"] == true){ var destroyed = true; } nestedFormInstance.initValues() Vue.set(nestedFormInstance.data) if(destroyed){ nestedFormInstance.hideNestedForm = true Vue.set(nestedFormInstance.data, "_destroy", true) } }) }) }, removeItem: function(){ Vue.set(this.data, "_destroy", true) this.hideNestedForm = true; var id = parseInt(this.nestedFormRuntimeId.replace("_"+this.props["fields_for"]+"_child_", "")); this.$parent.deletedNestedForms[this.props["fields_for"]].push(id); var serverErrorKey = this.props["fields_for"].replace("_attributes", "")+"["+this.nestedFormServerErrorIndex+"]." var self = this; Object.keys(self.$parent.errors).forEach(function(errorKey){ if (errorKey.lastIndexOf(serverErrorKey, 0) == 0) { delete self.$parent.errors[errorKey]; Vue.set(self.$parent.errors) } }); }, addItem: function(key){ var templateString = JSON.parse(this.$el.querySelector('#prototype-template-for-'+key).dataset[":template"]) if (this.nestedFormRuntimeTemplateDomElements[key] == null){ var dom_elem = document.createElement('div') dom_elem.innerHTML = templateString var existingItemsCount; if (this.nestedForms[key] == undefined){ existingItemsCount = 0 }else{ existingItemsCount = this.nestedForms[key].length } dom_elem.querySelector('.matestack-form-fields-for').id = key+"_child_"+existingItemsCount Vue.set(this.nestedFormRuntimeTemplateDomElements, key, dom_elem) Vue.set(this.nestedFormRuntimeTemplates, key, this.nestedFormRuntimeTemplateDomElements[key].outerHTML) }else{ var dom_elem = document.createElement('div') dom_elem.innerHTML = templateString var existingItemsCount = this.nestedForms[key].length dom_elem.querySelector('.matestack-form-fields-for').id = key+"_child_"+existingItemsCount this.nestedFormRuntimeTemplateDomElements[key].insertAdjacentHTML( 'beforeend', dom_elem.innerHTML ) Vue.set(this.nestedFormRuntimeTemplates, key, this.nestedFormRuntimeTemplateDomElements[key].outerHTML) } }, initValues: function () { let self = this; let data = {}; for (let key in self.$refs) { if (key.startsWith("input-component")) { self.$refs[key].initialize() } if (key.startsWith("textarea-component")) { self.$refs[key].initialize() } if (key.startsWith("select-component")) { self.$refs[key].initialize() } if (key.startsWith("radio-component")) { self.$refs[key].initialize() } if (key.startsWith("checkbox-component")) { self.$refs[key].initialize() } } }, shouldResetFormOnSuccessfulSubmit() { const self = this; if (self.props["success"] != undefined && self.props["success"]["reset"] != undefined) { return self.props["success"]["reset"]; } else { return self.shouldResetFormOnSuccessfulSubmitByDefault(); } }, shouldResetFormOnSuccessfulSubmitByDefault() { const self = this; if (self.props["method"] == "put") { return false; } else { return true; } }, perform: function(){ const self = this if (self.props["fields_for"] != null) { return; } var form = self.$el.tagName == 'FORM' ? self.$el : self.$el.querySelector('form'); if(form.checkValidity()){ self.loading = true; if (self.props["emit"] != undefined) { matestackEventHub.$emit(self.props["emit"]); } if (self.props["delay"] != undefined) { setTimeout(function () { self.sendRequest() }, parseInt(self.props["delay"])); } else { self.sendRequest() } } else { matestackEventHub.$emit('static_form_errors'); } }, transformToFormData: function (formData, dataNode, parentKey=null) { var self = this; for (let key in dataNode) { if (key.endsWith("[]")) { for (let i in dataNode[key]) { let file = dataNode[key][i]; if (parentKey != null) { formData.append(self.props["for"] + parentKey + "[" + key.slice(0, -2) + "][]", file); } else { formData.append(self.props["for"] + "[" + key.slice(0, -2) + "][]", file); } } } else { if (Array.isArray(dataNode[key])){ dataNode[key].forEach(function(item, index){ if (parentKey != null) { let _key = parentKey + "[" + key + "]" + "[]"; formData = self.transformToFormData(formData, item, _key) } else { let _key = "[" + key + "]" + "[]"; formData = self.transformToFormData(formData, item, _key) } }) } else { if (dataNode[key] != null){ if (parentKey != null) { formData.append(self.props["for"] + parentKey + "[" + key + "]", dataNode[key]); } else { formData.append(self.props["for"] + "[" + key + "]", dataNode[key]); } } } } } return formData; }, sendRequest: function(){ const self = this; let payload = {}; payload[self.props["for"]] = self.data; let axios_config = {}; if (self.props["multipart"] == true ) { let formData = new FormData(); formData = this.transformToFormData(formData, this.data) axios_config = { method: self.props["method"], url: self.props["submit_path"], data: formData, headers: { "X-CSRF-Token": document.getElementsByName("csrf-token")[0].getAttribute("content"), "Content-Type": "multipart/form-data", }, }; } else { axios_config = { method: self.props["method"], url: self.props["submit_path"], data: payload, headers: { "X-CSRF-Token": document.getElementsByName("csrf-token")[0].getAttribute("content"), "Content-Type": "application/json", }, }; } axios(axios_config) .then(function (response) { self.loading = false; if (self.props["success"] != undefined && self.props["success"]["emit"] != undefined) { matestackEventHub.$emit(self.props["success"]["emit"], response.data); } // transition handling if (self.props["success"] != undefined && self.props["success"]["transition"] != undefined && ( self.props["success"]["transition"]["follow_response"] == undefined || self.props["success"]["transition"]["follow_response"] === false ) && self.$store != undefined ) { let path = self.props["success"]["transition"]["path"] self.$store.dispatch('navigateTo', {url: path, backwards: false}) return; } if (self.props["success"] != undefined && self.props["success"]["transition"] != undefined && self.props["success"]["transition"]["follow_response"] === true && self.$store != undefined ) { let path = response.data["transition_to"] || response.request.responseURL self.$store.dispatch('navigateTo', {url: path, backwards: false}) return; } // redirect handling if (self.props["success"] != undefined && self.props["success"]["redirect"] != undefined && ( self.props["success"]["redirect"]["follow_response"] == undefined || self.props["success"]["redirect"]["follow_response"] === false ) && self.$store != undefined ) { let path = self.props["success"]["redirect"]["path"] window.location.href = path return; } if (self.props["success"] != undefined && self.props["success"]["redirect"] != undefined && self.props["success"]["redirect"]["follow_response"] === true && self.$store != undefined ) { let path = response.data["redirect_to"] || response.request.responseURL window.location.href = path return; } self.flushErrors(); if (self.shouldResetFormOnSuccessfulSubmit()) { self.initValues(); self.resetNestedForms(); } }) .catch(function (error) { self.loading = false; if (error.response && error.response.data && error.response.data.errors) { self.errors = error.response.data.errors; self.setErrors(error.response.data.errors); self.setNestedFormsError(error.response.data.errors); } if (self.props["failure"] != undefined && self.props["failure"]["emit"] != undefined) { matestackEventHub.$emit(self.props["failure"]["emit"], error.response.data); } // transition handling if (self.props["failure"] != undefined && self.props["failure"]["transition"] != undefined && ( self.props["failure"]["transition"]["follow_response"] == undefined || self.props["failure"]["transition"]["follow_response"] === false ) && self.$store != undefined ) { let path = self.props["failure"]["transition"]["path"] self.$store.dispatch('navigateTo', {url: path, backwards: false}) return; } if (self.props["failure"] != undefined && self.props["failure"]["transition"] != undefined && self.props["failure"]["transition"]["follow_response"] === true && self.$store != undefined ) { let path = error.response.data["transition_to"] || response.request.responseURL self.$store.dispatch('navigateTo', {url: path, backwards: false}) return; } // redirect handling if (self.props["failure"] != undefined && self.props["failure"]["redirect"] != undefined && ( self.props["failure"]["redirect"]["follow_response"] == undefined || self.props["failure"]["redirect"]["follow_response"] === false ) && self.$store != undefined ) { let path = self.props["failure"]["redirect"]["path"] window.location.href = path return; } if (self.props["failure"] != undefined && self.props["failure"]["redirect"] != undefined && self.props["failure"]["redirect"]["follow_response"] === true && self.$store != undefined ) { let path = error.response.data["redirect_to"] || response.request.responseURL window.location.href = path return; } }); }, }, mounted: function () { var self = this; if (this.props["fields_for"] != undefined) { this.isNestedForm = true; this.data = { "_destroy": false }; //initialize nestedForm data in parent form if required if(this.$parent.data[this.props["fields_for"]] == undefined){ this.$set(this.$parent.data, this.props["fields_for"], []); } if(this.$parent.nestedForms[this.props["fields_for"]] == undefined){ this.$set(this.$parent.nestedForms, this.props["fields_for"], []); } if(this.$parent.deletedNestedForms[this.props["fields_for"]] == undefined){ this.$set(this.$parent.deletedNestedForms, this.props["fields_for"], []); } var id = parseInt(self.$el.id.replace(this.props["fields_for"]+"_child_", "")); //setup data binding for serverside rendered nested forms if (isNaN(id)){ id = this.$parent.nestedForms[this.props["fields_for"]].length this.nestedFormRuntimeId = "_"+this.props["fields_for"]+"_child_"+id this.$el.id = this.props["fields_for"]+"_child_"+id this.initValues() this.$parent.data[this.props["fields_for"]].push(this.data); this.$parent.nestedForms[this.props["fields_for"]].push(this); } //setup data binding for runtime nested forms (dynamic add via v-runtime-template) if (!isNaN(id)){ this.nestedFormRuntimeId = "_"+this.props["fields_for"]+"_child_"+id if(this.$parent.data[this.props["fields_for"]][id] == undefined){ //new runtime form this.initValues() this.$parent.data[this.props["fields_for"]].push(this.data); this.$parent.nestedForms[this.props["fields_for"]].push(this); }else{ //retreive state for existing runtime form (after remount for example) this.data = this.$parent.data[this.props["fields_for"]][id] if (this.data["_destroy"] == true){ this.hideNestedForm = true; } this.$parent.nestedForms[this.props["fields_for"]][id] = this; Object.keys(this.$parent.errors).forEach(function(errorKey){ if (errorKey.includes(".")){ let childErrorKey = errorKey.split(".")[1] let childModelName = errorKey.split(".")[0].split("[")[0] let childModelIndex = errorKey.split(".")[0].split("[")[1].split("]")[0] let mappedChildModelIndex = self.$parent.mapToNestedForms(parseInt(childModelIndex), childModelName+"_attributes") if(childModelName+"_attributes" == self.props["fields_for"] && mappedChildModelIndex == id){ self.setNestedFormServerErrorIndex(parseInt(childModelIndex)) self.setErrorKey(childErrorKey, self.$parent.errors[errorKey]) } } }) } } } else { this.initValues(); } }, components: { VRuntimeTemplate: VRuntimeTemplate }
};
let component = Vue.component(“matestack-ui-core-form”, componentDef);
export default componentDef;