I'm trying to programmatically add an imported vue to a new Golden Layout pane. I've use the drag and drop example to build upon, but it is only adding HTML to the pane, not a vue that requires props passed to it, such as the pane width and height. https://golden-layout.com/tutorials/dynamically-adding-components.html
I've been trying to add it via the layout.registerComponent()
, with containter.on("open", funtion, ect.
but can't find:
As far as the docs say, on "open"
should fire after the new pane/container is created.
https://golden-layout.com/docs/Container.html#Events
However, as I've (erroneously?) implemented it the pane/container is not yet added when on "open"
The same vue is added to the default layout upon layout.init();
without an issue and works flawlessly.
But adding the same vue via the drag and drop method (as currently implemented) does not work and ends up with the following:
[Vue warn]: Cannot find element: #priceChart2
I've been doing my research and so far, can't figure out the solution.
import "./goldenlayout-base.css";
import "./goldenlayout-dark-theme.css";
// required for dynamic component instantiation from the config
import Vue from "vue";
import $ from "jquery";
import GoldenLayout from "./goldenlayout.js";
// import a price chart
import PriceChart from "src/components/TradingVueJs/Chart.vue"
let chartCnt = 1
export default {
name: "Trader",
props: {},
components: {},
computed: {},
data() {
return {};
},
mounted() {
var config = {
content: [
{
type: "column",
isClosable: true,
reorderEnabled: true,
title: "",
content: [
{
type: "stack",
width: 100,
height: 50,
isClosable: true,
reorderEnabled: true,
title: "",
activeItemIndex: 0,
content: [
{
type: "component",
componentName: "priceChart",
componentState: {
text: "Price Chart",
},
isClosable: true,
reorderEnabled: true,
title: "Chart",
id: "Chart1"
},
],
},
{
type: "stack",
header: {},
isClosable: true,
reorderEnabled: true,
title: "",
activeItemIndex: 0,
height: 50,
content: [
{
type: "component",
componentName: "example",
componentState: {
text: "Some message",
},
isClosable: false,
reorderEnabled: true,
title: "Example",
id: "Example",
},
],
},
],
},
],
};
var layout = new GoldenLayout(config, $("#layoutContainer"));
layout.registerComponent("example", function(container, state) {
container.getElement().html("<h2>" + state.text + "</h2>");
});
layout.registerComponent("priceChart", function(container, state) {
let id = "priceChart"+chartCnt++
container.on("open", () => {
container.getElement().html("<div id='"+id+"' class='chartComponent'></div>");
// mount price chart component
const chart = Vue.extend(PriceChart);
const Chart = new chart({
propsData: {
id: id,
width: container.width,
height: container.height
}
});
Chart.$mount('#'+id)
})
});
// Update GL on window resize
window.addEventListener("resize", () => {
layout.updateSize();
});
// attach the state change listener
layout.on("stateChanged", () => {
this.onLayoutStateChanged(layout.toConfig());
});
layout.init();
var addMenuItem = function(component, title, text) {
var element = $("<li>" + text + "</li>");
$("#menuContainer").append(element);
var newItemConfig = {
title: title,
type: "component",
componentName: component,
componentState: { text: text },
};
layout.createDragSource(element, newItemConfig);
};
addMenuItem("priceChart", "BTC/USDT", "Price Chart");
addMenuItem("example", "Add me!", "You've added me!");
addMenuItem("example", "Me too!", "You've added me too!");
},
methods: {
resetLayout() {
window.location.reload(true);
},
onLayoutStateChanged(state) {
var layoutState = JSON.stringify(state, null, 2);
// eslint-disable-next-line no-console
console.log("changed state", layoutState);
}
}
};
<template>
<!-- this is the golden-layout container where all the vue components will be contained -->
<div id="wrapper">
<ul id="menuContainer"></ul>
<div id="layoutContainer"></div>
</div>
</template>
In the end I had to use a setInterval()
to wait for the element to appear in the DOM.
I found a great solution here:
https://gist.github.com/chrisjhoughton/7890303#gistcomment-2638757
/**
* Wait for the specified element to appear in the DOM. When the element appears,
* provide it to the callback. If waiting times out, send null to the callback.
*
* @param selector a jQuery selector (eg, 'div.container img')
* @param callback function that takes selected element (null if timeout)
* @param maxtries number of times to try (return null after maxtries, false to disable, if 0 will still try once)
* @param interval ms wait between each try
*/
waitForEl(selector, callback, maxtries = false, interval = 100) {
const poller = setInterval(() => {
const el = jQuery(selector)
const retry = maxtries === false || maxtries-- > 0
if (retry && el.length < 1) return // will try again
clearInterval(poller)
callback(el || null)
}, interval)
}