l-rectangle component does not update - vue.js

I am using the l-rectangle in a Vue leaflet project.
I am creating a rectangle like so:
<l-rectangle :bounds="rectangle"></l-rectangle>
which displays the rectangle on my map doing this in the .js-file:
new Vue({
el: '#app',
data: function() {
return {
rectangle: [[69.81310023846743, 16.929931640625004],[69.11310023846743, 16.129931640625004]]
}
}
});
I have created a map click event, in this function I am changing the coordinates of the rectangle array in order to make the rectangle change size/shape. But nothing is happening (the function gets called but the rectangle does not change):
clickEvent:function(event)
{
var point = [event.latlng.lat,event.latlng.lng];
this.rectangle[0] = this.rectangle[0];
this.rectangle[1] = point;
}
Thanks for any help and guidance!

As per Vue documentation, the reactivity for array will not work if you set value for a particular index, Instead you can use some array operation methods which are mentioned in Vue documentation
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
the following above methods makes the array reactive
The click event can be replaced with
clickEvent:function(event)
{
var point = [event.latlng.lat,event.latlng.lng];
this.rectangle.splice(0, 1, this.rectangle[0]);
this.rectangle.splice(1, 1, point);
}

Related

call a component method from main vue app

I'm super new to Vue and I'm trying to get into components.
Basically, I have the main script file with my new vue app (with a countdown function based on setInterval and in the same file a vue component( it's a stamina bar made with canvas) with width increasing and decreasing methods. Basically, I would like to decrease the bar width every second but I don't get how to call the methods declared in the component. I guess that when they are declared inside the component I could access them from everywhere, but I didn't find a proper solution ( I tried with $refs but it's not working)
Is there any way where I can call the addWidth() and subWidth() function from a method in my new Vue app?
thank you
this is the component
Vue.component('stamina', {
template: '<div><canvas ref="stamina" style="height:25px;width:300px;" /></div>',
data(){
return {
stamina_width:300,
// vueCanvas: null
}
},
methods: {
drawStamina(){
// clear canvas
this.vueCanvas.clearRect(0, 0, 300, 50);
// draw rect
this.vueCanvas.rect(20,20, this.stamina_width, 100);
//colorize with gradient
var grd = this.vueCanvas.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "orange");
grd.addColorStop(1, "green");
this.vueCanvas.fillStyle = grd;
//fill the rect with gradient
this.vueCanvas.fillRect(0, 10,this.stamina_width, 10);
},
addWidth() {
this.stamina_width += 20
this.drawStamina()
},
subWidth() {
this.stamina_width -= 20
this.drawStamina()
}
},
mounted () {
var ctx = this.$refs.stamina.getContext('2d');
this.vueCanvas = ctx;
var stam = this.stamina_width;
var grd = this.vueCanvas.createLinearGradient(0, 0, 300, 0);
grd.addColorStop(0, "red");
grd.addColorStop(0.5, "orange");
grd.addColorStop(1, "green");
this.vueCanvas.fillStyle = grd;
//fill the rect with gradient
this.vueCanvas.fillRect(0,25,stam,25);
}
});
and this is the main vue
var match = new Vue({
el:'#app',
methods:{
//call the methods from component stamina
}
yes you can call methods up/down of parent/child components. You may need to look at creating custom events and listen for them.
Another option I believe might be less complex is to consider using Vuex.
All of your addWidth and subWidth logic will reside in a centralized place this way. All of your components--no matter how nested can "subscribe" to these things (and modify them if needed).
Check out actions. You'll dispatch an action addWidth which sends it to Vuex. In your component that is responsible for rendering the stamina bar I would use a computed property to watch for updates. Something like this:
computed: {
barWidth() {
return this.$store.getters["bar/width"]; // 20
}
}
That is just a really rough example and will not likely be what you're looking for.
VueSchool has some nice (free) courses to help get you rolling with Vuex as well.

How to add scroll second scroll to top like bottom scroll?

I have Vue component that greater than browser size. So browser add to bottom of it scrollbar. I need second one at top. I do not have any code to show, because I do not know how to do it. I have googled and found only pure\jquery solutions, but I do not know how to integrate them in Vue project.
The pure JS topic https://stackoverflow.com/a/50776007/1432751
https://jsfiddle.net/bqsw8pt9/2/
new Vue({
el: '#app',
data: {
message: 'Very loooooooooooooooooooooooooooooooooooooooong teeeeeeeeeeeeeeeeeeeeeeeeeexttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt'
}
})
I tried to add pure js in mount section, but got next error:
To achieve this:
Create two elements with the overflow property, both elements should be of the same width so the scroll behaviour will be uniform.
Add a scroll handler to the first element that scrolls the second element to its current position i.e. when element 1 scrolls to 20, scroll element 2 to 20 also.
Add a scroll handler to the second element, which will scroll the first element to its current position.
To ensure that both elements aren't trying to update each other at the same instance, maintain a scroll state that is updated when either of them fires the scroll handler.
Please find an updated version of your fiddle: link to fiddle
new Vue({
el: '#app',
data: {
message: 'Very loooooooooooooooooooooooooooooooooooooooong teeeeeeeeeeeeeeeeeeeeeeeeeexttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt',
scrolling: false
},
methods: {
handleScroll1: function () {
if(this.scrolling) {
this.scrolling = false;
return;
}
this.scrolling = true;
console.log(this.scrolling, this.$refs["wrapper1"].scrollLeft);
this.$refs["wrapper2"].scrollLeft = this.$refs["wrapper1"].scrollLeft;
},
handleScroll2: function () {
if(this.scrolling) {
this.scrolling = false;
return;
}
this.scrolling = true;
console.log(this.scrolling, this.$refs["wrapper2"].scrollLeft);
this.$refs["wrapper1"].scrollLeft = this.$refs["wrapper2"].scrollLeft;
}
}
})
As a side note i want to suggest that you learn Vanilla JS, it'll give you a better grasp of javascript, and also make it easy for you to understand any piece of code in whatever framework so you can always adapt it to your needs.
This answer is based on a similar one by StanleyH and HoldOffHunger

Solid guage refresh when receives new data (point value)

I am having issues with my solid gauge component using the highcharts-vue wrapper.
Basically every time it receives new data it re-draws the animation from 0. Please see sandbox link below for example: https://codesandbox.io/s/n0no0jky1l
The desired behavour im trying to achieve is shown here: https://www.highcharts.com/demo/gauge-solid
Where the gauge animates from old to new value smoothly.
From what i can tell it is not recommended to access the charts methods manually (for example to call the points.update method). (source: https://github.com/highcharts/highcharts-vue#chart-object-reference)
Any ideas?
It occurs because of mutating data in Highcharts. Please use Point.update, instead of updating whole series, to make animation work. You can access whole Chart object by reference in your component, specifically this.children[0].chart. Here is the code:
watch: {
title(newValue) {
this.chartOptions.title.text = newValue;
},
points(newValue, oldValue) {
console.log("watch firing", newValue, oldValue);
this.$children[0].chart.series[0].data[0].update(oldValue += 1);
},
units(newValue) {
this.chartOptions.series[0].dataLabels.format =
'<div style="text-align:center"><span style="font-size:2rem;color: black">{y}</span><div>' +
newValue +
"</div><div/>";
},
min(newValue) {
this.chartOptions.yAxis.min = newValue;
},
max(newValue) {
this.chartOptions.yAxis.max = newValue;
}
},
Don't worry about your data, Highcharts mutate it, so your points prop should be updated every time.
Live example: https://codesandbox.io/s/ojkzvo05z
Kind regards!

How to access or get value of specific graph on chart plot by click event?

I use vue-chartjs to draw some chart like line, bar, etc.
In my project, there are many cases using specific value or lable of data in chart.
Using tooltip option of vue-chartjs, I can check that value or label of data item when hovered.
I want to know how to access or get information of specific data matched with point on graph when clicked(not hovered).
Here is my code about chart options.
chartOptions: {
responsive: false,
onClick: function(evt){
//Some logic to get value of label of specific data...(type, label, value, ...)
}
In my case, I use 'onclick' option to access specific data on point triggered 'click' event. In 'onClick' callback, I checked all of chart elements and dataset, etc.
How can I get value of label specific dataItem on point of graph(like line) or bar of graph(like bar) when triggered click event?
I was not able to find a solution that worked for me, but I dug a little bit and this is what I came up with.
onClick: function(evt, array) {
if (array.length != 0) {
var position = array[0]._index;
var activeElement = this.tooltip._data.datasets[0].data[position]
console.log(activeElement);
} else {
console.log("You selected the background!");
}
}
This will get the position in the array that you clicked and grab the data from what position you clicked. This may not be the prettiest or best example, but it worked for me.
This solution use the getElementAtEvent method of chartjs, but to use that you need reference to the Chart itself, not the Vue component. We can get that from the $data._chart property. To use this in a parent Vue component, we use the $refs as seen below`.
So parent defines the chart options
{
...
options: {
onClick: this.handleChartClick
}
...
}
and then parent method, using $refs with $data._chart to get the chart. We get the datasetIndex and value and also the tooltip
handleChartClick(evt, elements) {
var chart = this.$refs.periodChart.$data._chart;
const chartIndex = chart.getElementAtEvent(evt);
if (chartIndex.length !== 0) {
const datasetIndex = chartIndex[0]._datasetIndex;
const position = chartIndex[0]._index;
const info = {
datasetIndex: datasetIndex,
valueIndex: position,
label: chart.tooltip._data.labels[position],
value: chart.tooltip._data.datasets[datasetIndex].data[position]
};
console.log(info);
} else {
console.log("Background clicked");
}

Use Dojo boxConstrainedMoveable to constrain movable div to window

I have a div, to which I applied Dojo dojo/dnd/Moveable. But, I'd like to prevent the user from dragging the div offscreen. So, I think I need to implement dojo/dnd/move/boxConstrainedMoveable.
I'm starting with this:
var dnd = new Moveable(this.domNode, {
'handle': this.titleNode
});
There's a similar SO question here:
Constrain a moveable object in Dojo. Applying that answer, I get something like this:
var dnd = new move.boxConstrainedMoveable(
'handle': this.titleNode
constraints: {
l: 0,
t: 20,
w: 500,
h: 500
},
within: true
);
But, I just can't understand how the bounding box works. I simply want the div to stay inside the window. I've tried implementing a few things with the window box, the div's margin box. Nothing's worked, and all I've made is a big mess.
I read the docs here:
http://dojotoolkit.org/api/?qs=1.9/dojo/dnd/move.boxConstrainedMoveable
Has anyone done this with Dojo? I'd be very appreciate of an example.
I looked up some old code I have and I did implement this type of movable once. This was written against Dojo 1.7, so things may have changed in 1.9. Fiddle demonstration: https://jsfiddle.net/4ev1daqr/26/
The main difference between your attempted solution and this is that the constraints property in the moveable needs to be a function rather than a static bounding box. When using the boxConstrainedMoveable module, the static bounding box should be assigned to a box property, rather than the constraints property.
This is actually a nice design, IMHO, because it allows the constraints to react to changes in application state, e.g. hiding a sidebar or moving a splitter, but it does make the simple case a bit more difficult to get working.
define(["dojo/_base/declare",
"dojo/dnd/move",
"dojo/dom",
"dojo/_base/window",
"dojo/dom-style",
"dojo/dom-geometry",
],
function (declare, move, dom, win, domStyle, domGeom) {
return declare( "my/dnd/move/BodyConstrainedMoveable", [move.constrainedMoveable], {
markupFactory: function(params, node){
return new this(node, params);
},
constructor: function(node, params) {
// Constrain the node to be within the body
this.constraints = function() {
var n = win.body(),
s = domStyle.getComputedStyle(n),
mb = domGeom.getMarginBox(n, s);
if ( this.node ) {
var menubox = domGeom.getMarginBox(this.node);
mb.w -= menubox.w;
mb.h -= menubox.h;
}
return mb;
};
}
})});