How to access data from the SVG map with Vue.js - vue.js

I use SVG map of Austria. Each province has "title" attribute which contains a name of the province.
[full example available in the snippet below]
Outside the map in my HTML I have a paragraph, where I want to display the name of the province that was clicked by the user.
{{ province }}
How can I achieve that?
Here is my snippet with my code:
https://mdbootstrap.com/snippets/jquery/marektchas/500840?view=project

If you can change the svg part you can add a click event on each path:
<path #click="setProvince('Burgenland')" id="AT-1" title="Burgenland" class="land" d="..." />
And add a method in your script:
methods: {
setProvince (title) {
this.province = title
}
}
Updated answer
If you have a lot of provinces, you can add the click event on mounted by selecting all the <path> selectors (or class name or any selector you find relevant for your case):
new Vue({
el: '#app',
data: {
province: null
},
mounted () {
this.addClickHandler()
},
methods: {
setProvince (title) {
this.province = title
},
addClickHandler () {
let paths = this.$el.querySelectorAll('path')
paths.forEach(el => {
let title = el.attributes.title.value
el.addEventListener('click', () => {
this.setProvince(title)
})
})
}
}
});
There's no need of #click in template anymore this way.
Live example here

You need to learn about event delegation. You should be able to attach an event handler to the svg root and get events for any element inside the svg
https://recursive.codes/blog/post/34
https://jqfundamentals.com/chapter/events

Related

Full Calendar Vue JS Component - Adding Events

I'm using Full Calendar with Vue JS: https://fullcalendar.io/docs/vue
I can't seem to find a way to add events using the Full Calendar Vue JS component. The only way I see the examples doing it is by getting ahold of the calendar's API and creating events through it. This seems a little anti-vue.
Demo from docs:
handleDateSelect(selectInfo) {
let title = prompt('Please enter a new title for your event')
let calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if (title) {
calendarApi.addEvent({
id: createEventId(),
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay
})
}
}
I'm wondering, is the only way to create an event on the Vue JS Full Calendar by tapping into the calendars native API as shown above? Are there no means of sending an event of sorts into the component?
You don't really have to fallback to using imperative instance API. The Vue FullCalendar components exposes the events as part of the options which you can use. For example:
<template>
<FullCalendar :options="opts" />
<button #click="addNewEvent"></button>
</template>
In the component definition, you can use the events key to set list of events declaratively. Every time, you need to add/remove the events just modify the events key which is part of your options object.
export default {
data() {
return {
opts: {
plugins: [ /* Any addition plugins you need */ ],
initialView: 'dayGridMonth',
events: [
{ title: 'First Event', date: '2021-05-12' },
/* Few more initial events */
]
}
}
},
methods: {
addNewEvent() {
this.opts.events = [
...this.opts.events,
{ title: 'Another Event', date: '2021-05-13' }
];
}
}
}

Get id of item clicked and use it for creating dynamic url in vuejs

