justGage - increase and decrease value with buttons - justgage

I'm using justGage (a plugin for generating and animating nice & clean dashboard gauges) to program some graphs - I need to be able to increase and decrease the value by clicking a button.
I can do this by using refresh (g1.refresh(20);) - but that makes the value 20...it doesn't increase it by 20.
Can anyone help please ?
Many thanks
Al

I couldn't find a method for retrieving the gauge's current value, so I just store that value in a separate variable. Then, every time the gauge is updated, this variable is too.
Demo
<script src="http://cdn.jsdelivr.net/raphael/2.1.2/raphael-min.js"></script>
<script src="http://cdn.jsdelivr.net/justgage/1.0.1/justgage.min.js"></script>
<input type="button" id="inc" onClick="inc()" value="increase by 20" />
<input type="button" id="dec" onClick="dec()" value="decrease by 20" />
<br><br>
amount to change: <input type="text" id="change" />
<br>
<input type="button" id="incVar" onClick="incVar()" value="increase (variable)" />
<input type="button" id="decVar" onClick="decVar()" value="decrese (variable)" />
<div id="gauge" class="200x160px"></div>
<script>
var gageValue = 100;
var g = new JustGage({
id: "gauge",
value: gageValue,
min: 0,
max: 200,
title: "BMI"
});
function updateGage(n) {
g.refresh(gageValue + n);
gageValue += n;
}
function inc() {
updateGage(20);
}
function dec() {
updateGage(-20);
}
function incVar() {
updateGage(parseInt(document.getElementById("change").value))
}
function decVar() {
updateGage(-parseInt(document.getElementById("change").value))
}
</script>

Related

Can't make my Mutation work in vue apollo client [duplicate]

I'm running into the problem, that Vue converts the value of an input field of type number into a string and I just can't figure out why. The guide I am following along does not run into this issue and get's the values as numbers, as expected.
The vue docs state, that Vue would convert the value to a number, if the type of the input is number.
The code is originated from a component, but I adjusted it to run in JSFiddle: https://jsfiddle.net/d5wLsnvp/3/
<template>
<div class="col-sm-6 col-md-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">
{{ stock.name }}
<small>(Price: {{ stock.price }})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input type="number" class="form-control" placeholder="Quantity" v-model="quantity"/>
</div>
<div class="pull-right">
<button class="btn btn-success" #click="buyStock">Buy</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['stock'],
data() {
return {
quantity: 0 // Init with 0 stays a number
};
},
methods: {
buyStock() {
const order = {
stockId: this.stock.id,
stockPrice: this.stock.price,
quantity: this.quantity
};
console.log(order);
this.quantity = 0; // Reset to 0 is a number
}
}
}
</script>
The quantity value is the issue.
It is initialized with 0 and when I just press the "Buy" button, the console shows:
Object { stockId: 1, stockPrice: 110, quantity: 0 }
But as soon as I change the value by either using the spinners or just type in a new value, the console will show:
Object { stockId: 1, stockPrice: 110, quantity: "1" }
Tested with Firefox 59.0.2 and Chrome 65.0.3325.181. Both state that they are up to date. I actually also tried it in Microsoft Edge, with the same result.
So what am I missing here? Why is Vue not converting the value to a number?
You need to use .number modifier for v-model, like this:
<input v-model.number="quantity" type="number">
Note: empty string ('') is not converted to a number, so you may need to handle it separately.
Change the order object to :
const order = {
stockId: this.stock.id,
stockPrice: this.stock.price,
quantity: +this.quantity
};
This will automatically parse the string to a number.
In general the data from HTML inputs are strings. The input type only checks if a valid string has been provided in the field.

Vue.js adding a toggle and method to a button

