knockout.js variable update on attr change - javascript

I've got a jQuery UI slider. When the user uses a slider, the slider updates an attr variable in the div tag of the slider
$(".slider").each(function() {
var value = parseInt($(this).attr('data-init-value'), 10);
var amin = parseInt($(this).attr('data-min'), 10);
var amax = parseInt($(this).attr('data-max'), 10);
console.log(value, " ", amin, " ", amax)
$(this).empty().slider({
value : value,
min : amin,
max : amax,
range : "min",
animate : true,
slide : function(event, ui) {
$(this).attr('data-value', ui.value);
}
});
});
The example div tag in the html:
<div class="slider" data-min="200" data-max="600" data-init-value="300" data-bind="attr: { 'data-value': someValue }"></div>
When the slider is changed the data-value is updated in the <div> but the js variable doesn't change. (in other, trivial binding cases - like text: - it works.)
How to bind this action?

I suggest you do this the other way around. Bind the value of the slider to an observable property on your viewmodel, and then bind the attribute to that observable.
Then you can always access the most recent value of the slider directly through the viewmodel, and the UI will stay up to date as well.
And further, if you want to subscribe to the update event of that observable, you can bind to that as well. Here is an example from the documentation:
myViewModel.personName.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
And finally, this might as well be a possible duplicate of: Identify the attribute change event in KnockoutJS?
-- Update to answer comments
Given the following viewmodel:
var viewModel = {
mySliderValue: ko.observable(0)
};
Then, in your slider callback, you could do something like this:
viewModel.mySliderValue(value);
And then, in your view, having the following attr binding:
data-bind="attr: { 'data-value': mySliderValue }"
... will cause the UI to update when the observable changes its value.
PS. I suggest you no longer delete this thread since my answer is starting to deviate more and more from the one I linked to.

