Last active
February 18, 2024 22:42
-
-
Save acetousk/ce1e25d66c984aab065192e8f4d42666 to your computer and use it in GitHub Desktop.
Copied vue and ftl components to allow overriding default behavior
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Subject: [PATCH] Copied vue and ftl components to allow overriding default behavior | |
--- | |
Index: data/SimpleScreensSetupData.xml | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
=================================================================== | |
diff --git a/data/SimpleScreensSetupData.xml b/data/SimpleScreensSetupData.xml | |
--- a/data/SimpleScreensSetupData.xml (revision 787bf9cf17e52f20b4eb4e69a03d3212dae7c493) | |
+++ b/data/SimpleScreensSetupData.xml (date 1708058009445) | |
@@ -73,6 +73,7 @@ | |
</#if> | |
]]></resourceValue> | |
</moqui.screen.ScreenThemeResource> | |
+ <moqui.screen.ScreenThemeResource screenThemeId="DEFAULT_QUASAR" sequenceNum="10" resourceTypeEnumId="STRT_SCRIPT_FOOTER" resourceValue="/ssstatic/lib/FancyDropDown.js"/> | |
<!-- /vapps (vuet) header plugins --> | |
<moqui.screen.ScreenThemeResource screenThemeId="DEFAULT" sequenceNum="130" resourceTypeEnumId="STRT_HEADER_NAVBAR_COMP" | |
Index: screen/SimpleScreens/Order/OrderDetail.xml | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
=================================================================== | |
diff --git a/screen/SimpleScreens/Order/OrderDetail.xml b/screen/SimpleScreens/Order/OrderDetail.xml | |
--- a/screen/SimpleScreens/Order/OrderDetail.xml (revision 787bf9cf17e52f20b4eb4e69a03d3212dae7c493) | |
+++ b/screen/SimpleScreens/Order/OrderDetail.xml (date 1708117634979) | |
@@ -15,6 +15,9 @@ | |
<screen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/xml-screen-3.xsd" | |
default-menu-title="Order Detail" default-menu-index="1"> | |
+ <!-- this is a test, and example, of overriding the default FTL macro template to use for this screen --> | |
+ <macro-template type="qvt" location="component://SimpleScreens/template/screen-macro/CustomScreenMacros.qvt.ftl"/> | |
+ | |
<parameter name="orderId" required="true"/> | |
<transition name="editParty"><default-response url="//${appRoot}/Party/EditParty"/></transition> | |
@@ -135,6 +138,12 @@ | |
<transition name="removeOrderItemReservations"><service-call name="mantle.product.AssetServices.remove#OrderItemReservations"/> | |
<default-response url="."/></transition> | |
+ <transition name="createProduct"><service-call name="create#mantle.product.Product" out-map="product"/> | |
+ <actions> | |
+ <script>ec.web.sendJsonResponse(product)</script> | |
+ </actions> | |
+ <default-response type="none"/></transition> | |
+ | |
<transition-include name="searchPartyList" location="component://SimpleScreens/template/party/PartyForms.xml"/> | |
<transition-include name="getProductList" location="component://SimpleScreens/template/product/ProductTransitions.xml"/> | |
<transition-include name="getProductPrice" location="component://SimpleScreens/template/product/ProductTransitions.xml"/> | |
@@ -1361,7 +1370,28 @@ | |
</field> | |
<field name="productId"> | |
<conditional-field condition="isProductItemType && orderPartInfo.partEditable"> | |
- <drop-down><dynamic-options transition="getProductList" server-search="true" min-length="0"/></drop-down> | |
+ <fancy-drop-down><no-option><container-dialog id="NewProductDialog" button-text="New Product"> | |
+ <form-single name="NewProductForm" transition="createProduct"> | |
+ <field name="orderId"><default-field><hidden/></default-field></field> | |
+ <field name="productName"><default-field><text-line/></default-field></field> | |
+ <field name="productTypeEnumId"><default-field title="Product Type"> | |
+ <widget-template-include location="component://webroot/template/screen/BasicWidgetTemplates.xml#enumDropDown"> | |
+ <set field="enumTypeId" value="ProductType"/></widget-template-include> | |
+ </default-field></field> | |
+ <field name="ownerPartyId"><default-field title="Owned By Org"><drop-down> | |
+ <option key="_NA_" text="N/A"/> | |
+ <entity-options key="${partyId}" text="PartyNameTemplate"> | |
+ <entity-find entity-name="mantle.party.PartyDetailAndRole"> | |
+ <econdition field-name="roleTypeId" value="OrgInternal"/> | |
+ <econdition field-name="disabled" value="N" or-null="true"/> | |
+ <order-by field-name="organizationName"/> | |
+ </entity-find> | |
+ </entity-options> | |
+ </drop-down></default-field></field> | |
+ <field name="submitButton"><default-field title="Create"><submit/></default-field></field> | |
+ </form-single> | |
+ </container-dialog> | |
+ </no-option><dynamic-options transition="getProductList" server-search="true" min-length="0"/></fancy-drop-down> | |
</conditional-field> | |
</field> | |
<field name="productLink" from="productId"><default-field title=""> | |
Index: template/screen-macro/CustomScreenMacros.qvt.ftl | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
=================================================================== | |
diff --git a/template/screen-macro/CustomScreenMacros.qvt.ftl b/template/screen-macro/CustomScreenMacros.qvt.ftl | |
new file mode 100644 | |
--- /dev/null (date 1708060295978) | |
+++ b/template/screen-macro/CustomScreenMacros.qvt.ftl (date 1708060295978) | |
@@ -0,0 +1,82 @@ | |
+<#-- | |
+This software is in the public domain under CC0 1.0 Universal plus a Grant of Patent License. | |
+ | |
+To the extent possible under law, the author(s) have dedicated all | |
+copyright and related and neighboring rights to this software to the | |
+public domain worldwide. This software is distributed without any | |
+warranty. | |
+ | |
+You should have received a copy of the CC0 Public Domain Dedication | |
+along with this software (see the LICENSE.md file). If not, see | |
+<http://creativecommons.org/publicdomain/zero/1.0/>. | |
+--> | |
+ | |
+<#include "runtime://template/screen-macro/DefaultScreenMacros.qvt.ftl"/> | |
+ | |
+<#macro "no-option"> | |
+ <#recurse/> | |
+</#macro> | |
+<#macro "fancy-drop-down"> | |
+ <#assign ddSubFieldNode = .node?parent> | |
+ <#assign ddFieldNode = ddSubFieldNode?parent> | |
+ <#assign tlId><@fieldId .node/></#assign> | |
+ <#assign allowMultiple = ec.getResource().expandNoL10n(.node["@allow-multiple"]!, "") == "true"> | |
+ <#assign allowEmpty = ec.getResource().expandNoL10n(.node["@allow-empty"]!, "") == "true"> | |
+ <#assign isDynamicOptions = .node["dynamic-options"]?has_content> | |
+ <#assign name><@fieldName .node/></#assign> | |
+ <#assign namePlain = ddFieldNode["@name"]> | |
+ <#assign options = sri.getFieldOptions(.node)> | |
+ <#assign currentValue = sri.getFieldValuePlainString(ddFieldNode, "")> | |
+ <#if !currentValue?has_content && .node["@no-current-selected-key"]?has_content> | |
+ <#assign currentValue = ec.getResource().expandNoL10n(.node["@no-current-selected-key"], "")></#if> | |
+ <#if currentValue?starts_with("[")><#assign currentValue = currentValue?substring(1, currentValue?length - 1)?replace(" ", "")></#if> | |
+ <#assign currentValueList = (currentValue?split(","))!> | |
+ <#if currentValueList?has_content><#if allowMultiple><#assign currentValue=""><#else><#assign currentValue = currentValueList[0]></#if></#if> | |
+ <#if !allowMultiple && !allowEmpty && !currentValue?has_content && options?has_content><#assign currentValue = options.keySet()?first></#if> | |
+ <#-- for server-side dynamic options/etc if no currentValue get first in options and set in context so fields rendering after this have it available --> | |
+ <#if currentValue?has_content && !ec.context.get(namePlain)?has_content && _formMap?exists && !_formMap.get(namePlain)?has_content> | |
+ <#assign _void = _formMap.put(ddFieldNode["@name"], currentValue)!></#if> | |
+ <#assign currentDescription = (options.get(currentValue))!> | |
+ <#assign validationClasses = formInstance.getFieldValidationClasses(ddSubFieldNode)> | |
+ <#assign optionsHasCurrent = currentDescription?has_content> | |
+ <#if !optionsHasCurrent && .node["@current-description"]?has_content> | |
+ <#assign currentDescription = ec.getResource().expand(.node["@current-description"], "")></#if> | |
+ <#if isDynamicOptions> | |
+ <#assign doNode = .node["dynamic-options"][0]> | |
+ <#assign depNodeList = doNode["depends-on"]> | |
+ <#assign doUrlInfo = sri.makeUrlByType(doNode["@transition"], "transition", doNode, "false")> | |
+ <#assign doUrlParameterMap = doUrlInfo.getParameterMap()> | |
+ <#if currentValue?has_content && !currentDescription?has_content><#assign currentDescription><@widgetTextValue .node true/></#assign></#if> | |
+ </#if> | |
+ <#assign fieldLabel><@fieldTitle ddSubFieldNode/></#assign> | |
+ <fancy-drop-down name="${name}" id="${tlId}"<#if fieldLabel?has_content> label="${fieldLabel}"</#if><#if formDisabled!> disable</#if><#rt> | |
+ <#t> class="<#if isDynamicOptions>dynamic-options</#if><#if .node["@style"]?has_content> ${ec.getResource().expandNoL10n(.node["@style"], "")}</#if><#if validationClasses?has_content> ${validationClasses}</#if>"<#rt> | |
+ <#t><#if fieldsJsName?has_content> v-model="${fieldsJsName}.${name}" :fields="${fieldsJsName}"<#else><#if allowMultiple> :value="[<#list currentValueList as curVal><#if curVal?has_content>'${curVal}',</#if></#list>]"<#else> value="${currentValue!}"</#if></#if> | |
+ <#t><#if allowMultiple> :multiple="true"</#if><#if allowEmpty> :allow-empty="true"</#if><#if .node["@combo-box"]! == "true"> :combo="true"</#if> | |
+ <#t><#if .node["@required-manual-select"]! == "true"> :required-manual-select="true"</#if> | |
+ <#t><#if .node["@submit-on-select"]! == "true"> :submit-on-select="true"</#if> | |
+ <#t> :bg-color="formProps.fieldChanged('${name}')?'blue-1':''" | |
+ <#t><#if .node?parent["@tooltip"]?has_content> tooltip="${ec.getResource().expand(.node?parent["@tooltip"], "")}"</#if> | |
+ <#t><#if ownerForm?has_content> form="${ownerForm}"</#if><#if .node["@size"]?has_content> size="${.node["@size"]}"</#if> | |
+ <#if isDynamicOptions> options-url="${doUrlInfo.url}" value-field="${doNode["@value-field"]!"value"}" label-field="${doNode["@label-field"]!"label"}"<#if doNode["@depends-optional"]! == "true"> :depends-optional="true"</#if> | |
+ <#t> :depends-on="{<#list depNodeList as depNode><#local depNodeField = depNode["@field"]>'${depNode["@parameter"]!depNodeField}':'${depNodeField}'<#sep>, </#list>}" | |
+ <#t> :options-parameters="{<#list doUrlParameterMap.keySet() as parameterKey><#if doUrlParameterMap.get(parameterKey)?has_content>'${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(parameterKey)}':'${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(doUrlParameterMap.get(parameterKey))}', </#if></#list>}" | |
+ <#t><#if doNode["@server-search"]! == "true"> :server-search="true"</#if><#if doNode["@delay"]?has_content> :server-delay="${doNode["@delay"]}"</#if> | |
+ <#t><#if doNode["@min-length"]?has_content> :server-min-length="${doNode["@min-length"]}"</#if> | |
+ <#t><#if (.node?children?size > 1)> :options-load-init="true"</#if> | |
+ </#if> | |
+ :options="[<#if currentValue?has_content && !allowMultiple && !optionsHasCurrent>{value:'${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(currentValue)}',label:'<#if currentDescription?has_content>${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(currentDescription!)}<#else>${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(currentValue)}</#if>'},</#if><#rt> | |
+ <#t><#list (options.keySet())! as key>{value:'<#if key?has_content>${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(key)}</#if>',label:'${Static["org.moqui.util.WebUtilities"].encodeHtmlJsSafe(options.get(key)!)}'}<#sep>,</#list>]" | |
+ <#lt>> | |
+ | |
+ <template v-slot:no-option><#visit .node["no-option"][0]/></template> | |
+ <#-- support <#if .node["@current"]! == "first-in-list"> again? --> | |
+ <#if ec.getResource().expandNoL10n(.node["@show-not"]!, "") == "true"> | |
+ <template v-slot:after> | |
+ <q-field dense outlined :bg-color="formProps.fieldChanged('${name}_not')?'blue-1':''"> | |
+ <#t><q-checkbox size="xs" name="${name}_not" label="${ec.getL10n().localize("Not")}"<#if ownerForm?has_content> form="${ownerForm}"</#if><#rt> | |
+ <#t><#if fieldsJsName?has_content> true-value="Y" false-value="N" v-model="${fieldsJsName}.${name}_not"<#else> value="Y"<#if ec.getWeb().parameters.get(name + "_not")! == "Y"> checked="checked"</#if></#if>></q-checkbox></q-field> | |
+ </template> | |
+ </#if> | |
+ </fancy-drop-down> | |
+</#macro> | |
Index: screen/ssstatic/lib/FancyDropDown.js | |
IDEA additional info: | |
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP | |
<+>UTF-8 | |
=================================================================== | |
diff --git a/screen/ssstatic/lib/FancyDropDown.js b/screen/ssstatic/lib/FancyDropDown.js | |
new file mode 100644 | |
--- /dev/null (date 1708064291106) | |
+++ b/screen/ssstatic/lib/FancyDropDown.js (date 1708064291106) | |
@@ -0,0 +1,287 @@ | |
+Vue.component('fancy-drop-down', { | |
+ name: "fancyDropDown", | |
+ props: { value:[Array,String], options:{type:Array,'default':function(){return [];}}, combo:Boolean, | |
+ allowEmpty:Boolean, multiple:Boolean, requiredManualSelect:Boolean, submitOnSelect:Boolean, | |
+ optionsUrl:String, optionsParameters:Object, optionsLoadInit:Boolean, | |
+ serverSearch:Boolean, serverDelay:{type:Number,'default':300}, serverMinLength:{type:Number,'default':1}, | |
+ labelField:{type:String,'default':'label'}, valueField:{type:String,'default':'value'}, | |
+ dependsOn:Object, dependsOptional:Boolean, form:String, fields:{type:Object}, | |
+ tooltip:String, label:String, name:String, id:String, disable:Boolean, bgColor:String, onSelectGoTo:String }, | |
+ data: function() { return { curOptions:this.options, allOptions:this.options, lastVal:null, lastSearch:null, loading:false } }, | |
+ template: | |
+ // was: ':fill-input="!multiple" hide-selected' changed to ':hide-selected="multiple"' to show selected to the left of input, | |
+ // fixes issues with fill-input where set values would sometimes not be displayed | |
+ '<q-select ref="qSelect" v-bind:value="value" v-on:input="handleInput($event)"' + | |
+ ' dense outlined options-dense use-input :hide-selected="multiple" :name="name" :id="id" :form="form"' + | |
+ ' input-debounce="500" @filter="filterFn" :clearable="allowEmpty||multiple" :disable="disable"' + | |
+ ' :multiple="multiple" :emit-value="!onSelectGoTo" map-options behavior="menu"' + | |
+ ' :rules="[val => allowEmpty||multiple||val===\'\'||(val&&val.length)||\'Please select an option\']"' + | |
+ ' stack-label :label="label" :loading="loading" :bg-color="bgColor" :options="curOptions">' + | |
+ '<q-tooltip v-if="tooltip">{{tooltip}}</q-tooltip>' + | |
+ '<template v-slot:no-option><slot name="no-option"><q-item><q-item-section class="text-grey">No results fancy!</q-item-section></q-item></slot></template>' + | |
+ '<template v-if="multiple" v-slot:prepend><div>' + | |
+ '<q-chip v-for="valueEntry in value" :key="valueEntry" dense size="md" class="q-my-xs" removable @remove="removeValue(valueEntry)">{{optionLabel(valueEntry)}}</q-chip>' + | |
+ '</div></template>' + | |
+ '<template v-slot:append><slot name="append"></slot></template>' + | |
+ '<template v-slot:after>' + | |
+ '<slot name="after"></slot>' + | |
+ '</template>' + | |
+ '</q-select>', | |
+ // TODO: how to add before slot pass through without the small left margin when nothing in the slot? <template v-slot:before><slot name="before"></slot></template> | |
+ methods: { | |
+ handleInput: function($event) { | |
+ // console.warn(this.onSelectGoTo + ": " + JSON.stringify($event)); | |
+ if (this.onSelectGoTo && this.onSelectGoTo.length) { | |
+ if ($event[this.onSelectGoTo]) this.$root.setUrl($event[this.onSelectGoTo]); | |
+ } else { | |
+ this.$emit('input', $event); | |
+ } | |
+ if (this.submitOnSelect) { | |
+ var vm = this; | |
+ // this doesn't work, even alternative of custom-event with event handler in DefaultScreenMacros.qvt.ftl in the drop-down macro that explicitly calls the q-form submit() method: vm.$nextTick(function() { console.log("emitting submit"); vm.$emit('submit'); }); | |
+ // doesn't work, q-form submit() method blows up without an even, in spite of what docs say: vm.$nextTick(function() { console.log("calling parent submit"); console.log(vm.$parent); vm.$parent.submit(); }); | |
+ // doesn't work, q-form submit() doesn't like this event for whatever reason, method missing on it: vm.$nextTick(function() { console.log("calling parent submit with event"); console.log(vm.$parent); vm.$parent.submit($event); }); | |
+ | |
+ // TODO: find a better approach, perhaps pass down a reference to m-form or something so can refer to it more explicitly and handle Vue components in between | |
+ // TODO: if found a better approach change the removeValue method below | |
+ // this assumes the grandparent is m-form, if not it will blow up... alternatives are tricky | |
+ vm.$nextTick(function() { vm.$parent.$parent.submitForm(); }); | |
+ } | |
+ }, | |
+ filterFn: function(search, doneFn, abortFn) { | |
+ if (this.serverSearch) { | |
+ if ((this.lastSearch === search) || (this.serverMinLength && ((search ? search.length : 0) < this.serverMinLength))) { | |
+ doneFn(); | |
+ } else { | |
+ this.lastSearch = search; | |
+ this.populateFromUrl({term:search}, doneFn, abortFn); | |
+ } | |
+ } else if (this.allOptions && this.allOptions.length) { | |
+ var vm = this; | |
+ if (search && search.length) { | |
+ doneFn(function() { | |
+ var needle = search.toLowerCase(); | |
+ vm.curOptions = vm.allOptions.filter(function (v) { | |
+ return v.label && v.label.toLowerCase().indexOf(needle) > -1; | |
+ }); | |
+ }); | |
+ } else { | |
+ if ((vm.curOptions ? vm.curOptions.length : 0) === (vm.allOptions ? vm.allOptions.length : 0)) { | |
+ doneFn(); | |
+ } else { | |
+ doneFn(function() { vm.curOptions = vm.allOptions; }); | |
+ } | |
+ } | |
+ } else if (this.optionsUrl && this.optionsUrl.length) { | |
+ // no current options, get from server | |
+ this.populateFromUrl({}, doneFn, abortFn); | |
+ } else { | |
+ console.error("m-drop-down " + this.name + " has no options and no options-url"); | |
+ abortFn(); | |
+ } | |
+ }, | |
+ processOptionList: function(list, page, term) { | |
+ var newData = []; | |
+ var labelField = this.labelField; | |
+ var valueField = this.valueField; | |
+ $.each(list, function(idx, curObj) { | |
+ var valueVal = curObj[valueField]; | |
+ var labelVal = curObj[labelField]; | |
+ newData.push(Object.assign(curObj, { value:valueVal||labelVal, label:labelVal||valueVal })); | |
+ }); | |
+ return newData; | |
+ }, | |
+ serverData: function(params) { | |
+ var hasAllParms = true; | |
+ var dependsOnMap = this.dependsOn; | |
+ var parmMap = this.optionsParameters; | |
+ var reqData = { moquiSessionToken: this.$root.moquiSessionToken }; | |
+ for (var parmName in parmMap) { if (parmMap.hasOwnProperty(parmName)) reqData[parmName] = parmMap[parmName]; } | |
+ for (var doParm in dependsOnMap) { if (dependsOnMap.hasOwnProperty(doParm)) { | |
+ var doValue; | |
+ if (this.fields) { | |
+ doValue = this.fields[dependsOnMap[doParm]]; | |
+ } else { | |
+ var doParmJqEl = $('#' + dependsOnMap[doParm]); | |
+ doValue = doParmJqEl.val(); | |
+ if (!doValue) doValue = doParmJqEl.find('select').val(); | |
+ } | |
+ // console.warn("do " + doParm + ":" + dependsOnMap[doParm] + " val " + doValue); | |
+ if (!doValue) { hasAllParms = false; } else { reqData[doParm] = doValue; } | |
+ }} | |
+ if (params) { reqData.term = params.term || ''; reqData.pageIndex = (params.page || 1) - 1; } | |
+ else if (this.serverSearch) { reqData.term = ''; reqData.pageIndex = 0; } | |
+ reqData.hasAllParms = hasAllParms; | |
+ return reqData; | |
+ }, | |
+ processResponse: function(data, params) { | |
+ if (moqui.isArray(data)) { | |
+ return { results:this.processOptionList(data, null, params.term) }; | |
+ } else { | |
+ params.page = params.page || 1; // NOTE: 1 based index, is 0 based on server side | |
+ var pageSize = data.pageSize || 20; | |
+ return { results: this.processOptionList(data.options, params.page, params.term), | |
+ pagination: { more: (data.count ? (params.page * pageSize) < data.count : false) } }; | |
+ } | |
+ }, | |
+ populateFromUrl: function(params, doneFn, abortFn) { | |
+ var reqData = this.serverData(params); | |
+ // console.log("populateFromUrl 1 " + this.optionsUrl + " reqData.hasAllParms " + reqData.hasAllParms + " dependsOptional " + this.dependsOptional); | |
+ // console.log(reqData); | |
+ if (!this.optionsUrl || !this.optionsUrl.length) { | |
+ console.warn("In m-drop-down tried to populateFromUrl but no optionsUrl"); | |
+ if (abortFn) abortFn(); | |
+ return; | |
+ } | |
+ if (!reqData.hasAllParms && !this.dependsOptional) { | |
+ console.warn("In m-drop-down tried to populateFromUrl but not hasAllParms and not dependsOptional"); | |
+ this.curOptions = []; | |
+ this.allOptions = []; | |
+ if (abortFn) abortFn(); | |
+ return; | |
+ } | |
+ var vm = this; | |
+ this.loading = true; | |
+ $.ajax({ type:"POST", url:this.optionsUrl, data:reqData, dataType:"json", headers:{Accept:'application/json'}, | |
+ error:function(jqXHR, textStatus, errorThrown) { | |
+ vm.loading = false; | |
+ moqui.handleAjaxError(jqXHR, textStatus, errorThrown); | |
+ if (abortFn) abortFn(); | |
+ }, | |
+ success: function(data) { | |
+ vm.loading = false; | |
+ var list = moqui.isArray(data) ? data : data.options; | |
+ var procList = vm.processOptionList(list, null, (params ? params.term : null)); | |
+ if (list) { | |
+ if (doneFn) { | |
+ doneFn(function() { | |
+ vm.setNewOptions(procList); | |
+ }); | |
+ } else { | |
+ vm.setNewOptions(procList); | |
+ if (vm.$refs.qSelect) vm.$refs.qSelect.refresh(); | |
+ // tried this for some drop-downs getting value set and have options but not showing current value's label, didn't work: if (vm.$refs.qSelect) vm.$nextTick(function() { vm.$refs.qSelect.refresh(); }); | |
+ // NOTE: don't want to do this, was mistakenly used before, use only if setting the input value string to an explicit value otherwise clears it and calls filter again: vm.$refs.qSelect.updateInputValue(); | |
+ } | |
+ } | |
+ } | |
+ }); | |
+ }, | |
+ setNewOptions: function(options) { | |
+ this.curOptions = options; | |
+ if (this.multiple && this.allOptions && this.allOptions.length && this.value && this.value.length && moqui.isArray(this.value)) { | |
+ // for multiple retain current value(s) in allOptions, at end of Array, so that in most cases already selected values are retained | |
+ var newAllOptions = options.slice(); | |
+ for (var vi = 0; vi < this.value.length; vi++) { | |
+ var curValue = this.value[vi]; | |
+ for (var oi = 0; oi < this.allOptions.length; oi++) { | |
+ var curOption = this.allOptions[oi]; | |
+ if (curValue === curOption.value) newAllOptions.push(curOption); | |
+ } | |
+ } | |
+ this.allOptions = newAllOptions; | |
+ } else { | |
+ this.allOptions = options; | |
+ this.checkCurrentValue(this.allOptions); | |
+ } | |
+ }, | |
+ checkCurrentValue: function(options) { | |
+ // if cur value not in new options either clear it or set it to the new first option in list if !allowEmpty | |
+ var isInNewOptions = false; | |
+ var valIsArray = moqui.isArray(this.value); | |
+ if (this.value && this.value.length && options) for (var i=0; i<options.length; i++) { | |
+ var curObj = options[i]; | |
+ // console.warn("option val " + curObj.value + " cur value " + JSON.stringify(this.value) + " valIsArray " + valIsArray + " is in value " + (valIsArray ? this.value.includes(curObj.value) : curObj.value === this.value)); | |
+ if (valIsArray ? this.value.includes(curObj.value) : curObj.value === this.value) { | |
+ isInNewOptions = true; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ // console.warn("curOptions updated " + this.name + " allowEmpty " + this.allowEmpty + " value '" + this.value + "' " + " isInNewOptions " + isInNewOptions + ": " + JSON.stringify(options)); | |
+ if (!isInNewOptions) { | |
+ if (!this.allowEmpty && !this.multiple && options && options.length && options[0].value && (!this.requiredManualSelect || (!this.submitOnSelect && options.length === 1))) { | |
+ // simulate normal select behavior with no empty option (not allowEmpty) where first value is selected by default | |
+ // console.warn("checkCurrentValue setting " + this.name + " to " + options[0].value + " options " + options.length); | |
+ this.$emit('input', options[0].value); | |
+ } else { | |
+ // console.warn("setting " + this.name + " to null"); | |
+ this.$emit('input', null); | |
+ } | |
+ } | |
+ }, | |
+ optionLabel: function(value) { | |
+ var options = this.allOptions; | |
+ if (!options || !options.length) return ""; | |
+ for (var i=0; i < options.length; i++) { | |
+ var curOption = options[i]; | |
+ if (value === curOption.value) return curOption.label; | |
+ } | |
+ return ""; | |
+ }, | |
+ removeValue: function(value) { | |
+ var curValueArr = this.value; | |
+ if (!moqui.isArray(curValueArr)) { console.warn("Tried to remove value from m-drop-down multiple " + this.name + " but value is not an Array"); return; } | |
+ var newValueArr = []; | |
+ for (var i = 0; i < curValueArr.length; i++) { | |
+ var valueEntry = curValueArr[i]; | |
+ if (valueEntry !== value) newValueArr.push(valueEntry); | |
+ } | |
+ if (curValueArr.length !== newValueArr.length) this.$emit('input', newValueArr); | |
+ // copied from handleInput method above | |
+ if (this.submitOnSelect) { | |
+ var vm = this; | |
+ // this doesn't work, even alternative of custom-event with event handler in DefaultScreenMacros.qvt.ftl in the drop-down macro that explicitly calls the q-form submit() method: vm.$nextTick(function() { console.log("emitting submit"); vm.$emit('submit'); }); | |
+ // doesn't work, q-form submit() method blows up without an even, in spite of what docs say: vm.$nextTick(function() { console.log("calling parent submit"); console.log(vm.$parent); vm.$parent.submit(); }); | |
+ // doesn't work, q-form submit() doesn't like this event for whatever reason, method missing on it: vm.$nextTick(function() { console.log("calling parent submit with event"); console.log(vm.$parent); vm.$parent.submit($event); }); | |
+ | |
+ // TODO: find a better approach, perhaps pass down a reference to m-form or something so can refer to it more explicitly and handle Vue components in between | |
+ // TODO: if found a better approach change the handleInput method above | |
+ // this assumes the grandparent is m-form, if not it will blow up... alternatives are tricky | |
+ vm.$nextTick(function() { vm.$parent.$parent.submitForm(); }); | |
+ } | |
+ }, | |
+ clearAll: function() { this.$emit('input', null); } | |
+ }, | |
+ mounted: function() { | |
+ // TODO: handle combo somehow: if (this.combo) { opts.tags = true; opts.tokenSeparators = [',',' ']; } | |
+ | |
+ if (this.serverSearch) { | |
+ if (!this.optionsUrl) console.error("m-drop-down in form " + this.form + " has no options-url but has server-search=true"); | |
+ } | |
+ if (this.optionsUrl && this.optionsUrl.length) { | |
+ var dependsOnMap = this.dependsOn; | |
+ for (var doParm in dependsOnMap) { if (dependsOnMap.hasOwnProperty(doParm)) { | |
+ if (this.fields) { | |
+ var vm = this; | |
+ this.$watch('fields.' + dependsOnMap[doParm], function() { | |
+ // in the case of dependency change clear current value | |
+ vm.$emit('input', null); | |
+ vm.populateFromUrl({term:vm.lastSearch}); | |
+ }); | |
+ } else { | |
+ // TODO: if no fields passed, use some sort of DOM-based value like jQuery val()? | |
+ } | |
+ } } | |
+ // do initial populate if not a serverSearch or for serverSearch if we have an initial value do the search so we don't display the ID | |
+ if (this.optionsLoadInit) { | |
+ if (!this.serverSearch) { this.populateFromUrl(); } | |
+ else if (this.value && this.value.length && moqui.isString(this.value)) { this.populateFromUrl({term:this.value}); } | |
+ } | |
+ } | |
+ // simulate normal select behavior with no empty option (not allowEmpty) where first value is selected by default - but only do for 1 option to force user to think and choose from multiple | |
+ if (!this.multiple && !this.allowEmpty && (!this.value || !this.value.length) && this.options && this.options.length && (!this.requiredManualSelect || (!this.submitOnSelect && options.length === 1))) { | |
+ this.$emit('input', this.options[0].value); | |
+ } | |
+ } | |
+ /* probably don't need, remove sometime: | |
+ watch: { | |
+ // need to watch for change to options prop? options: function(options) { this.curOptions = options; }, | |
+ curOptionsFoo: function(options) { | |
+ // save the lastVal if there is one to remember what was selected even if new options don't have it, just in case options change again | |
+ if (this.value && this.value.length) this.lastVal = this.value; | |
+ | |
+ } | |
+ } | |
+ */ | |
+}); | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment