Live Data UI
Last modified by Manuel Leduc on 2026/03/24 10:19
Content
Reference
Provides the Vue components and methods needed to initialize a Live Data UI.
BaseDisplayer
A generic displayer that can be used as a base for implementing more specific displayers.
Props
- viewOnly: when true, disable the edit mode for the displayer, false otherwise
- isView: when true, set the displayer in view mode, when false in edit mode
- isLoading: when true, set the displayer in loading mode
- isEmpty: indicates that the content of the displayer is actually empty and not just a missing value (e.g., because of rights)
- interceptTouch: when false, disable the interception of touch events, in particular for handling links inside displayers. The default is true.
displayerMixin
A mixin providing base methods for displayers.
/code
/**
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
import DOMPurify from "dompurify";
/**
* The displayerMixin is a vue mixin containing all the needed
* props, computed values, methods, etc. for any custom displayer:
* `propertyId`, `entry`, `value`, `config`, `applyEdit()`, ...
* It should be included in every custom displayer component
*/
export default {
inject: ["logic"],
directives: {
// Only used by the date displayer.
onInserted: {
inserted(el, binding) {
const handler = binding.value;
if (!(handler instanceof Function)) {
console.warn(`Warning: v-on-inserted directive expects a function`);
return;
}
handler();
},
},
// This directive autofocus the element that has it
// This can be useful in order to autofocus the input in the Editor widget
// right after the user switched from the Viewer widget
autofocus: {
mounted(el) {
el.focus();
},
},
},
props: {
propertyId: String,
entry: Object,
},
// The computed values provide common data needed by displayers
computed: {
// The value to be displayed
value() {
return this.entry[this.propertyId];
},
safeValue() {
return this.sanitizeHtml(this.value);
},
// The property descriptor of `this.propertyId`
propertyDescriptor() {
return this.logic.getPropertyDescriptor(this.propertyId);
},
// The configuration (aka displayerDescriptor) of the displayer
config() {
return this.logic.getDisplayerDescriptor(this.propertyId);
},
// The whole Livedata data object
data() {
return this.logic.data;
},
// The base value uses the value provided in the props the initial value of the form input.
// Once the form is edited, `this.editedValue` is defined and is used instead.
// This is needed in order to have a initial value (this.value) computed by `displayerMixing`
// while being able to bind the edited value of the input tag in the template to a data
// attribute (editedValue) that will be updated at runtime without changing the initial
// `this.value`.
baseValue: {
get() {
return this.editedValue || this.value;
},
set(value) {
this.editedValue = value;
},
},
// Checks if the property value is allowed to be edited and if the livedata is in a state where
// the displayer can be edited.
isEditable() {
const editable = this.logic.isEditable({
entry: this.entry,
propertyId: this.propertyId,
});
// Checks that no other property is currently being edited.
const noOtherEditing = this.logic.getEditBus().isEditable();
return editable && noOtherEditing;
},
},
methods: {
/**
* Generic save operation.
*
* @param value - of a value is provided, it is used for saving the property, otherwise `this.editedValue` is used
*/
genericSave(value) {
const savedValue = value || this.editedValue;
this.logic
.getEditBus()
.save(this.entry, this.propertyId, { [this.propertyId]: savedValue });
},
sanitizeHtml(value) {
if (!this.logic.isContentTrusted()) {
// TODO: Take into account xml.htmlElementSanitizer properties when sanitizing (see
// XWIKI-20249).
return DOMPurify.sanitize(value);
} else {
return value;
}
},
sanitizeUrl(url, subtitute) {
// TODO: Take into account xml.htmlElementSanitizer properties when sanitizing (see
// XWIKI-20249).
if (
this.logic.isContentTrusted() ||
DOMPurify.isValidAttribute("a", "href", url || "")
) {
return url;
} else {
return subtitute || "#";
}
},
},
data() {
return {
editedValue: undefined,
};
},
watch: {
isView: function (newIsView) {
if (newIsView) {
// When we switch back to view mode, the edited value is reset.
this.editedValue = undefined;
}
},
},
};
XWikiIcon
An icon displayer Vue component.
Props
- iconDescriptor: The descriptor of an icon, see the icons API.
XWikiLivedata
The root component of a Live Data. This is the main entry point of the Live Data UI.
loadById
Utility method to load a requirejs module by its id, but returning a promise instead of a taking a callback method.
loadById(...ids): Promise<unknown>populateStore
Initialize the Component Store with the default displayers and layouts.