azure map will not render markers in correct position - vue.js

I am trying to render a simple Azure Map in a vue.js single-file component. I can get the map to draw at a specified center and zoom. And draw a line segment exactly where I want it.
But I cannot draw a marker properly. It does draw, but it is seriously south-west from the specified coordinate (which is on the an endpoint of a line segment drawn previously).
Here's a single page Vue.js 'App.vue':
<template>
<div id="myMap"></div>
</template>
<script>
import * as atlas from "azure-maps-control";
export default {
mounted: function() {
this.map = new atlas.Map("myMap", {
center: [-113.666783, 53.806008],
zoom: 7,
view: "Auto",
authOptions: {
authType: "subscriptionKey",
subscriptionKey: "<redacted>",
},
});
let self = this;
//Wait until the map resources are ready.
this.map.events.add("ready", function() {
//Create a data source and add it to the map.
var dataSource = new atlas.source.DataSource();
self.map.sources.add(dataSource);
//Create a line and add it to the data source.
dataSource.add(
new atlas.data.LineString([
[-112.926043, 53.803],
[-113.666783, 53.806],
])
);
//Create a line layer to render the line to the map.
self.map.layers.add(
new atlas.layer.LineLayer(dataSource, null, {
strokeColor: "blue",
strokeWidth: 5,
})
);
//Create an HTML marker and add it to the map.
var marker1 = new atlas.HtmlMarker({
color: "DodgerBlue",
position: [-112.926043, 53.803],
anchor: "bottom",
htmlContent: '<div class="pulseIconNormal"></div>',
popup: new atlas.Popup({
content:
'<div style="padding:10px">Sensor</div>',
pixelOffset: [0, -30],
}),
});
self.map.markers.add(marker1);
//Add a click event to toggle the popup.
self.map.events.add("click", marker1, () => {
marker1.togglePopup();
});
});
}
}
</script>
<style>
#myMap {
height: 100vh;
width: 100vw;
}
.pulseIconNormal {
display: block;
width: 10px;
height: 10px;
border-radius: 50%;
background: blue;
}
</style>
When I looked at DOM for the marker (in Firefox dev tools), this is the style that I see:
transform: translate(-50%, -100%) translate(737px, 235px) rotateX(0deg) rotateZ(0deg);
This isn't coming from CSS, but is in inline. That's the reason, but not the explanation why. It appears the control itself is generating this.

I found the problem. I am using NPM to load azure-maps-control and I had to explicitly add
<style src='azure-maps-control/dist/atlas.min.css'></style>
to the .vue file.

The map div in your code isn't closed properly. Instead of <div id="myMap" /> it should be <div id="myMap"></div>. HTML standards say self closing div's are invalid. Give that a try and see if it helps.
If it doesn't try inspecting the HTML marker DOM to see if any CSS is being appended to it by your app and try adjusting to see if it addresses the issue.
Looking at your code, the HTML marker should be anchored bottom center to its position.

For the same problem with Angular (11), I had to add the azure css file to my angular.json like so:
"styles": [
"src/styles/styles.scss",
"node_modules/azure-maps-control/dist/atlas.min.css"
],

Related

How to do width transition in Tailwind CSS?

