how to put a vue component on leaflet js map? - javascript

I'm creating a leaflet world map, and need to show a modal to user, according to what country the user clicks on.
I'm creating the modal as a component, and using it in the index.vue. the codes in index.vue are:
<template>
<div id="map-wrap" style="height: 100vh">
<client-only>
<l-map :zoom=13 :center="[55.9464418,8.1277591]" :options="options" ref="map">
<l-tile-layer url="..."></l-tile-layer>
<l-geo-json :geojson="geojson" :options="geoJSON_options"></l-geo-json>
<Detail_modal />
</l-map>
</client-only>
</div>
</template>
<script>
import axios from "axios";
import Detail_modal from '~/components/detail_modal.vue';
export default {
name: 'IndexPage',
components:{
Detail_modal
},
data: function(){
return {
options: {
noWrap: true,
maxBounds: [
[-90, -180],
[90, 180]
],
minZoom: 3,
maxZoom: 5,
},
geojson: null
}
},
methods:{
get_data(){
...
},
},
computed:{
geoJSON_options(){
return {
style(feature){
...
},
onEachFeature(feature, layer){
...
}
}
}
},
mounted(){
this.get_data();
setTimeout(() => {
let map = this.$refs.map.mapObject;
map.createPane("detail-modal");
map.getPane("detail-modal").style.zIndex = 1000;
})
}
}
</script>
<style lang="scss" scoped>
</style>
and the component codes are:
<template>
<div id="detail-modal">
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
div#detail-modal{
position: absolute;
width: 300px;
height: 400px;
background-color: white;
z-index: 99;
left: 500px;
top: 20px;
}
</style>
As we see, we can expect a white rectangle to be shown above the map.
The problem is that the component (white rectangle) is always under the map, even when we are using z-index.
I've searched a lot about it, some people say that we should use map panes and map.createPane() in these situations, which turns the element into a leaflet layer and controls the order between different parts of the map, but the problem gets worse when there is no guide about how we are supposed to turn custom elements such as html element with a ID into this such layers.
Well, as always, any help is appreciated! :)

Related

Cannot shrink div width (Vuetify, Nuxt)

I am creating a formulary and some fields are intended to occupy just 50% of the size that a normal field would take.
It is a dynamic formulary so i use a v-for that take a form object from vuex and use its data to summon the correct component on screen.
I em using because i have TextField's, DateField's, CheckField's and all this configuration is made in the forms object.
Now, to the code:
At store/module/forms.js (vuex) (it is a list of objects like this one):
[
{
name: 'familiarIncome',
type: 'TextField',
value: '',
options: {
dataname: 'familiarIncome',
mandatory: true,
label: 'Renda familiar',
placeholder: '',
rules: fieldRules.float('a renda familiar'),
css: 'width: 50%', // Attention here. This will be injected into component
},
}
]
Then, at pages/formulario.js: (note that until now i wrote only 3 input components)
<template>
<v-app class="container d-flex justify-center">
<h1>Coleta de dados</h1>
<v-form class="container fluid">
<h2>Dados pessoais</h2>
<div class="personal-data d-flex flex-wrap">
<div
v-for="field in getForm.personalData"
:key="field.name"
class="field"
<!-- Pay attention to this class up here (field) -->
>
<component
:is="field.type"
<!-- Here is where all those options -including "width: 50%" are being injected -->
v-bind="field.options"
#change="updateData((section = 'personalData'), $event)"
></component>
</div>
</div>
<div class="social-benefits"></div>
<div class="is-disabled"></div>
<div class="address"></div>
<div class="child-already-born"></div>
</v-form>
</v-app>
</template>
<script>
import { mapGetters } from 'vuex'
import TextField from '../components/inputs/textfield.vue'
import ConfirmButton from '../components/inputs/confirmbutton.vue'
import SelectField from '../components/inputs/selectfield.vue'
export default {
name: 'Formulario',
components: { TextField, ConfirmButton, SelectField },
data() {
return {
formToken: this.$route.params.token,
}
},
computed: {
...mapGetters(['getForm']),
},
methods: {
updateData(section, eventData) {
const data = {
section,
...eventData,
}
this.$store.commit('setFormField', data)
console.log(this.$store.state)
},
},
}
</script>
<style scoped>
.field {
padding: 7px;
width: 50%;
}
h1 {
font-size: 36px;
}
h2 {
font-size: 24px;
}
</style>
And now a print of the web page:
If i missed any important info, please let me know ;)
(just for the case where someone complains, i searched A LOT before asking here ;) )
Ok so i figured it out some minutes later by trying to do things that would look wrong (great technique by the way)
What i did was taking out the wrapping div arount the <component> tag, so then the 50% width would really take the right effect.

How to add vanilla JavaScript to vue.js project?