The way to do it is to register an event handler.
o.utils.registerEventHandler(element, "slidechange", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
JSFiddle for full example:
http://jsfiddle.net/snLk8/3/

Related

how to pass a value to a data-attribute?

How do I pass one data-attribute value to another?
I have the following input:
<input type="radio" class="d-none" name="varieties" value="option_0" data-price-package-value-id-0="2 625">
Where I need to transfer the value of data-price-package-value-id-0 to the following element:
<li id="front_0" data-value="1 625">
Image Preview
Here's the JavaScript I have so far:
// Radio buttons prise
$(document).ready(function(){
$('input[name="varieties"]:checked').change(function() {
var package_select = this.attr('data-price-package-value-id-0');
var dataval = document.getElementById("0").attr('data-value');
dataval = package_select;
});
});
I also have a range input that already pulls the value out of li this already works but in another function,
now I have a task to pass the value from radio to li
First you should probably not be using ID's that start with a number, as it makes it harder to select in CSS and jQuery, but here you go:
$(document).ready(function(){
$('input[name="varieties"]').change(function() {
var package_select = $(this).attr('data-price-package-value-id-0');
$(document.getElementById("0")).attr('data-value', package_select );
});
});
attr() is a jQuery method. If you want to do this with vanilla javascript, then you will want to use
this.getAttribute('data-price-package-value-id-0')
this.setAttribute('data-price-package-value-id-0', newValue)
If you want to use jQuery then use
$(this).data('price-package-value-id-0') //getter
$(this).data('price-package-value-id-0', newValue) //setter
Or you can use attr() but do not intermix the use of attr() and data()
$(this).attr('data-price-package-value-id-0') //getter
$(this).attr('data-price-package-value-id-0', newValue) //setter
const element = document.getElementById('front_0');
element.setAttribute('data-value', 'my spectacular new value');

jquery storing dynamic innerhtml into usable jquery variable

var = cooldynamicelement
How could I store the inner html I grab with jQuery from my div ie. <div class="username"> </div> to store as an accessible variable in jQuery eg. cooldynamicelement so I can grab and use at different areas of my site by just calling ie. $cooldynamicelement and updates with the dynamic .username element value.
1. Store HTML into localStorage
var dynamicElementHTML = localstorage.dynamicElementHTML || $(".username").html() || "";
localstorage["dynamicElementHTML"] = dynamicElementHTML;
To make it available to other pages a way would be to use the power of localstorage
https://developer.mozilla.org/en/docs/Web/API/Window/localStorage
If you're actually interested in the whole element (not only it's inner HTML) than instead of .html() use .prop("outerHTML")
2. Binding using jQuery (essential idea)
If you only want a way to reflect some variable HTML as actual html and make it alive you could do like:
var $myElement = $("<div />", {
class : "userData",
append : $someDynamicElements,
appendTo : $someParentElement,
on : {
contentUpdate : function() {
$(this).html( $someDynamicElements );
}
}
});
than whenever your $someDynamicElements changes you can trigger a contentUpdate
$myElement.trigger("contentUpdate")
3. Binding using jQuery (concept)
Here's the same elements binding concept gone wild:
// Here we will store our elements
var EL = {};
// Create desired HTML elements like this:
var LIST = {
username: $("<b/>", {
html : "UNKNOWN",
click : function() {
alert( $(this).text() );
}
}),
email: $("<a/>", {
html : "test#test.test",
href : "mailto:"+ "test#test.test"
}),
// add more here, you got the idea.
// don't forget that you can assign any JS / jQuery propery to your element.
// You can go insane using .on() and later .trigger()
};
// Our small "program" that replaces data-bind elements
// with dynamic elements from our list
$("[data-bind]").replaceWith(function(i){
var bind = this.dataset.bind;
if(!LIST[bind]) return;
if(!EL.hasOwnProperty(bind)) EL[bind] = [];
var klon = LIST[bind].clone(true)[0];
EL[bind].push(klon);
return klon;
});
// That's it. Now goes your code ///////////////
$(EL.username).css({color:"red"}); // just to test if it works :D
$("[data-target]").on("input", function(){
var target = this.dataset.target;
$(EL[target]).html( this.value );
});
// P.S: Even having thousands of elements inside EL
// say you have "EL.tableRows" you can do fabulously
// quick stuff like i.e: sorting, cause you iterate over a plain JS array.
// After the sorting of EL.tableRows is done and you need a jQuery
// representation simply use $(EL.tableRows).
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Dynamic element Binding in jQuery</h2>
Enter some text and see the update trigger in different places<br>
<input data-target="username"><br>
Welcome <span data-bind="username"></span> !!<br>
You name is <span data-bind="username"></span> Click the red text!<br>
<span data-bind="email"></span>
Well if you want to have the jqueryObject in a variable, just do this:
$(function(){
window.$cooldynamicelement = $("div.username");
})
that way you're able to use $cooldynamicelement in a global context. If is that what you want. This way you're saving a reference to your .username element and thus every time you use it will be updated.
NOTE: If you decide to do this, be careful with polluting your global context.:

Getting value of jQuery slider