When I try to do a transition using the default "w-#" options in Tailwind, my transitions don't apply. When I hard code in my own classes for width, it works fine. Is there something weird with Tailwinds CSS and how it handles width that would cause this?
Here's the HTML text. The main part here is the dynamic class "sidebarWidth" which switches when the button is clicked. The transition-all, slowest and ease are all things I extended in Tailwind.
<nav class="text-white absolute md:relative flex-col min-h-full bg-black mt-24 md:mt-12 transition-all transition-slowest ease" :class="sidebarWidth">
Here's the JS code in the computed properties of the Vue component
sidebarWidth: function() {
if (this.$store.getters.isSidebarCollapsed) {
return "w-14 invisible md:visible";
} else {
return "w-64";
}
}
If I swap out w-14 and w-64 for the following classes, it works great.
<style scoped>
.width1 {
width: 100px;
}
.width2 {
width: 400px;
}
</style>
I basically want my sidebar nav to slide in when I click a button. In mobile, the sidebar nav is hidden and I want it to slide out. In the desktop, it should be a small nav and then slide out to a full screen nav. It works, but the slide transition doesn't work. Also, the margin change between mobile and desktop does animate properly.
You need to perform a few steps to activate the behaviour you are looking for.
The first one is about extending you Tailwind theme via tailwind.config.js. You need to add the transitionProperty for width.
module.exports = {
...
theme: {
...
extend: {
...
transitionProperty: {
'width': 'width'
},
},
},
}
The changes above create the transition-width class. Simply apply this one to your nav tag. In your specific case you can overwrite the transition-all class.
<nav class="text-white absolute md:relative flex-col min-h-full bg-black mt-24 md:mt-12 transition-width transition-slowest ease" :class="sidebarWidth">
The last step is quite easy: ensure that Tailwind is recreating the CSS. Afterwards you should see a smooth width transition in your project.
Background to the Problem
By default, the width and height transitions are not activated in Tailwind CSS. Here is the CSS that will be applied when using transition class.
transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
Like you can see width and height are missing in transition-property.
You can find more information about it in the Tailwind documentation.
You can make your own transition property like in tailwind.config.js :
Multiple properties :
module.exports = {
theme: {
extend: {
transitionProperty: {
multiple: "width , height , backgroundColor , border-radius"
}
}
}
One property :
module.exports = {
theme: {
extend: {
transitionProperty: {
width: "width"
}
}
}

Why should I use v-bind for style

I just started learning Vue and I was wondering, why should I use v-bind for style and not write it regularly in html/css file
Let's say you need to create a progress bar that is not static. You will then need to update the style attribute width for-example.
To accomplish this, we need to programatically edit the width of the element. We 'cannot' to this in plain css, therefore the :style attribute comes in handy.
Let's create an example:
Codepen
HTML
<div id="vue">
<div class="progress-bar">
<div :style="{'width':progress + '%'}" class="progress" />
</div>
<button #click="fakeProgress">Init fake progress</button>
</div>
Css;
.progress-bar, .progress {
border-radius: 20px;
height: 20px;
}
.progress-bar {
width: 250px;
background-color: gray;
}
.progress {
background-color: blue;
width: 0;
transition: all 1s ease;
}
Javascript
new Vue({
el: '#vue',
data: {
progress: 0
},
methods: {
fakeProgress() {
let progress = setInterval(() => {
if(this.progress == 100) {
clearInterval(progress)
} else {
this.progress += 1;
}
}, 50)
}
}
})
As you see here, we bind the progress data attribute to the width value on the fake progress bar. This is just a simple example, but I hope this makes you see its potential. (You could achieve this same effect using the <progress> tag, but that would ruin the explanation.
EDIT; Also want to point out that you are supposed to write all your css as normal as you point out in your question. However, :style is used in cases that you cannot normally use css for. Like the example above where we need css to change from a variable.

print vueJS component or convert to pdf

I have a vueJS component that I want to print. However, when I use the standard print dialog I lose all the CSS and basically only have plain text.
I have also tried Printd.
My code is along the lines of:
mounted () {
this.cssText = `
.a4-paper {
height: 29cm;
width: 14cm;
}
h4, h3, h2, h1 {
text-align: center;
width: 100%;
}
label.underline {
border-bottom: solid black 1px;
height: 0.3cm;
width: 100%;
}`;
this.d = new Printd()
},
methods: {
show(event: Event) {
this.event = event;
this.visible = true;
},
print() {
this.d.print(this.$el, this.cssText)
}
}
However, the result looks nothing like how the component is rendered. I haven't been able to find a solution for this. Can somebody help me?
The problem exists because printd creates a new Document for printing, therefore styles aren't carried down into the child component, which you are referencing with this.$el
The workaround which I use is to take all of the style elements from the head of the current document and append them to the document that printd creates. Change your print method to the following;
print() {
const d = new Printd()
d.print(this.$el, this.cssText, (win, doc, node, launchPrint) => {
// Get style elements
const styles = [].slice.call(document.getElementsByTagName('style'))
// append them to the the new document head element
styles.forEach(styleElement => doc.head.appendChild(styleElement.cloneNode(true)))
launchPrint(win)
})
},

MapBox (mapbox-gl-vue) renders the map on only 50% of the width of the container

I am trying MapBox with Vue 2 and I cannot make the map take the full width of the container. It only renders on 50% of the width of the container.
I have included the files in the head of my index.html as follows:
<script src='https://api.mapbox.com/mapbox-gl-js/v0.40.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v0.40.0/mapbox-gl.css' rel='stylesheet' />
I want the map in a component (Map.vue, I am using vue-router), so here is the code in Map.vue:
Script:
import Mapbox from 'mapbox-gl-vue';
export default {
components: {
'mapbox': Mapbox
}
}
Template:
<mapbox access-token="pk.eyJ1Ijoic3BlZW5pY3Q....."
:map-options="{
style: 'mapbox://styles/mapbox/streets-v9',
center: [-96, 37.8],
zoom: 3
}"
:geolocate-control="{
show: true,
position: 'top-left'
}"
:scale-control="{
show: true,
position: 'top-left'
}"
:fullscreen-control="{
show: true,
position: 'top-left'
}">>
</mapbox>
Style:
#map {
width: 100%;
height: 600px;
position: absolute;
margin:0;
z-index:1;
}
I have tried everything I know in the CSS id but it only renders the map in the right half of the width of the container, in the left one only the logo and the controls are displayed while the rest of the area is empty.
To solve the problem, I just had to delete "text-align: center;" from #app in App.vue.
For more details, check the issue I had opened here:
https://github.com/phegman/vue-mapbox-gl/issues/11
It looks like to me, there is something dynamic with the div or the div is rendered later after the instantiation. I have not used vue, however.
I have had this problem with tabs and div rendered after the page load such as in tabs or triggered by JavaScript.
If you use map.invalidateSize(); where map is the object instantiated. This will redraw the map. Try and put this after the window is loaded to test the code. Then perhaps it can be converted into the correct Vue implementation.
window.addEventListener("load", function(){
map.invalidateSize();
});;