I have a button that should toggle and also call a method. How do I achieve this? Seems like it can be only one or the other.
new Vue({
el: "#app",
data: {
iExist:false,
iDoNotExist: true,
},
methods: {
iSignedUpforThis: function(){
console.log("step X");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<p v-show="iExist"> i EXISTS </p>
<p v-show="iDoNotExist">
<strong> You are not found: </strong>
<form >
First name:<br>
<input type="text" name="firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
</form>
<BUTTON v-on:click="iExists = iDoNotExist">
TOGGLE MY EXISTENCE
</BUTTON>
</div>
Move
iExists = iDoNotExist to a method:
methods: {
iSignedUpforThis: function(){
this.iExist = this.iDoNotExist
console.log("step X");
}
}
<button v-on:click="iSignedUpForThis">
TOGGLE MY EXISTENCE
</button>
First off to accomplish your desired result you need only one Boolean variable. Then in your method just switch between true and false. Also you have an invalid markup - there is closing tap p but no closing. That's why your example does not work.
Notice: it's bad idea to nest form tag inside p tag, so use div instead. It's considered a good practice to associate your input with it's label using label tag. Also there is shortcut for v-on:click - #click. data should be an function that returns an object, this will prevent . multiple instance to share the same object.
If you follow above recommendations you will make your code much clear and bug-less.
new Vue({
el: '#app',
data: {
isExist: false,
},
methods: {
method() {
this.isExist = !this.isExist
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-show="isExist">I exist</div>
<div v-show="!isExist">
<strong>You are not found:</strong>
<form>
<label>First name:<br>
<input type="text" name="firstname" value="Mickey">
</label>
<br>
<label>Last name:<br>
<input type="text" name="lastname" value="Mouse">
</label>
</form>
</div>
<button #click="method">Toggle</button>
</div>
It might be late but I am sure it will help others. Create a component ToggleButton.js and paste the below codes.
<template>
<label for="toggle_button">
<span v-if="isActive" class="toggle__label">On</span>
<span v-if="! isActive" class="toggle__label">Off</span>
<input type="checkbox" id="toggle_button" v-model="checkedValue">
<span class="toggle__switch"></span>
</label>
</template>
<script>
export default {
data() {
return {
currentState: false
}
},
computed: {
isActive() {
return this.currentState;
},
checkedValue: {
get() {
return this.defaultState
},
set(newValue) {
this.currentState = newValue;
}
}
}
}
</script>
Take a look at the article to learn more https://webomnizz.com/create-toggle-switch-button-with-vue-js/

Trying to pull data from Vue components in single file components

This is my first time experimenting with Vue.js so it's altogether possible I'm missing something very obvious.
Basically, I have a component that calculates the number of boxes needed for a certain quantity of a printed piece (I work in the print industry).
If needed, I have a button to create an additional component if the printed piece has multiple parts.
I'd like to have a reactive way to update the total number of boxes needed for all parts, but I just can't seem to get there.
Here's a link to my Gitlab repo with the code: https://gitlab.com/dsross/printutils
Any help would be appreciated.
I'm also using Browserify to write the build.js and build.css files referenced in index.html.
Here are my files, in case no one wants to look at the repo:
App.vue
<template>
<div id="app">
<div>
</div>
<div>
<calculator v-for="(part, index) in parts" :key="index"></calculator>
<br />
<br />
<div class="card shadow-sm">
<div class="card-body">
<button type="button" class="btn" #click="addPart()">Add Part</button>
<button type="button" class="btn" #click="totalBoxes">Total Boxes</button>
<span>Box Total (all parts): </span><span id="grandtotal"></span>
</div>
</div>
</div>
</div>
</template>
<script>
// import Hello from './components/Hello.vue'
import Calculator from './components/Calculator.vue'
export default {
name: 'app',
components: {
Calculator
},
methods: {
addPart: function () {
console.log("Adding part");
this.parts.push(Calculator);
},
totalBoxes: function () {
console.log("totalBoxes called");
let totalBoxes = 0;
let partTotals = document.querySelectorAll("#partBoxTotal");
for (var i = 0; i < partTotals.length; i++) {
totalBoxes += parseInt(partTotals[i].innerHTML);
}
this.totalBoxCount = totalBoxes;
document.getElementById("grandtotal").innerHTML = totalBoxes;
}
},
data: function () {
return {
parts: [Calculator],
totalBoxCount: 0
}
},
}
</script>
Calculator.vue
<template>
<div class="card shadow-sm" id="boxCalculator">
<div class="card-body">
<form>
<div class="form-group">
<p>Paper:
<select class="custom-select" v-model="paperWeight">
<option v-for="(mweight, paper) in mweights" :key="mweight" v-bind:value="paper">{{paper}}</option>
</select>
</p>
<br />
<br />
<p>Final Width:
<input class="form-control" type="text" v-model="finalWidth" id="finalWidth" value="">
</p>
<p>Final Height:
<input type="text" class="form-control" v-model="finalHeight" id="finalHeight" value="">
</p>
<p>Sheets Per Unit:
<input type="text" class="form-control" v-model="numberOfSheets" id="numberOfSheets" name="numberOfSheets"
value="">
</p>
<p>Quantity:
<input type="text" class="form-control" v-model="quantity" id="quantity" name='quantity'>
</p>
<p>Stitched:
<input type="checkbox" v-model="stitched" name="stitched" id="stitched" value="">
</p>
</div>
</form>
<div class="card">
<div class="card-body">
<div id='results'>
<p id="partWeightTotal">Part Total Weight: {{ totalWeight }}</p>
<p><span>Part Box Total: </span><span id="partBoxTotal">{{ boxQuantity }}</span></p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {
mWeights,
stitchedMultiplier,
maxArea
} from "../constants.js"
module.exports = {
data: function () {
return {
paperWeight: this.selected,
paperType: "",
finalWidth: "",
finalHeight: "",
numberOfSheets: "",
quantity: "",
stitched: "",
boxes: "",
mweights: mWeights
}
},
computed: {
squareInches: function () {
return this.finalHeight * this.finalWidth;
},
squareInchWeight: function () {
let mWeight = mWeights[`${this.paperWeight}`];
return (mWeight / 1000) / maxArea;
},
totalWeight: function () {
return ((this.squareInches * this.squareInchWeight) * this.numberOfSheets) * this.quantity;
},
boxQuantity: function () {
let boxes = this.totalWeight / 35;
if (this.stitched) {
this.boxes = Math.ceil(boxes * stitchedMultiplier);
// this.$root.$emit('box-change', this.boxes);
return this.boxes
} else {
this.boxes = Math.ceil(boxes);
// this.$root.$emit('box-change', this.boxes);
return Math.ceil(this.boxes);
};
},
},
}
</script>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>boxcalculator2</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../favicon.png">
<title>Box Calculator</title>
<!-- Bootstrap core CSS -->
<link href="dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="dist/sticky-footer.css" rel="stylesheet">
<link rel="stylesheet" href="dist/build.css">
</head>
<body>
<div class="container">
<div class='row'>
<div class='col'>
<div id="app"></div>
</div>
</div>
</div>
<script src="dist/build.js"></script>
</body>
</html>
If I understand correctly, you'd like the App's total box count to be updated automatically whenever the individual Calculators determine their box counts. One way to do this is to emit an event from Calculator when its box count changes, which could be monitored with a watcher.
There are a couple issues we'll address below:
It doesn't make sense (and is inefficient) to store Calculator -- a single-file-component definition -- in this.parts[]. Instead, it could store meaningful data points, such as Calculator's output.
Instead of DOM manipulation (i.e., querying the DOM for an element to get/set its value), opt for modeling the data in Vue, and using interpolation in the template. This lets Vue automatically display the updated value in the target element. It also obviates the element ID assignments (assuming they were used exclusively for DOM manipulation), simplifying the template for improved readability.
Storing Calculator output
In App, we must use this.parts[] to track the result of each part's calculation (which we'll capture below). We'll define each array element (i.e., a "part") to be:
{
boxes: 0 // box count for this part
}
This definition allows a computed property (which we'll define later), based on .boxes, to be reactive.
So, in addPart() and the data option:
// App.vue
export default {
// ...
methods: {
addPart() {
this.parts.push({ boxes: 0 });
}
},
data() {
return {
parts: [{ boxes: 0 }]
}
}
}
Notifying App of Calculator output
Typically, parents pass data to children via props, and children communicate data to parents with events. Alternatives include using a state management library, such as Vuex, but for the sake of simplicity, we'll use events here.
In Calculator, we want to notify the parent (App) of changes to the boxes value, so we'll add a watcher that emits an event (e.g., called boxes-changed) whenever boxes changes:
// Calculator.vue
export default {
//...
watch: {
boxes(value) {
this.$emit('boxes-changed', value);
}
}
}
In App, we'll listen to the boxes-changed event, and copy the event detail's value to the current part's boxes variable, where part is the current array element of parts[] being iterated.
// App.vue
<calculator v-for="(part, index) in parts" #boxes-changed="part.boxes = $event" :key="index"></calculator>
Breakdown of #boxes-changed="part.boxes = $event":
#boxes-changed="..." - listen to boxes-changed event emitted from <calculator>
part.boxes = $event - set part.boxes to value of event detail
Making totalBoxCount reactive
With the changes above, we have the tools needed to make App's totalBoxCount reactive:
Change totalBoxCount into a computed property that sums up the .boxes fields of this.parts[]. This property will be computed automatically whenever array elements of this.parts[] changes.
// App.vue
export default {
computed: {
totalBoxCount() {
// Note: Alternatively, use a simple for-loop to sum .boxes
return this.parts
.filter(p => p.boxes && !Number.isNaN(p.boxes) // get only parts that have a positive `.boxes` value
.map(p => p.boxes) // map object array into an integer array of `.boxes` values
.reduce((p,c) => p + c, 0); // sum all array elements
},
},
data() {
return {
parts: [],
// totalBoxCount: 0 // CHANGED INTO COMPTUED PROP ABOVE
}
}
}
In App's template, use string interpolation to display totalBoxCount:
<!--
<span>Box Total (all parts): </span><span id="grandtotal"></span>
--> <!-- DON'T DO THIS -->
<span>Box Total (all parts): {{totalBoxCount}}</span>
We might as well remove the Total Boxes button (previously used to manually trigger a calculation) from the template:
<!--
<button type="button" class="btn" #click="totalBoxes">Total Boxes</button>
--> <!-- DELETE -->
and its associated click-handler:
// App.vue
export default {
methods: {
// totalBoxes: function() { /* .. */ } // DELETE THIS
}
}
demo

Remove listing from Vuejs1 vs Vuejs2

Currently I am using Vuejs2 to generate listing for image upload, which will show preview image after select an image file, here is my code written in codepan with Vuejs2:
https://codepen.io/anon/pen/MELZKe
<div v-for="(image, index) in images" :key="index">
<div class="image-upload">
<label v-bind:for="'cover-upload-' + index">
<img src="http://via.placeholder.com/100x100" width="100" class="preview-img"/>
</label>
<input v-bind:id="'cover-upload-' + index" v-bind:name="'slides[' + index + ']'" type="file" class="upload-preview" style="display:none">
</div>
<button type="button"
#click.prevent="removeImage(index)"
v-show="image_quantity > 1">
Remove
</button>
</div>
I created 2 file upload images, upload first image (will preview your image), delete the first preview image, but it will delete the last preview image instead of first one.
However, almost same coding but using Vuejs1, it will delete the first preview image instead of last one. Here is codepen with Vuejs1:
https://codepen.io/anon/pen/VMgqbG
I have no idea how to write such situation for Vuejs2 or I have to stick with Vuejs1?
Thanks.
Using the index of an array as the key is a bad idea. This is because indexes mutate. Use a proper key and your issue is resolved.
Think about the case in the original code where there are two images in the images array. At first, the index of image one is zero and the index of the second image is one. When you delete the first image, now the index of what was originally was the second image has changed to zero, but Vue has existing DOM elements for key zero that show the image for the former object that was at index zero. Since Vue has those DOM elements for key zero, it re-uses them and it appears as though the first image was not deleted.
console.clear()
new Vue({
el: '#content',
data: {
images: [{ name: '' , id: 1}],
},
computed: {
image_quantity: function () {
return this.images.length;
},
},
methods: {
removeImage: function (index) {
this.images.splice(index, 1);
},
addImage: function (event) {
event.preventDefault();
this.images.push({
name: '',
id: Math.max(...this.images.map(i => i.id)) + 1
});
}
}
});
function readURL(input, preview) {
if (input.files && input.files[0] && input.files[0].type.match('image.*')) {
var reader = new FileReader();
reader.onload = function (e) {
$(preview).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$(document).on('change', '.upload-preview', function () {
readURL(this, $(this).prev().find('.preview-img'));
});
<div id="content">
<div v-for="(image, index) in images" :key="image.id">
<div class="image-upload">
<label v-bind:for="'cover-upload-' + index">
<img src="https://via.placeholder.com/100x100" width="100" class="preview-img"/>
</label>
<input v-bind:id="'cover-upload-' + index" v-bind:name="'slides[' + index + ']'" type="file" class="upload-preview" style="display:none">
</div>
<button type="button"
#click.prevent="removeImage(index)"
v-show="image_quantity > 1">
Remove
</button>
</div>
<button type="button" v-on:click="addImage">Create New</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>

How to turn off the webcam after using Pubnub?

I started to use Pubnub for making video group chats. However, when I was testing it, I found a little problem: As I connect my computer to their servers, my webcam turns on and never turns off, unless I leave the page.
However, I wish to be able to close a video chatting, and turning off the webcam at the same time. How to do it?
Thank you very much!
EDIT: Here is a code I'm using for my tests, I'm using the one given in the tutorial:
<script src="js/jquery-2.1.4.min.js"></script>
<script src="js/jquery-ui.min.js"></script>
<script src="js/pubnub-3.7.18.min.js"></script>
<script src="js/webrtc.js"></script>
<script src="js/rtc-controller.js"></script>
<div id="vid-box"></div>
<div id="vid-thumb"></div>
<form name="loginForm" id="login" action="#" onsubmit="return login(this);">
<input type="text" name="username" id="username" placeholder="Pick a username!" />
<input type="submit" name="login_submit" value="Log In">
</form>
<form name="callForm" id="call" action="#" onsubmit="return makeCall(this);">
<input type="text" name="number" placeholder="Enter user to dial!" />
<input type="submit" value="Call"/>
</form>
<div id="inCall"> <!-- Buttons for in call features -->
<button id="end" onclick="end()">End</button> <button id="mute" onclick="mute()">Mute</button> <button id="pause" onclick="pause()">Pause</button>
</div>
<script>
var video_out = document.getElementById("vid-box");
var vid_thumb = document.getElementById("vid-thumb");
function login(form) {
var phone = window.phone = PHONE({
number : form.username.value || "Anonymous", // listen on username line else Anonymous
media : { audio : true, video : true },
publish_key : 'pub-c-c66a9681-5497-424d-b613-e44bbbea45a0',
subscribe_key : 'sub-c-35aca7e0-a55e-11e5-802b-02ee2ddab7fe',
});
var ctrl = window.ctrl = CONTROLLER(phone);
ctrl.ready(function(){
form.username.style.background="#55ff5b"; // Turn input green
form.login_submit.hidden="true"; // Hide login button
ctrl.addLocalStream(vid_thumb); // Place local stream in div
}); // Called when ready to receive call
ctrl.receive(function(session){
session.connected(function(session){ video_out.appendChild(session.video); });
session.ended(function(session) { ctrl.getVideoElement(session.number).remove(); });
});
ctrl.videoToggled(function(session, isEnabled){
ctrl.getVideoElement(session.number).toggle(isEnabled); // Hide video is stream paused
});
ctrl.audioToggled(function(session, isEnabled){
ctrl.getVideoElement(session.number).css("opacity",isEnabled ? 1 : 0.75); // 0.75 opacity is audio muted
});
return false; //prevents form from submitting
}
function makeCall(form){
if (!window.ctrl) alert("Login First!");
else ctrl.dial(form.number.value);
return false;
}
function end(){
ctrl.hangup();
}
function mute(){
var audio = ctrl.toggleAudio();
if (!audio) $("#mute").html("Unmute");
else $("#mute").html("Mute");
}
function pause(){
var video = ctrl.toggleVideo();
if (!video) $('#pause').html('Unpause');
else $('#pause').html('Pause');
}
</script>
Note that I tried to find the function through the console in addition to my searches, but I was unable to find it...