Mercurial > gemma
view client/src/components/fairway/Fairwayprofile.vue @ 5475:791a372553a0
Client:FWA: Fix setting request parameters and legend
* Set default limit-width values (80m, 150m) for bottlenecks that limited
by width
* Fix condition of generating the legend
* Fix setting the boundaries of the legend
* Convert limit-width values to "cm" for the request.
author | Fadi Abbud <fadi.abbud@intevation.de> |
---|---|
date | Mon, 16 Aug 2021 15:55:57 +0200 |
parents | de86a96d55c3 |
children | 3b842e951317 |
line wrap: on
line source
<template> <div class="d-flex flex-column flex-fill"> <UIBoxHeader icon="chart-area" :title="title" :closeCallback="close" /> <div class="d-flex flex-fill" v-if="openLayersMap()"> <DiagramLegend> <div class="legend"> <span style="background-color: #5995ff; width: 20px; height: 20px;" ></span> <span class="fix-trans-space" style="display:inline;" v-translate >Water</span > </div> <div class="legend"> <span :style=" 'width: 16px; height: 16px; background:' + this.getLayerStyle(1).fillColor + '; border: dotted 2px ' + this.getLayerStyle(1).strokeColor + '; background-clip: padding-box; box-sizing: content-box;' " ></span> <span class="fix-trans-space" style="display:inline;" v-translate >Fairway (LOS 1)</span > </div> <div class="legend"> <span :style=" 'width: 16px; height: 16px; background:' + this.getLayerStyle(2).fillColor + '; border: dashed 2px ' + this.getLayerStyle(2).strokeColor + '; background-clip: padding-box; box-sizing: content-box;' " ></span> <span class="fix-trans-space" style="display:inline;" v-translate >Fairway (LOS 2)</span > </div> <div class="legend"> <span :style=" 'width: 16px; height: 16px; background:' + this.getLayerStyle(3).fillColor + '; border: solid 2px ' + this.getLayerStyle(3).strokeColor + '; background-clip: padding-box; box-sizing: content-box;' " ></span> <span class="fix-trans-space" style="display:inline;" v-translate >Fairway (LOS 3)</span > </div> <div class="legend"> <span style="width: 14px; height: 14px; background-color: #4a2f06; border: solid 3px black; background-clip: padding-box; box-sizing: content-box;" ></span> <span class="fix-trans-space" style="display:inline;" v-translate >Sediment</span > </div> <div class="legend"> <span style="width: 14px; height: 14px; background-color: rgba(74, 47, 6, 0.6); border: solid 3px #943007; background-clip: padding-box; box-sizing: content-box;" ></span> <span class="fix-trans-space" style="display:inline;" v-translate >Sediment (Compare)</span > </div> <div> <select v-model="form.template" @change="applyChange" class="form-control d-block custom-select-sm w-100" > <option v-for="template in templates" :value="template" :key="template.name" > {{ template.name }} </option> </select> <button @click="downloadPDF" type="button" class="btn btn-sm btn-info d-block w-100 mt-2" > <translate>Export to PDF</translate> </button> <a @click="downloadImage('fairwaypng', title)" id="fairwaypng" class="btn btn-sm btn-info text-white d-block w-100 mt-2" :download="`${fileName}.png`" > <translate>Export as Image</translate> </a> </div> </DiagramLegend> <div id="pdfContainer" class="d-flex flex-fill justify-content-center align-items-center diagram-container position-relative" > <div class="direction-indicator"></div> <div v-if="!fairwayData"> <translate>No data available.</translate> </div> <div v-if="!refWaterlevelValid"> <translate>No valid reference waterlevel data available.</translate> </div> <div v-if="!waterlevelValid"> <translate>No valid waterlevel data available.</translate> </div> </div> </div> </div> </template> <style lang="sass" scoped> .direction-indicator width: 70px height: 0 border-top: dashed 2px #333 position: absolute bottom: 50px left: 115px margin-left: -35px &::after content: "" width: 0 height: 0 border-width: 10px border-top-width: 5px border-bottom-width: 5px border-style: solid border-color: transparent border-left-color: #333 position: absolute right: -20px top: -6px </style> <script> /* This is Free Software under GNU Affero General Public License v >= 3.0 * without warranty, see README.md and license for details. * * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * * Copyright (C) 2018, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Thomas Junk <thomas.junk@intevation.de> * Markus Kottländer <markus.kottlaender@intevation.de> * Fadi Abbud <fadi.abbud@intevation.de> */ import * as d3 from "d3"; import { mapState, mapGetters } from "vuex"; import debounce from "debounce"; import { diagram, pdfgen, templateLoader } from "@/lib/mixins"; import { HTTP } from "@/lib/http"; import { displayError } from "@/lib/errors"; import { defaultDiagramTemplate } from "@/lib/DefaultDiagramTemplate"; const GROUND_COLOR = "#4A2F06"; const WATER_COLOR = "#005DFF"; const isNumber = value => { if (typeof value !== "number") { return false; } if (value !== Number(value)) { return false; } if (Number.isFinite(value) === false) { return false; } return true; }; export default { mixins: [diagram, pdfgen, templateLoader], name: "fairwayprofile", components: { DiagramLegend: () => import("@/components/DiagramLegend") }, data() { return { resizeListenerFunction: null, width: null, height: null, form: { template: null }, templates: [], defaultTemplate: defaultDiagramTemplate, pdf: { doc: null, width: 32, height: 297 }, templateData: null }; }, computed: { ...mapGetters("map", ["openLayersMap"]), ...mapGetters("fairwayprofile", ["totalLength"]), ...mapState("fairwayprofile", [ "additionalSurvey", "currentProfile", "startPoint", "endPoint", "fairwayData", "minAlt", "maxAlt", "selectedWaterLevel", "depth", "useCustomDepth" ]), ...mapState("bottlenecks", ["selectedSurvey", "selectedBottleneck"]), ...mapState("application", ["paneSetup"]), title() { let dates = [this.selectedSurvey.date_info]; let waterlevelLabel = this.selectedWaterLevel === "ref" ? this.selectedSurvey.depth_reference : "Current"; if (this.additionalSurvey) dates.push(this.additionalSurvey.date_info); dates.map(d => this.$options.filters.dateTime(d, true)); const waterlevelMeasurement = this.waterlevelValid && this.refWaterlevelValid ? `${this.$options.filters.waterlevel(this.waterlevel)} m` : this.$gettext("No valid value available"); return `${this.$gettext("Fairwayprofile")}: ${ this.selectedBottleneck } (${dates.join( ", " )}) WL: ${waterlevelLabel} ( ${waterlevelMeasurement} )`; }, currentData() { if ( !this.selectedSurvey || !this.currentProfile.hasOwnProperty(this.selectedSurvey.date_info) ) return []; return this.currentProfile[this.selectedSurvey.date_info].points; }, additionalData() { if ( !this.additionalSurvey || !this.currentProfile.hasOwnProperty(this.additionalSurvey.date_info) ) return []; return this.currentProfile[this.additionalSurvey.date_info].points; }, bottleneck() { return this.openLayersMap() .getLayer("BOTTLENECKS") .getSource() .getFeatures() .find(f => f.get("objnam") === this.selectedBottleneck); }, waterlevel() { return this.selectedWaterLevel === "ref" ? this.refWaterlevel : this.bottleneck.get("gm_waterlevel"); }, refWaterlevel() { if (!this.selectedSurvey) return 0; return this.selectedSurvey.waterlevel_value; }, waterlevelValid() { return isNumber(this.waterlevel); }, refWaterlevelValid() { return isNumber(this.refWaterlevel); }, fileName() { return this.downloadFilename( this.$gettext("Fairwayprofile"), this.selectedBottleneck ); } }, watch: { depth() { if (!this.useCustomDepth) return; this.drawDiagram(); }, useCustomDepth() { this.drawDiagram(); }, currentData() { this.drawDiagram(); }, additionalData() { this.drawDiagram(); }, width() { this.drawDiagram(); }, height() { this.drawDiagram(); }, waterLevels() { this.drawDiagram(); }, selectedWaterLevel() { this.drawDiagram(); }, fairwayData() { this.drawDiagram(); }, selectedBottleneck() { this.$store.commit("application/paneSetup", "DEFAULT"); } }, methods: { addLegendToCanvas(ctx, { width, height }) { let x = width / 12, y = height - 55; ctx.font = "12px sans-serif"; ctx.textAlign = "start"; ctx.beginPath(); ctx.fillStyle = "#5995ff"; ctx.strokeStyle = "#5995ff"; ctx.arc(x, y, 8, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); ctx.fillStyle = "black"; ctx.fillText(this.$gettext("Water"), x + 14, y + 5); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = this.getLayerStyle(1).fillColor; ctx.strokeStyle = this.getLayerStyle(1).strokeColor; ctx.arc(x, (y += 20), 8, 0, 2 * Math.PI); ctx.setLineDash([0.8], 0); ctx.fill(); ctx.stroke(); ctx.fillStyle = "black"; ctx.fillText(this.$gettext("Fairway (LOS 1)"), x + 14, y + 5); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = this.getLayerStyle(2).fillColor; ctx.strokeStyle = this.getLayerStyle(2).strokeColor; ctx.arc(x, (y += 20), 8, 0, 2 * Math.PI); ctx.setLineDash([1.8], 0); ctx.fill(); ctx.stroke(); ctx.fillStyle = "black"; ctx.fillText(this.$gettext("Fairway (LOS 2)"), x + 14, y + 5); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = this.getLayerStyle(3).fillColor; ctx.strokeStyle = this.getLayerStyle(3).strokeColor; ctx.arc(x, (y += 20), 8, 0, 2 * Math.PI); ctx.setLineDash([]); ctx.fill(); ctx.stroke(); ctx.fillStyle = "black"; ctx.fillText(this.$gettext("Fairway (LOS 3)"), x + 14, y + 5); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = "#4a2e06"; ctx.strokeStyle = "black"; ctx.arc(x, (y += 20), 8, 0, 2 * Math.PI); ctx.setLineDash([]); ctx.fill(); ctx.stroke(); ctx.fillStyle = "black"; ctx.fillText(this.$gettext("Sediment"), x + 14, y + 5); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = "rgba(74, 47, 6, 0.6)"; ctx.strokeStyle = "#943007"; ctx.arc(x, (y += 20), 8, 0, 2 * Math.PI); ctx.setLineDash([]); ctx.fill(); ctx.stroke(); ctx.fillStyle = "black"; ctx.fillText(this.$gettext("Sediment (compare)"), x + 14, y + 5); ctx.closePath(); }, close() { this.$store.commit( "application/paneSetup", this.paneSetup === "COMPARESURVEYS_FAIRWAYPROFILE" ? "COMPARESURVEYS" : "DEFAULT" ); this.$store.dispatch("fairwayprofile/clearCurrentProfile"); }, getLayerStyle(los) { let style = this.openLayersMap() .getLayer("FAIRWAYDIMENSIONSLOS" + los) .getStyle()()[0]; // use spread operator to clone arrays let fillColor = style.getFill().getColor(); let strokeColor = style.getStroke().getColor(); let strokeDash = style.getStroke().getLineDash(); return { fillColor, strokeColor, strokeDash }; }, applyChange() { if (this.form.template.hasOwnProperty("properties")) { this.templateData = this.defaultTemplate; return; } if (this.form.template) { this.loadTemplates("/templates/diagram/" + this.form.template.name) .then(response => { this.prepareImages(response.data.template_data.elements).then( values => { values.forEach(v => { response.data.template_data.elements[v.index].url = v.url; }); this.templateData = response.data.template_data; } ); }) .catch(error => { let message = "Backend not reachable"; if (error.response) { const { status, data } = error.response; message = `${status}: ${data.message || data}`; } displayError({ title: this.$gettext("Backend Error"), message: message }); }); } }, downloadPDF() { let fairwayInfo = this.selectedBottleneck + " (" + this.selectedSurvey.date_info + ")"; this.generatePDF({ templateData: this.templateData, diagramTitle: fairwayInfo }); this.pdf.doc.save(this.fileName + ".pdf"); }, // Diagram legend addDiagramLegend(position, offset, color) { let x = offset.x, y = offset.y; this.pdf.doc.setFontSize(10); let width = (this.pdf.doc.getStringUnitWidth("Sediment (Compare)") * 10) / (72 / 25.6) + 4; if (["topright", "bottomright"].indexOf(position) !== -1) { x = this.pdf.width - offset.x - width; } if (["bottomright", "bottomleft"].indexOf(position) !== -1) { y = this.pdf.height - offset.y - this.getTextHeight(8); } this.pdf.doc.setTextColor(color); this.pdf.doc.setDrawColor("#5995ff"); this.pdf.doc.setFillColor("#5995ff"); this.pdf.doc.circle(x, y, 2, "FD"); this.pdf.doc.text(x + 3, y + 1, this.$gettext("Water")); const toRGB = s => { let [, r, g, b] = s.match(/.*?(\d+), (\d+), (\d+), .*/); const toHex = n => { let val = parseInt(n).toString(16); if (val.length === 1) return `0${val}`; return val; }; return `#${toHex(r)}${toHex(g)}${toHex(b)}`; }; const los1Color = toRGB(this.getLayerStyle(1).strokeColor); const los1Fill = toRGB(this.getLayerStyle(1).fillColor); const los2Color = toRGB(this.getLayerStyle(2).strokeColor); const los2Fill = toRGB(this.getLayerStyle(2).fillColor); const los3Color = toRGB(this.getLayerStyle(3).strokeColor); const los3Fill = toRGB(this.getLayerStyle(3).fillColor); this.pdf.doc.setLineDashPattern([0.8], 0); this.pdf.doc.setDrawColor(los1Color); this.pdf.doc.setFillColor(los1Fill); this.pdf.doc.circle(x, y + 5, 2, "FD"); this.pdf.doc.text(x + 3, y + 6, this.$gettext("Fairway (LOS 1)")); this.pdf.doc.setLineDashPattern([1.8], 0); this.pdf.doc.setFillColor(los2Fill); this.pdf.doc.setDrawColor(los2Color); this.pdf.doc.circle(x, y + 10, 2, "FD"); this.pdf.doc.text(x + 3, y + 11, this.$gettext("Fairway (LOS 2)")); this.pdf.doc.setLineDashPattern([], 0); this.pdf.doc.setFillColor(los3Fill); this.pdf.doc.setDrawColor(los3Color); this.pdf.doc.circle(x, y + 15, 2, "FD"); this.pdf.doc.text(x + 3, y + 16, this.$gettext("Fairway (LOS 3)")); this.pdf.doc.setDrawColor("black"); this.pdf.doc.setFillColor("#4a2e06"); this.pdf.doc.circle(x, y + 20, 2, "FD"); this.pdf.doc.text(x + 3, y + 21, this.$gettext("Sediment")); this.pdf.doc.setDrawColor("#943007"); this.pdf.doc.setFillColor("#928269"); this.pdf.doc.circle(x, y + 25, 2, "FD"); this.pdf.doc.text(x + 3, y + 26, this.$gettext("Sediment (Compare)")); }, getPrintLayout(svgHeight, svgWidth) { return { main: { top: Math.floor(0.08 * svgHeight), right: Math.floor(0.08 * svgWidth), bottom: Math.floor(0.2 * svgHeight), left: Math.floor(0.08 * svgWidth) } }; }, drawDiagram() { d3.select(".diagram-container svg").remove(); this.scaleFairwayProfile(); if (!this.height || !this.width) return; // do not try to render when height and width are unknown const layout = this.getPrintLayout(this.height, this.width); this.renderTo({ element: ".diagram-container", dimensions: this.getDimensions({ svgWidth: this.width, svgHeight: this.height, ...layout }) }); }, renderTo({ element, dimensions }) { if (!this.waterlevelValid || !this.refWaterlevelValid) return; let svg = d3.select(element).append("svg"); svg.attr("width", "100%"); svg.attr("height", "100%"); svg .append("g") .append("rect") .attr("width", "100%") .attr("height", "100%") .attr("fill", "#ffffff"); const width = dimensions.width; const height = dimensions.mainHeight; const offsetY = 15; const currentData = this.currentData; const additionalData = this.additionalData; const { xScale, yScaleRight, graph } = this.generateScalesAndGraph({ svg, height, width, dimensions, offsetY }); this.drawWaterlevel({ graph, xScale, yScaleRight, height, offsetY }); this.drawLabels({ graph, dimensions }); if (currentData) { this.drawProfile({ graph, xScale, yScaleRight, currentData, height, color: GROUND_COLOR, strokeColor: "black", opacity: 1, offsetY }); } if (additionalData) { this.drawProfile({ graph, xScale, yScaleRight, currentData: additionalData, height, color: GROUND_COLOR, strokeColor: "#943007", opacity: 0.6, offsetY }); } this.drawFairway({ graph, xScale, yScaleRight, offsetY }); }, /** * Draws Fairway rectangle * * start end * ____________ * [___________] customDepth | referenceDepth * * Starting point is the 0 line of the diagram * */ drawFairway({ graph, xScale, yScaleRight, offsetY }) { if (this.fairwayData === undefined) { return; } for (let data of this.fairwayData) { data.coordinates.forEach(coordinates => { const [startPoint, endPoint, depth] = coordinates; const referenceDepth = this.maxAlt * 1.1 + (this.waterlevel - this.refWaterlevel) / 100; let customdepth = this.depth < referenceDepth ? this.depth : referenceDepth; let fairwayArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(yScaleRight(0)) .y1(function(d) { return yScaleRight(d.y); }); let strokColor = this.getLayerStyle(data.los).strokeColor; // Convert stroke value to rgb() and opacity to pass them separately let [r, g, b, opacity] = strokColor .substring(5, strokColor.length - 1) .split(","); let rgb = `rgb(${r}, ${g}, ${b})`; graph .append("path") .datum([ { x: startPoint, y: this.useCustomDepth ? customdepth : depth }, { x: endPoint, y: this.useCustomDepth ? customdepth : depth } ]) .attr("fill", `${this.getLayerStyle(data.los).fillColor}`) .attr("stroke", rgb) .attr("stroke-opacity", opacity) .attr("stroke-dasharray", this.getLayerStyle(data.los).strokeDash) .attr("d", fairwayArea) .attr("transform", `translate(0 ${-offsetY})`); }); } }, drawLabels({ graph, dimensions }) { graph .append("text") .attr("transform", ["rotate(-90)"]) .attr("y", dimensions.width + Math.floor(0.06 * dimensions.width)) .attr("x", -dimensions.mainHeight / 2) .attr("fill", "black") .style("text-anchor", "middle") .text(this.$gettext("Depth [m]")); graph .append("text") .attr("transform", ["rotate(-90)"]) .attr("y", -1 * Math.floor(0.065 * dimensions.width)) .attr("x", -dimensions.mainHeight / 2) .attr("fill", "black") .style("text-anchor", "middle") .text(this.$gettext("Waterlevel [m]")); graph .append("text") .attr("y", 0) .attr("x", 0) .attr("dy", "1em") .attr("fill", "black") .style("text-anchor", "middle") .attr("transform", [ `translate(${dimensions.width / 2} ${dimensions.mainHeight})`, "rotate(0)" ]) .text(this.$gettext("Width [m]")); }, generateScalesAndGraph({ svg, height, width, dimensions, offsetY }) { let xScale = d3 .scaleLinear() .domain([0, this.totalLength]) .rangeRound([0, width]); // Upper limit is relevant for the extension of the y-Axis // If there is no measure above the waterlevel we choose a cosmetic value of this.maxAlt *0.1 // to get a bit of space above the waterlevel. // Otherwise we take the maximum measured value const upperLimit = this.minAlt > 0 ? this.maxAlt * 0.1 : Math.abs(this.minAlt); // In order to draw positive values downwards, we switch both values in the // domain definition [min, max] => [max, min] has the desired effect. let yScaleRight = d3 .scaleLinear() .domain([ this.maxAlt * 1.1 + (this.waterlevel - this.refWaterlevel) / 100, -upperLimit ]) .rangeRound([height, 0]); // This definition is accordingly but uses values * 100 for the tickformatter let yScaleLeft = d3 .scaleLinear() .domain([ this.waterlevel - (this.maxAlt * 110 + (this.waterlevel - this.refWaterlevel)), this.waterlevel + upperLimit * 100 ]) .rangeRound([height, 0]); let xAxis = d3 .axisBottom(xScale) .tickSizeOuter(0) .ticks(5); let yAxisRight = d3 .axisRight() .tickSizeOuter(0) .tickSizeInner(5) .scale(yScaleRight); let yAxisLeft = d3 .axisLeft() .tickSizeOuter(0) .tickSizeInner(5) .scale(yScaleLeft) .tickFormat(d => this.$options.filters.waterlevel(d)); let graph = svg .append("g") .attr( "transform", "translate(" + dimensions.mainMargin.left + "," + dimensions.mainMargin.top + ")" ); graph .append("g") .attr("transform", `translate(0 ${height - offsetY})`) .call(xAxis) .selectAll(".tick text") .attr("fill", "black") .select(function() { return this.parentNode; }) .selectAll(".tick line") .attr("stroke", "black"); graph .append("g") .attr("transform", `translate(${width} ${-offsetY})`) .call(yAxisRight) .selectAll(".tick text") .attr("fill", "black") .select(function() { return this.parentNode; }) .selectAll(".tick line") .attr("stroke", "black"); graph .append("g") .attr("transform", `translate(0 ${-offsetY})`) .call(yAxisLeft) .selectAll(".tick text") .attr("fill", "black") .select(function() { return this.parentNode; }) .selectAll(".tick line") .attr("stroke", "black"); graph.selectAll(".domain").attr("stroke", "black"); return { xScale, yScaleRight, graph }; }, /** * Draws a rectangle for the waterlevel * (0,0) (totalLength,0) * ____________ * [____________] * (0,height) (totalLength, height) */ drawWaterlevel({ graph, xScale, yScaleRight, height, offsetY }) { let waterArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(height) .y1(function(d) { return yScaleRight(d.y); }); graph .append("path") .datum([ { x: 0, y: 0 }, { x: this.totalLength, y: 0 } ]) .attr("fill-opacity", 0.65) .attr("fill", WATER_COLOR) .attr("stroke", "transparent") .attr("d", waterArea) .attr("transform", `translate(0 ${-offsetY})`); }, /** * * Draws the ground level * * (x, y + ΔWaterlevel) * * ΔWaterlevel is the difference between the current waterlevel and the reference level * * Is the current level higher as the reference level ΔWaterlevel is positive, which in * turn means that the distance between the surface and the ground is increased. * * Is the current level below the reference level ΔWaterlevel is negative, which in turn means * that the distance between the surface and the ground is decreased. * */ drawProfile({ graph, xScale, yScaleRight, currentData, height, color, strokeColor, opacity, offsetY }) { for (let part of currentData) { let profileLine = d3 .line() .x(d => { return xScale(d.x); }) .y(d => yScaleRight(d.y + (this.waterlevel - this.refWaterlevel) / 100) ); let profileArea = d3 .area() .x(function(d) { return xScale(d.x); }) .y0(height) .y1(d => yScaleRight(d.y + (this.waterlevel - this.refWaterlevel) / 100) ); graph .append("path") .datum(part) .attr("fill", color) .attr("stroke", "transparent") .attr("fill-opacity", opacity) .attr("d", profileArea) .attr("transform", `translate(0 ${-offsetY})`); graph .append("path") .datum(part) .attr("fill", "none") .attr("stroke", strokeColor) .attr("stroke-linejoin", "round") .attr("stroke-linecap", "round") .attr("stroke-width", 3) .attr("stroke-opacity", opacity) .attr("fill-opacity", 0) .attr("d", profileLine) .attr("transform", `translate(0 ${-offsetY})`); } }, scaleFairwayProfile() { if (!document.querySelector(".diagram-container")) return; const clientHeight = document.querySelector(".diagram-container") .clientHeight; const clientWidth = document.querySelector(".diagram-container") .clientWidth; if (!clientHeight || !clientWidth) return; this.height = clientHeight; this.width = clientWidth; } }, created() { this.resizeListenerFunction = debounce(this.drawDiagram, 100); window.addEventListener("resize", this.resizeListenerFunction); }, mounted() { // Nasty but necessary if we don't want to use the updated hook to re-draw // the diagram because this would re-draw it also for irrelevant reasons. // In this case we need to wait for the child component (DiagramLegend) to // render. According to the docs (https://vuejs.org/v2/api/#mounted) this // should be possible with $nextTick() but it doesn't work because it does // not guarantee that the DOM is not only updated but also re-painted on the // screen. setTimeout(this.drawDiagram, 150); this.templates[0] = this.defaultTemplate; this.form.template = this.templates[0]; this.templateData = this.form.template; HTTP.get("/templates/diagram", { headers: { "X-Gemma-Auth": localStorage.getItem("token"), "Content-type": "text/xml; charset=UTF-8" } }) .then(response => { if (response.data.length) { this.templates = response.data; this.form.template = this.templates[0]; this.templates[this.templates.length] = this.defaultTemplate; this.applyChange(); } }) .catch(error => { let message = "Backend not reachable"; if (error.response) { const { status, data } = error.response; message = `${status}: ${data.message || data}`; } displayError({ title: this.$gettext("Backend Error"), message: message }); }); }, updated() { this.drawDiagram(); }, destroyed() { window.removeEventListener("resize", this.resizeListenerFunction); } }; </script>