TabContainer displays Tabs only at windowresize

I want to create a Tabcontainer and fill its TabPage contents programmatically, but the TabPages won't be displayed. So far my Code:
_buildUI: function () {
var bordercontainer = new dj_BorderContainer({ style: "height: 100%; width: 100%;", gutters: false, region: "top" });
var tabcontainer = new dj_TabContainer({ useMenu: false, region: "center", tabposition: "top", doLayout: "false", style: "height: 100%; width: 100%;" });
for (var i = 0; i < ki_KisConfig.widgets.movingwindow.calccount; i++) {
var contentpane = new dj_ContentPane({ title: "Calculation " + (i + 1), content: "content", style: "height: 100%; width: 100%;" });
//contentpane.startup();
tabcontainer.addChild(contentpane);
}
tabcontainer.startup();
bordercontainer.addChild(tabcontainer);
bordercontainer.startup();
do_domConstruct.place(bordercontainer.domNode, this.interface, "first");
bordercontainer.resize({ h: "265px", w: "432px" });
},
I've googled around and tried different things. As you cann see I'm setting the doLayout-Property mentioned here. I also use a BorderContainer like mentioned here in the last posting and I'm trying to resize it after creating the TabContainer like mentioned here.
It doens't matter if I'm calling the method in the postCreate- or the startup-function of the containing widget.
I'm trying to set the width and height via style or to startup every "sub"widget.
Nothing works and the TabContainer only gets displayed when I'm resizing the browserwindow or resizing it by opening/closing the developertools (F12). If it gets displayed it looks like I want it. The only problem is that the TabList has a size of 0x0 and the same with the TabPaneWrapper when I'm inspecting directly the DOM.
Has anyone any idea?
Edit
After calling startup only on the BorderContainer I get this result:
The tablist layout is strange and also the content of the programmatic selected tab isn't displayed. Everything is again fine after a window resize:
Solution (summary)
I retrieved the best result with defining the BorderContainer and the TabContainer in the HTML-template. Unfortunately the layout of the tablist still failed. This answer delivered the solution for correct tablist layout: My widget didn't contain resize() so I added it and everything is now working fine.
resize: function() {
var tabcontainer = dj_registry.byId("tabContainerMW");
if (tabcontainer) {
tabcontainer.resize(arguments);
}
},
Some notes to your code:
The region attribute is here not required. Its only used to indicate the position for BorderContainer children.
var bordercontainer = new dj_BorderContainer({
style: "height: 100%; width: 100%;",
gutters: false,
region: "top"
});
You don't need to set a width and height on your ContentPane, let this do the TabContainer.
var contentpane = new dj_ContentPane({
title: "Calculation " + (i + 1),
content: "content",
style: "height: 100%; width: 100%;"
});
I've created a sample for you, maybe this helps you out.
require(["dijit/layout/BorderContainer", "dijit/layout/TabContainer",
"dijit/layout/ContentPane", "dojo/domReady!"],
function(BorderContainer, TabContainer, ContentPane) {
// first create the BorderContainer without any arguments.
let bc = new BorderContainer({}, "bc");
// then create your TabContainer with region center.
let tc = new TabContainer({
region: 'center'
}, document.createElement("div"));
// add it to your BorderContainer
bc.addChild(tc);
// then create three tab panes (ContentPane) and add them to your TabContainer
let cp = new ContentPane({
content: "My tab pane!",
title: "My tab title"
}, document.createElement("div"));
tc.addChild(cp);
let cp2 = new ContentPane({
content: "My second tab pane!",
title: "My second tab title"
}, document.createElement("div"));
tc.addChild(cp2);
let cp3 = new ContentPane({
content: "My closable tab pane!",
title: "My closable tab title",
closable: true
}, document.createElement("div"));
tc.addChild(cp3);
// call startup on your BorderContainer. startup of BorderContainer will call also the startup methods of all children (TabContainer, ContentPane's).
bc.startup();
});
body, html {
height: 100%;
width: 100%;
overflow: hidden;
margin: 0 auto;
}
<link href="//ajax.googleapis.com/ajax/libs/dojo/1.4/dijit/themes/tundra/tundra.css" rel="stylesheet"/>
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>
<span class="tundra" style="width: 100%; height: 100%;">
<div id="bc" style="width: 100%; height: 100%;"></div>
</span>
Edit
As an addition:
I was able to create a fiddle, which reproduces the failure. The problem here is that the createDialogContent() method is getting called after the dialog show's up. As I mentioned below in the comments section, it is important to create a dialog's content before showing it.
In this fiddle (bottom end of code) are two sections, which call both the same methods, just transposed. In the first snippet, the methods are called in the wrong order. Int the second snippet, they're called in the right order.
// uncomment this
createDialogContent();
dialog.show();
// comment this
// dialog.show();
// createDialogContent();