apostrophe
Version:
The Apostrophe Content Management System.
145 lines (139 loc) • 3.59 kB
JavaScript
// Renders a single subform by configuration. Supports preview and edit modes.
// The component is not tied to a specific module and communicates with the
// parent component via events. See the `AposSettingsManager` component for
// an example of how to implement this component.
//
// ## Props
// `values` prop is the current value of the subform, which is an object.
// `subform` prop is the schema for the subform, which is an object.
// `errors` prop is an array or object as expected by the Editor Mixin.
// `busy` prop is a boolean indicating whether the subform (edit mode) is busy.
// `expanded` prop is a boolean indicating whether the subform is in preview
// (false) or edit (true) mode. `updateIndicator` prop is a boolean indicating
// whether the subform should display an updated indicator in preview mode.
import { klona } from 'klona';
import AposEditorMixin from 'Modules/@apostrophecms/modal/mixins/AposEditorMixin';
export default {
name: 'AposSubform',
mixins: [ AposEditorMixin ],
props: {
errors: {
type: Object,
default: null
},
subform: {
type: Object,
required: true
},
values: {
type: Object,
required: true
},
busy: {
type: Boolean,
default: false
},
expanded: {
type: Boolean,
default: true
},
updateIndicator: {
type: Boolean,
default: false
},
separator: {
type: Boolean,
default: false
}
},
emits: [ 'submit', 'cancel', 'update-expanded' ],
data() {
return {
docFields: {
data: {},
hasErrors: false
},
triggerValidation: false,
triggerHover: false,
scrollTimeout: null
};
},
computed: {
schema() {
return (this.subform.schema ?? []);
},
serverError() {
return this.error || !!this.serverErrors;
}
},
watch: {
values(newValues) {
this.docFields.data = klona(newValues);
},
errors(newErrors) {
this.serverErrors = newErrors;
},
expanded(newExpanded) {
this.triggerHover = false;
if (newExpanded) {
setTimeout(() => {
this.scrollIntoView();
}, 350);
}
}
},
// If we don't do this, we get stale initial values.
async mounted() {
await this.evaluateExternalConditions();
this.evaluateConditions();
this.docFields.data = klona(this.values);
},
beforeUnmount() {
clearTimeout(this.scrollTimeout);
},
methods: {
scrollIntoView() {
this.$el.scrollIntoView();
},
toggleExpanded() {
this.$emit('update-expanded', {
name: this.subform.name,
value: !this.expanded
});
},
updateDocFields(value) {
this.docFields = value;
this.evaluateConditions();
},
submit() {
this.triggerValidation = true;
this.$nextTick(() => {
if (this.docFields.hasErrors) {
this.triggerValidation = false;
return;
}
this.$emit('submit', {
name: this.subform.name,
values: this.docFields.data
});
});
},
async cancel() {
this.$emit('cancel', {
name: this.subform.name
});
this.toggleExpanded();
// Security: we don't want to leave any passwords in the DOM
this.$nextTick(() => {
this.clearPasswords();
});
},
clearPasswords() {
this.subform.schema
.filter(field => field.type === 'password')
.forEach(field => {
this.docFields.data[field.name] = '';
});
}
}
};