html
<h1>HTML Slider Test</h1>
<div class="slider" data-max="100"></div>
<p>Your slider has a value of <span class="slider-value"></span></p>
<input type="button" value="send" class="send">
<div class="slider" data-max="400"></div>
<p>Your slider has a value of <span class="slider-value"></span></p>
<input type="button" value="send" class="send">
jQuery
var a = 0;
$(".slider").each(function() {
$(this).slider({
value : 5,
min : 1,
max : $(this).data('max'),
step : 1,
slide : function (event, ui) {
a = ui.value;
$(this).next().find('span.slider-value').html(ui.value);
}
});
});
$(".send").click(function () {
var c=$(".slider-value").text();
alert(c);
});
on clicking first button i want value of that alone..but i am getting the value of both sliders.
http://jsfiddle.net/5TTm4/1906/
$(".slider-value") is returning both fields. Use Refiners to get a specific one.
Example:
$(".slider-value").first()
$(".slider-value").last()
http://jsfiddle.net/5TTm4/1909/
Dynamic solution
Simply create the button and its click event on the fly (or attach to an inline button) when creating the slider.
var a = 0;
$(".slider").each(function() {
var slider = this;
$(slider).slider({
value : 5,
min : 1,
max : $(this).data('max'),
step : 1,
slide : function (event, ui) {
a = ui.value;
$(this).next().find('span.slider-value').html(ui.value);
}
});
var button = $('<button>send</button>');
$(button).click(function() {
alert( $(slider).slider("option", "value"));
});
$(slider).next().find('span.slider-value').after($("<br />"), button);
});
Demo
You need a way of identifying the slider value you want to take, which currently wasn't possible without hardcoding it: .prev().prev().find(...blabla), which is a bad way of doing it, since your structure might change.
I updated your jsfiddle to make it work and give an example of how to easily do this using a data attribute and an ID: http://jsfiddle.net/5TTm4/1908/
You basicly give the button a selector of what element it is 'bound' to: data-slider="#slider-value-2"
You also give the slider value an id that matches that selector: id="slider-value-2"
Modify the onClick function:
var $this = $(this);
var c=$($this.attr('data-slider')).text();
Now you have a flexible way of retrieving values and binding elements to the buttons without being dependent on the dom. I suggest using the same technique for binding the value elements to the slider itself.
While i'm at it: cache the value of $(this), it's faster and saves you a lot of scoping issues if you expand your code.
$(".slider").each(function() {
var $this = $(this);
$this.slider({
value : 5,
min : 1,
max : $this.data('max'),
step : 1,
slide : function (event, ui) {
a = ui.value;
$this.next().find('span.slider-value').html(ui.value);
}
});
});

How to change the containment of an object on load in jQuery

I am currently trying to change the containment of jquery page items as a page loads. The current set up is that each of the items is associated with a class which then sets up the draggable properties of all the items.
Given particular actions, I need to be able to change the containment of particular bars to allow them to move inside different bounds than those initially set.
I've attached a jsFiddle (http://jsfiddle.net/cyVYq/28/) demonstrating what I am currently doing which for some reason will not break the original containment that has been set. Any help would be appreciated.
//find the elements that have changed on pback
var newlyConstrainedItems = $("[id*=container]");
$.each(newlyConstrainedItems, function (key, value) {
var barID = this.id.split("_");
$(barID[1]).draggable({
containment: $("#" + this.id)
});
});
Thanks
You forgot the hash symbol in your id selector:
var newlyConstrainedItems = $("[id*=container]");
$.each(newlyConstrainedItems, function (key, value) {
var barID = this.id.split("_");
$('#'+barID[1]).draggable({
containment: $("#" + this.id)
});
});

Knockout.js CSS Computed From Observables

I have two observables, one that specifies a module name and the other that sets a height for some of its children. I am trying to make a custom css computed observable that includes these two observables like so:
self.moduleId = ko.observable('dbm-name');
self.slideWidth = ko.observable(75);
self.css = ko.observable('');
self.setcss = ko.computed({
read: function() {
return '#' + self.moduleId() +
' {\n\tbackground: #fff;\n\tcolor: #000;\n}
\n.cont_width {\n\twidth: ' + self.slideWidth() +
'%;\n}';
},
write: function(value) {
self.css(value);
return value;
}
});
but it should also allow the user to manually write/edit some css. The problem is that once either moduleId or slideWidth is changed the css observable is not updated unless the user goes into the custom css textarea and makes a change (ie. add and remove a space just to get it to update). How can I automatically update the css observable when either of the other two observable values change? Or is there a better way of setting up my css functionality?
You can specify the read function to also set your css:
self.setcss = ko.computed({
read: function() {
var _css = '#' + self.moduleId() +
'{\n\tbackground: #fff;\n\tcolor: #000;\n}' +
'\n.cont_width {\n\twidth: ' + self.slideWidth() +
'%;\n}';
self.css(_css);
},
write: self.css
});
But like this you can only have one of the values as your css, either the user typed css or the computed product, at each moment in time.
But I suppose you could modify the read in such a way that it either concatenating the values to add the new one at the end of the css you already have.
here is a working fiddle

Categories