Multiple sliders with two number inputs - javascript

I'm trying to make noUiSlider work with one class on multiple locations in the same page. Right now i have 2 sliders, both are working fine, except for a strange behavior when i change the value of the second input, it actually updates the first handle and input. I'll be gratefull if someone helps me.
JSFiddle
function bindValues(price_slider, inputNumberMin, inputNumberMax){
price_slider.noUiSlider.on('update', function( values, handle ) {
var value = values[handle];
if ( handle ) {
inputNumberMax.value = Math.round(value);
} else {
inputNumberMin.value = Math.round(value);
}
});
inputNumberMin.addEventListener('change', function(){
price_slider.noUiSlider.set(this.value);
});
inputNumberMax.addEventListener('change', function(){
price_slider.noUiSlider.set(this.value);
});
}
var price_sliders = $('.price-filter');
var inputsNumberMin = $('.price-filter-min-price');
var inputsNumberMax = $('.price-filter-max-price');
for ( var i = 0; i < price_sliders.length; i++ ) {
noUiSlider.create(price_sliders[i], {
start: [0, 100],
connect: true,
range: {
'min': 0,
'max': 100
}
});
bindValues(price_sliders[i], inputsNumberMin[i], inputsNumberMax[i]);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/11.1.0/nouislider.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/11.1.0/nouislider.min.css" rel="stylesheet"/>
<div class="price-filter"></div>
<input type="number" class="price-filter-min-price">
<input type="number" class="price-filter-max-price">
<div class="price-filter"></div>
<input type="number" class="price-filter-min-price">
<input type="number" class="price-filter-max-price">

Your problem is in this:
inputNumberMin.addEventListener('change', function(){
price_slider.noUiSlider.set(this.value);
});
inputNumberMax.addEventListener('change', function(){
price_slider.noUiSlider.set(this.value); // <<< here
});
The set method takes an array of values for the whole slider. If a single value is passed, that is set on the first handle. Instead, you'll have to be explicit about which value you want to update:
price_slider.noUiSlider.set([null, this.value]); // 'null' values are ignored.
Updated fiddle:
http://jsfiddle.net/leongersen/z83oz9np/268/

Related

Svelte3 input validation

Yesterday I was trying to translate a problem I had to solve in React, to Svelte, and I can't figure it out.
The problem is the following:
I have 3 inputs each one of them holds a percentage.
The 3 percentages altogether cannot add more than 100.
I have a fourth input, it's disabled, so it just shows the remaining percentage to 100%
In react is fairly easy, declare a function that takes, the event and a variable to know which input I'm taking the event from. Do the proper validations and done.
Sadly in svelte I have almost 0 experience, and I don't know how to tackle it.
This the code so far (spoiler alert it does not even get close to do what is supposed to do).
Svelte REPL
Running a console.log to show the value of sp1, inside the function that validates it, and outside the function (before and after the function), shows what I do expect:
Before the function (before): value in the input
Inside the function: value validated
Outside the function (after): value validated
So the validation and assignment of the correct value takes place, nonetheless the input keep showing wrong value (ex: input shows 112 and the value should be 100).
A more dynamic way to do this would be using arrays.
let inputs = [{
value: 0,
color: 'GRAY'
}, {
value: 0,
color: 'DARKSLATEGRAY'
}, {
value: 0,
color: 'TEAL'
}];
This would also make calculations easier since we can use the array methods. It would also enable us to use each blocks which remove redundant code. Then we will use a function called validate and pass the index of the current input to it. Using the input we can calculate the maximum possible value that can be entered in that input and set the input if it crosses that maximum value. This validate function will be called from the template on the input event.
Here's the code.
<script>
let total = 100;
let inputs = [{
value: 0,
color: 'GRAY'
}, {
value: 0,
color: 'DARKSLATEGRAY'
}, {
value: 0,
color: 'TEAL'
}];
$: spOthers = total - inputs.map(x => x.value || 0).reduce((a, b) => a + b);
function validate(index) {
let maxInput = total - inputs.map(x => x.value).filter((x, i) => i !== index).reduce((a, b) => a + b);
if (inputs[index].value > maxInput) {
inputs[index].value = maxInput;
}
}
</script>
{#each inputs as {value, color}, i}
<div class='input-container'>
<div class='square' style="background-color: {color}" />
<input type="number" min="0" class='input' bind:value={value} on:input={() => validate(i)} />
</div>
{/each}
<div class='input-container'>
<div class='square' style="background-color: DARKORANGE" />
<input type='number' class='input input-others' bind:value={spOthers} disabled/>
</div>
Note: I have omitted the styles above as there are no changes in them.
Here is a working REPL.
Nice. You can also do:
$: spOthers = inputs.reduce((a, c, i, inputs) => a + c.value, 0);
function validate(index) {
const delta = spOthers - total;
if (delta > 0) {
inputs[index].value -= delta;
}
}

vue.js force update input element not working

I have an input element that bind with v-model to a item value, I want to limit the user input to just type numeric value on range between 0 to 10, I was tried this thing before(add #input and check the input value to keep it in range)
my code is like this:
<v-text-field #input="checkItem" v-model="item"></v-text-field>
checkItem(val) {
if(parseInt(val) < 1) {
this.item = 1;
} else if(parseInt(val) >10) {
this.item = 10;
}
}
Problem
after first time we type number out of range the function works great and keep it in range but when we type out of range number again the element didn't update because the new item value is the same as the old item value! to solve this I try to use forceUpdate and the $forceUpdate() not work!!!
for example
if user type anything between range number into input, because it's in range everything is ok;
but if user type 0 or any number outside of range, on the first time item value change to 1 if value was under 1 but if again type any negative value because the item value last time was changed to 1 when we set it to 1 again nothing happening on the view and the element value was not updated.
The main question is how to force vue to update this input field value?
<div><input type="number" v-model="item"></input></div>
</template>
<script>
export default {
name: "ranges",
data() {
return {
item: Number,
error: String
};
},
watch: {
item(newVal, lastVal) {
if (newVal > 10) this.item = 10
if (newVal < 1) this.item = 1
}
}
};
</script>
Here using the watcher you can do that validation
The only way to force the reactivity when the the final result is always the same, is to re-render the component for it to reflect the changes by updating its key.
Reference link here.
I have forked the sample Vue project from mdiaz00147, and modify into
this, and I think it works as the author intended it to be.
Solution Code modified from mdiaz00147 's code snippet
<template>
<div>
<input :key="inputKey" v-model="item" #change="checkItem" />
</div>
</template>
<script>
export default {
name: "ranges",
data() {
return {
item: null,
inputKey: 0,
};
},
methods: {
checkItem() {
if (parseInt(this.item) < 1) {
this.item = 1;
} else if (parseInt(this.item) > 10) {
this.item = 10;
}
this.inputKey += 1;
},
},
};
</script>

Show increasing and decreasing values on noUiSlider

I'm using a noUiSlider with one handle and a range from 0 to 50. I'm trying to show the increasing value of the slider on an input field to the left of the slider and the diminishing value on the right - as in the below example:
Slider
Has anyone else tried to do this or know how it can be achieved?
var connectSlider = document.getElementById('slider');
noUiSlider.create(connectSlider, {
start: 20,
tooltips: true,
decimals: 0,
connect: [true, false],
range: {
'min': 0,
'max': 50,
},
format: wNumb ({decimals:0})
});
var inputFormat = document.getElementById('slider-value');
sliderFormat.noUiSlider.on('update', function( values, handle ) {
inputFormat.value = values[handle];
});
inputFormat.addEventListener('change', function(){
sliderFormat.noUiSlider.set(this.value);
});
$("#slider").Link('lower').to('#slider-value',function(value){
$(this).val(Math.abs(value));
});
Here it is in jsfiddle
I managed to get the left value to display on the screen but don't know how to bind it to the input field. And I don't know how to get the value on the right. I assume this should be 'max value' minus 'slider value' but don't have enough knowledge of JavaScript so I would really appreciate some help with this :)
I'm working with Slider control from the 0.99.0 Materialize version and I wanted to get that behavior too. So I do some tricks as follow:
My HTML inside a form tag:
<div class="input-field col s12">
<label for="wmAttendance">Porcentaje de Asistencias</label><br>
<p class="range-field">
<input type="range" id="test5" min="0" max="100" oninput="wAttendance.value = test5.value" />
<output id="wAttendance" name="wAttendance">0<span>% - Mujeres</span></output>
<output id="mAttendance" name="mAttendance" class="right">0<span>% - Hombres</span></output>
</p>
</div>
JS needed to intercept changes on the range input
$(document).ready(function() {
$("#test5").on("input", function() {
var women = this.value, men = 100 - women;
$("#a_women").html(women);
$("#a_men").html(men);
$("#wAttendance").html(women + "<span>% - Mujeres</span>");
$("#mAttendance").html(men + "<span>% - Hombres</span>");
});
});
And voila we have make the magic happen and the result is something like this:
In this way we can catch both values to send in the form or to make some operations etc. Here you have my pen for reference.
I hope it could be useful for somebody else.
Ive had a play with you code and got this working:
var connectSlider = document.getElementById('slider');
// add max amount variable (used to calculate #slider-value-after input value)
var maxAmount = 50
noUiSlider.create(connectSlider, {
start: 20,
tooltips: true,
decimals: 0,
connect: [true, false],
range: {
'min': 0,
'max': maxAmount, // use max amount variable to set max range amount
},
format: wNumb ({decimals:0})
});
var inputFormat = document.getElementById('slider-value');
// created variable for #slider-value-after input
var inputFormat2 = document.getElementById('slider-value-after');
// for some reason you were looking for a 'sliderFormat' variable to watch for slider updates
// this wasnt created and was causing JS errors in the fiddle
// Ive updated this to the 'connectSlider' variable you created.
connectSlider.noUiSlider.on('update', function( values, handle ) {
// on update set first input value
inputFormat.value = values[handle];
// also set #slider-value-after input value by minus'ing the max value by current slider value
inputFormat2.value = maxAmount - values[handle];
});
inputFormat.addEventListener('change', function(){
connectSlider.noUiSlider.set(this.value);
});
I think it is also possible to get slider options which would remove the need to set the maxAmount variable as you could query the sliders max range amount instead but I cant remember the code for this.

AngularJs Call function on hidden input on value change $watch

I want to use $watch in order to trigger a function each time one of those 3 values is changed :
html:
<input type="hidden" name="source_x" id="source_x" ng-model="source_x"/>
<input type="hidden" name="source_y" id="source_y" ng-model="source_y"/>
<input type="hidden" name="id" id="id" ng-model="id"/>
I just started angular and I want to use the $watch to trigger a function.
Those value are changed each time I drag one div with the draggable function below :
$("#div").draggable({
helper : 'clone',
stop:function(event,ui) {
var wrapper = $("#container-emote").offset();
var borderLeft = parseInt($("#container-emote").css("border-left-width"),10);
var borderTop = parseInt($("#container-emote").css("border-top-width"),10);
var pos = ui.helper.offset();
$("#source_x").val(pos.left - wrapper.left - borderLeft);
$("#source_y").val(-(pos.top - wrapper.top - borderTop)+185);
$("#id").val(2);
}
});
I started with this but I think it is not right because if I move one div I am going to call 3 times the function ? Moreover I don't know if I can use it with input hidden.. Thank you !
//Fonction
$scope.$watch($scope.source_x, createEmote);
$scope.$watch($scope.source_y, createEmote);
$scope.$watch($scope.id, createEmote);
function createEmote(newValue, oldValue, scope){
}
UPDATE : answer fiddle
I just add a function at the end of stop of drag
jsfiddle.net/5e7zbn5z/1/
You need to use your $scope.$watch like so:
$scope.$watch(function() {
return $scope.source_x + $scope.source_y + $scope.id;
}, function() {
$scope.createEmote();
});
And have your createEmote be a function on your $scope:
$scope.createEmote = function() {
// do something with $scope.source_x, etc
}
EDIT
As noted in the comments by #Sergey the exact watcher function will be dependent on your expected data. You could also just duplicate it and change which variable is returned (Similar to your existing code) if you want.

My list becomes crazy after sorting

I'm creating a list editor using Angular and jQuery UI. The goal is to add/edit/remove/sort items.
Here's my HTML code to display the list.
<ul id="sortable" >
<li ng-repeat="cat in admin.catalogues">
<label for="title">Title</label>
<input type="text" name="title" ng-model="cat.title" />
<label for="image">Image</label><input type="text" name="image" ng-model="cat.image" istype="image" />
<button ng-click="admin.removeCatalogue(cat)">Remove</button>
</li>
</ul>
Then I have these Angular methods (I have just copied what should be useful).
this.catalogues = [];
this.addCatalogue = function () {
var newcat = {
title: "",
image: "",
pdf: ""
};
this.catalogues.push(newcat);
};
this.removeCatalogue = function (item) {
this.catalogues.splice(this.catalogues.indexOf(item), 1);
};
this.sort = function(fromIndex, toIndex){
var element = this.catalogues[fromIndex];
this.catalogues.splice(fromIndex, 1);
this.catalogues.splice(toIndex, 0, element);
};
And finally this jQuery script for the sortable part - it works using jQuery UI, and I call the Angular controller to update the list. I have probably found this snippet somewhere on Stackoverflow.
$(document).ready(function(){
startIndex = -1;
newIndex = -1;
$('#sortable').sortable({
placeholder:'placeholder',
start: function (event, ui) {
startIndex = ($(ui.item).index());
},
stop: function(event, ui){
newIndex = ($(ui.item).index());
angular.element(document.getElementById('content')).controller().sort(startIndex, newIndex);
}
});
});
Adding items, removing and sorting are all working fine. But after sorting, the list becomes crazy. New items are added more or less randomly inside the list, and the remove button sometimes removes more than one item.
I have created a Fiddle for testing: https://jsfiddle.net/Neow85/jbg8kdv4/
Thanks for any help!

Categories