Good Day, I'm very new to Vue.js and want a navbar, which is transparent by default, but changes its background on scrolling. Unfortunately, it does not work. I tried few solutions, but none of this worked. So this JavaScript code is an example from Stack Overflow, which works in a Fiddle. If you need more information and/or code, please let me know.
Navigation.vue
<template>
<div id="navigation">
<nav class="nav-items">
<router-link class="item" to="/home">Home</router-link>
<router-link class="item" to="/about">About</router-link>
<router-link class="item" to="/japan">Japan</router-link>
</nav>
</div>
</template>
<script>
export default {
name: 'navigation'
}
import scroll from '../assets/js/scroll.js';
</script>
scroll.js
const navbar = document.querySelector('#navigation')
window.addEventListener('scroll', function(e) {
const lastPosition = window.scrollY
if (lastPosition > 50 ) {
navbar.classList.add('colored')
} else if (navbar.classList.contains('colored')) {
navbar.classList.remove('colored')
} else {
navbar.classList.remove('colored')
}
})
navigation.scss
FYI: I've removed unneccessary code here.
#navigation {
background: transparent;
.colored {
background: #fff;
transition: 0.3s;
}
}
Note: To view how to import custom code in a Vue component (general case), scroll down past the last <hr>.
Vue is a JavaScript framework and therefore you can insert vanilla code anywhere in it and it will run perfectly fine.
IMHO, you issue is not about importing vanilla code. It's about running it at the correct moment.
You have to run your code inside mounted() hook, because that's when #navigation exists in DOM:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('navigation', {
template: '#navigationTemplate',
mounted() {
window.addEventListener('scroll',
() => document.querySelector('#navigation')
.classList.toggle('colored', window.scrollY > 50)
)
}
})
new Vue({
el: '#app'
});
#app {
min-height: 200vh;
background: url("https://picsum.photos/id/237/1024/540") no-repeat center center /cover;
}
#navigation {
background: transparent;
position: fixed;
top: 0;
padding: 1rem;
transition: 0.3s;
width: 100%;
box-sizing: border-box;
color: white;
}
#navigation.colored {
background: rgba(255, 255, 255, .85);
color: black;
}
body {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script id="navigationTemplate" type="text/template">
<div id="navigation">
<nav class="nav-items">
<a class="item" to="/home">Home</a>
<a class="item" to="/about">About</a>
<a class="item" to="/japan">Japan</a>
</nav>
</div>
</script>
<div id="app">
<navigation />
</div>
your scroll.js can safely be written as:
window.addEventListener('scroll',
() => document.querySelector('#navigation')
.classList.toggle('colored', window.scrollY > 50)
)
Your SCSS seems incorrect:
#navigation {
.colored {
declaration
}
}
will apply declaration to any element with a class of .colored that's inside an element with the id of navigation. But your code toggles the class colored on #navigation. Therefore your SCSS should look like this:
#navigation {
&.colored {
declaration
}
}
Might not seem like much, but the & makes your code apply (or not).
You probably want to apply transition to #navigation, as it should apply when it has the colored class and when it doesn't. If you don't, the return transition (from .colored to :not(.colored)) will not be animated.
For the record and to also answer your initial question, the proper way to import custom code into a Vue component is:
a) export your code as a function:
(in scroll.js)
export function menuScroll = function() {
/* your custom code here */
}
b) import it:
(in your component)
import { menuScroll } from 'path/to/scroll'
c) run it exactly where you need it:
(i.e: in mounted)
export default {
name: 'navigation',
mounted() {
menuScroll();
}
}
Obviously, you want to rename the function in accordance with its purpose/role and the project's naming conventions.
Last, but not least, if your function needs to take params, you might want to use it as a method:
export function someName = function(...args) {
/** do stuff with args **/
}
... and, in component:
import { someName } from 'path/to/it'
export default {
name: 'whatever',
methods: {
someName,
/* more methods... */
}
}
just like that
<template>
.... your HTML
</template>
<script>
export default {
data: () => ({
......data of your component
}),
mounted() {
let recaptchaScript = document.createElement('script')
recaptchaScript.setAttribute('src', 'https://www.google.com/recaptcha/api.js')
document.head.appendChild(recaptchaScript)
},
methods: {
......methods of your component
}
}
</script>
source : Link

Perfect Scrollbar as an Vue.js2 directive

I am working on a perfect-scrollbar directive.
yarn add perfect-scrollbar
I found this lying around, but it silently fails.
This is the code:
<template>
<div class="scroll-area">
<img src="https://i.kinja-img.com/gawker-media/image/upload/s--I5-_uX-9--/c_scale,fl_progressive,q_80,w_800/oh1cxyrtivnead6tn8k6.png" v-scroll/>
</div>
</template>
<script>
import { Container, PerfectScrollbar } from 'myCompnts';
export default {
name: 'Test',
data() {
return {
};
},
directives: {
scroll: PerfectScrollbar
},
components: {
Container
},
};
</script>
<style scoped>
.scroll-area {
position: relative;
width: 100px;
height: 300px;
overflow: hidden;
}
</style>
Unfortunately, it either "no element is specified to initialize PerfectScrollbar", or it gives back these cryptic error messages suggesting an issue with compilation:
Just as the element was not passed in correctly. Vue Chrome debugger provides no info on whether the directive actually got there. The idea sounds sexy as hell, but how to implement it? Thanks for any help, really! :)

css button styles wont work using <style> tags with vue.js