I have a vue bootstrap table displaying, in each row, few properties of objects of an array (got through an api call with axios).
Every row has a button that should redirect me to a detail page, with more properties of that object, plus a map.
I was thinking to make a function to get the property id of the object contained in the clicked row, but I'm not sure on how to do it. I need the id to use it in the last part of the api call.
The store is structured so that I have a module for the user and another one for these objects (activities). In these modules I deal with state, actions and mutations. A separate file handles the getters. As these activities will be modified, I need to save their state too.
I will also need to be able to easily access all the properties of the single object (not only the ones shown in the table row) from other components.
I'm getting very confused.
Here the code:
Table with all the activities:
<b-table
responsive
:fields="fields"
:items="activity"
>
<template
slot="actions"
>
<b-button
v-b-tooltip.hover
title="Mostra dettagli"
variant="info"
class="px-3"
#click="goToActivityDetail"
>
<span class="svg-container">
<svg-icon icon-class="search"/>
</span>
</b-button>
</template>
</b-table>
In the script:
export default {
name: 'AllActivities',
data() {
return {
fields: [
{ key: 'activity.activityName', label: 'Activity', _showDetails: true},
{ key: 'related_activity', label: 'Related activity', _showDetails: true},
{ key: 'start', label: 'Start', _showDetails: true },
{ key: 'end', label: 'End', _showDetails: true },
{ key: 'travel_mode', label: 'Travel mode', _showDetails: true },
{ key: 'actions', label: '' }
],
activity: [],
methods: {
getIdActivity(){
**?? how to get it ??**
},
goToActivityDetail() {
this.$router.push({
name: 'activityDetail'
})
}
}
goToActivityDetail()
obviously does not work, in the console:
- [vue-router] missing param for named route "activityDetail": Expected "activityId" to be defined
- [vue-router] missing param for redirect route with path "/see-all-activities/:activityId": Expected "activityId" to be defined)
In the getters file I have:
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token
}
export default getters
So here I will need to have something like:
activityId: state => state.activity.activityId
Which is coming from activity.js, which is:
import {
getActivityId
} from '#/components/AllActivities'
const state = {
activityId: getActivityId()
}
const mutations = {
SET_ACTIVITY_ID: (state, activityId) => {
state.activityId = activityId
}
}
const actions = {
setActivityId({
commit
}) {
return new Promise(resolve => {
commit('SET_ACTIVITY_ID', '')
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
IF this is right, what is left is the function to get the id of the object contained in the table row clicked.
Also, how to write that activity id in the api call (axios)?
Now I have:
export function getSingleActivity() {
return request({
url: 'http://localhost:8000/api/user_activity/:activityId',
method: 'get'
})
}
But I am not sure if that's correct.
Also, how to access the other properties (to be displayed in the detailActivity page)?
This will be made of a list of some properties (probably a stacked table component) and a map component, so I will need to access the properties in both these components.
I hope I've been clear enough,
thank you.
It was dead simple. I post how to solve it in case someone else get stuck on this too.
I added a slot scope to the template that contains the button:
<template
slot="actions"
slot-scope="data"
>
Then I added the single activity (following the vue bootstrap markup data.item) as parameter to the button click
#click="goToDetailActivity(data.item)"
And the function called by the click became:
goToDetailActivity(activity) {
this.$router.push({
name: 'DettaglioAttivita',
params: { activityId: activity.id }
})
}
That's it.
Worth mentioning is you're using vuex. If I understand correctly you want to get the property read from vuex?
To read a property from vuex you can eather use this.$store.getters.activity
Or use mapGetter.
Read the following page https://vuex.vuejs.org/guide/getters.html
Also you have to set the param when you do a router.push
router.push({ name: 'activity', params: { id: activityId } })

How to build a VUE link in a method using vue-router

I'm new using VUE.JS and I'm in love with it! I love the vue-router and router-link! They are awesome!
Now I have a table populated by data coming from axios and I would like to build a link using this data in a custom method to have the team name clickable.
Here the template:
<BootstrapTable :columns="table.columns" :data="table.data" :options="table.options"></BootstrapTable>
Axios returns ID, name and other data used to update the table as here
Basically, I need to update the values in my table using the axios's received data. Something like:
team: '<a v-bind:href="club/'+team.id+'">'+team.team+'</a>',
or
team: '<router-link :to="club/'+team.id+'">'+team.team+'</router-link>',
But obviously it dosn't works...
How can a build a link?
I fixed it using custom column event and formatter in columns table setting:
{
field: 'match',
title: 'Match',
formatter (value, row) {
return `${value}`
},
events: {
'click a': (e, value, row, index) => {
e.preventDefault();
this.$router.push(`/matches/${row.pos}`)
}
}
},
Another solution:
Just in case of JSON code having links instead of table config is adding click listener in mounted() and a well formatted dataset in JSON HTML link:
team: "<a href=\"/club/"+team.id+"\" data-to='{\"name\": \"team\",\"params\":{\"teamId\":"+ team.id+"}}'>"+ team.team+"</a> "+userCode
Here the listener:
mounted() {
window.addEventListener('click', event => {
let target = event.target;
if (target && target.href && target.dataset.to) {
event.preventDefault();
const url = JSON.parse(target.dataset.to);
//router.push({ name: 'user', params: { userId: '123' } })
this.$router.push(url);
}
});
}
This might be shorter solution for your issue :
routes = [
{
component : 'club',
name : 'club',
path : '/club/:teamid'
}
]
<a #click="$router.push({ name: 'club', params: { teamid: team.id}})">team.team</a>

vuejs2-ace-editor: accessing editor instance

I am using this Vue2 component for ACE editor:
https://github.com/chairuosen/vue2-ace-editor
This is how I add the component to my app
var app = new Vue({
el: '#vue_app',
data: {
message: 'Hello Vue!',
editor_content: 'somecontent'
},
methods:{
editorInit:function (el) {
require('brace/mode/json');
require('brace/theme/tomorrow');
}
},
components: {
editor:require('vue2-ace-editor')
}
});
And when I put this into my HTML everything works correctly:
<editor v-model="editor_content" #init="editorInit();" lang="json" theme="tomorrow" width="500" height="100"></editor>
However the editor renders with a gutter which I don't need, so I want to access the editor instance to tweak some properties.
The source code for this component says it emits an event on mount:
mounted: function () {
var vm = this;
var lang = this.lang||'text';
var theme = this.theme||'chrome';
require('brace/ext/emmet');
var editor = vm.editor = ace.edit(this.$el);
this.$emit('init',editor);
editor.$blockScrolling = Infinity;
editor.setOption("enableEmmet", true);
editor.getSession().setMode('ace/mode/'+lang);
editor.setTheme('ace/theme/'+theme);
editor.setValue(this.value,1);
editor.on('change',function () {
var content = editor.getValue();
vm.$emit('input',content);
vm.contentBackup = content;
});
}
How and where do I catch this event and access the editor object?
You are already listening to the init event and calling the editorInit method. However, you need to pass the data being emitted by the <editor> component.
You can either do that explicitly by using $event:
<editor v-model="editor_content" #init="editorInit($event)" ...></editor>
Or implicitly by simply providing the method name as the event handler:
<editor v-model="editor_content" #init="editorInit" ...></editor>
Then, in your editorInit method, the param being passed in will be the instance of the editor. And I believe you can specify to not render the gutter like so:
editorInit:function (editor) {
editor.renderer.setShowGutter(false)
require('brace/mode/json');
require('brace/theme/tomorrow');
}

VueJS + VeeValidator + Multiple fields

Versions:
VueJs: 2.2.2
Vee-Validate: 2.0.0-beta.25
Description:
I'm wondering if there's a way to have a unique validator for multiple fields?
Typically, an Address form with 1 input fot the street, 1 for the number and 1 for the city
I want to make a validation on the combination of all the elements.
I've read the documentation but I can't find an exemple that could help me for that.
You could apply a custom validator to a custom component that contained all the fields you want to validate together. For example, you could build a location component (using location instead of address because address is an HTML5 element, and you cannot name a Vue component the same as an existing HTML element).
Vue.component("location", {
props:["value"],
template: "#location-template",
data(){
return {
location: this.value
}
},
methods:{
update(){
this.$emit('input', Object.assign({}, this.location))
}
},
})
Then you can build a validator for that component.
const locationValidator = {
currentLocation: null,
getMessage(field, args) {
if (!this.currentLocation.street)
return "Location requires a street";
if (!this.currentLocation.street_number)
return "Location requires a street_number";
if (!this.currentLocation.city)
return "Location requires a city";
},
validate(location, args) {
this.currentLocation = location;
if (!location.street || !location.street_number || !location.city)
return false;
return true
}
};
Finally, you can pull that together in your Vue.
new Vue({
el:"#app",
data:{
loc: {}
},
created(){
this.$validator.extend("location", locationValidator)
}
})
And your Vue template
<span v-show="errors.has('location')" style="color:red">{{ errors.first('location') }}</span>
<location v-validate="'location'" v-model="loc" data-vv-name="location"></location>
Here is an example.