Im currently trying to learn vue.js and am trying to add styles to a component. The component itself works, and the functionality (alert message) works aswell but I cant get the styles to implement.
(Now I understand that technically I'm not using vue.js to style in my first example but this is to show what I had tried)
Attempt 1:
<template>
<div class="container">
<input id="test-btn" type="button" v-on:click= clicked()>
</div>
</template>
<script>
export default{
name: 'test-btn',
methods: {
clicked: function () {
alert("Here's your message")
}
}
}
</script>
<style scoped>
#test-btn{
color: #CC00CC;
width: 150;
height: 50;
}
</style>
Though I had changed the color width and height the button remains the generic grey and doesn't change width or height (it just stays a square). but it does work when I click it (at least something works).
Since I couldn't get that to work I tried to use the v-bind method.
Attempt 2:
<template>
<div class="container">
<input id="test-btn" type="button" v-on:click= clicked() v-bind:style="btnStyle">
</div>
</template>
<script>
export default{
name: 'test-btn',
methods: {
clicked: function () {
alert("Here's your message")
}
},
data: {
btnStyle: {
color: 'red',
width: 100,
height: 50
}
}
}
</script>
<style scoped>
/* #test-btn{
color: #CC00CC;
width: 150;
height: 50;
}*/
</style>
This attempt at v-bind also had failed as well. A friend told me that buttons are difficult to make the styling work and it may not be an error with my code, it may be default styling that is over riding it (I cant accept that). So what I did was tried to add !important to my css color line in the script tags but that didn't work either.
Your <button> is not styled because you have a CSS issue. Add px to width and height. See CSS in the demo below.
The color CSS property is the font color. To change the <button> color use background: yellow;.
new Vue({
el: '#app',
methods: {
clicked: function() {
alert("Here's your message")
}
}
})
#test-btn {
color: #CC00CC;
background-color: yellow;
width: 150px; /* was 150, now 150px */
height: 50px;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="container">
<input id="test-btn" type="button" v-on:click="clicked()" value="Click Me">
</div>
</div>
Works with data and v-bind:style as well (just do width: '150px'; and height: '50px';). To change the background color add background: 'yellow' as well.
new Vue({
el: '#app',
data: {
btnStyle: {
color: '#CC00CC',
background: 'yellow',
width: '100px',
height: '50px'
}
},
methods: {
clicked: function() {
alert("Here's your message")
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="container">
<input id="test-btn" type="button" v-on:click="clicked()" v-bind:style="btnStyle" value="CLICK ME">
</div>
</div>

How to pass data to Vue JS components?

Disclaimer: I have tried to read the docs before opening this question.
I have this component:
<template>
<accordion id="facilities-menu" :one-at-atime="true">
<template v-for="facilities in facilitiesGroups">
<panel class="accordion-toggle" :header="`Facilities #${$index+1}`" :is-open="$index === 0">
<ul>
<li v-for="facility in facilities">
{{facility}}
</li>
</ul>
</panel>
</template>
</accordion>
</template>
<style lang="scss" scoped>
#import "../../../styles/theme-colors.scss";
.panel {
background: #5E6466;
border: none;
}
</style>
<script>
import { accordion, panel } from 'vue-strap'
module.exports = {
components: {
accordion, panel
},
data () {
return {
'facilitiesGroups': [['Continente Alfragide', 'Jumbo Almada', 'Portugália'], ['Pingo Doce', 'Lidl', 'Allegro'], ['Casa']]
}
}
}
</script>
And then I instantiate this component like this:
<template>
<div class="user">
<user></user>
</div>
<facilities></facilities>
</template>
<style lang="scss" scoped>
#import "../../../styles/theme-colors";
.user {
width: 100%;
height: auto;
margin-top: 8%;
margin-bottom: 5%;
}
</style>
<script>
import User from './userProfile'
import Facilities from './groupedFacilities'
module.exports = {
components: {
'user': User,
'facilities': Facilities
}
}
</script>
However, you can see that in the first component I am defining the data to be { 'facilitiesGroups': [['Continente Alfragide', 'Jumbo Almada', 'Portugália'], ['Pingo Doce', 'Lidl', 'Allegro'], ['Casa']] }. But i want this to be passed as an argument, not to be statically defined in the component as it is now.
I have read the docs about how could this be done here. But their example...
Vue.component('child', {
// declare the props
props: ['msg'],
// the prop can be used inside templates, and will also
// be set as `this.msg`
template: '<span>{{ msg }}</span>'
})
...Resembles nothing to what I have in my code... I don't even use Vue.component() anywhere!
How come I am using a different "style" of coding with Vue JS (I started from their boilerplate)?
How can I map Vue JS official documentation to this "style"?
How can pass that array as an argument/property?
Thanks!
Your component needs to declare the data you want passed in as 'props'
<script>
import { accordion, panel } from 'vue-strap'
module.exports = {
components: {
accordion, panel
},
props: ['facilitiesGroups']
}
</script>
... then in your parent component template you pass your data as an attribute. Below is an example where "facilitiesGroups" is a data element of your parent component:
<template>
<div class="user">
<user></user>
</div>
<facilities :facilities-groups="facilitiesGroups"></facilities>
</template>

Categories

Resources