Knockout js jquery range slider && 2 inputs - javascript

I need a some help. I have a code http://jsfiddle.net/ZNvWR/19/. I'm newbie in knockout, and I can't find any solution.
So, how to rewrite this code for getting working inputs (change values in inputs changes slider values)?
<div data-bind="jqSlider: percent, jqOptions: { min: 0, max: 100, range:true }"></div>
<hr/>
Percent: <input data-bind="value: percent()[0]" />
Percent: <input data-bind="value: percent()[1]" />
ko.bindingHandlers.jqSlider = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize the control
var options = allBindingsAccessor().jqOptions || {};
$(element).slider(options);
//handle the value changing in the UI
ko.utils.registerEventHandler(element, "slide", function() {
//would need to do some more work here, if you want to bind against non-observables
var observable = valueAccessor();
observable($(element).slider("values"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).slider("destroy");
});
},
//handle the model value changing
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).slider("values", value);
}
};
var viewModel = {
percent: ko.observableArray([10,50])
};
ko.applyBindings(viewModel)

I just helped antoher SO user with a slider, it can be altered like this to do what you want
http://jsfiddle.net/N9uwx/3/
<input data-bind="value: min" /><input data-bind="value: max" /><div data-bind="slider: { min: min, max: max }, sliderOptions: {min: 0, max: 100, step: 1}"></div>

Related

Unable to process binding "bootstrapSwitchOn: function (){return Exclude }"

I am making an ajax call and loading the data to ko.observablearray([]) call self.AllMedia; Then array is loaded I am doing for each of media. When I am doing binding ko.applyBindings(self); I am getting an error stating:
knockout-3.4.0.js:72 Uncaught TypeError: Unable to process binding "foreach: function (){return AllMedia }"
Message: Unable to process binding "bootstrapSwitchOn: function (){return Exclude }"
Message: valueBinding.value is not a function
Html:
<div id="draggablePanelList" data-bind="foreach: AllMedia">
<div class="col-sm-3 list-unstyled">
<div class="panel panel-info">
<div class="panel-heading">
<div class="col-sm-4 text-center">
<label #*for="IsDisabled0"*# class="control-label">Disabled</label>
<input type="checkbox" id="IsDisabled0" data-bind="bootstrapSwitchOn: { value: Exclude, options: {size: 'mini'}}" />
</div>
</div>
</div>
</div>
</div>
Viewmodel:
self.load = function() {
self.service.getProductDetail(self.Id(), self.populate);
}
self.populate = function(data) {
ko.mapping.fromJS(data, "", self);
self.loadSubProductsandMedia(data.SubProducts);
}
self.loadSubProductsandMedia = function(data) {
self.AllMedia.removeAll();
for (var j = 0; j < data.length; j++) {
for (var a = 0; a < data[j].Media.length; a++) {
self.AllMedia.push(data[j].Media[a]);
}
}
}
ko.applyBindings(self);
ko.bindingHandlers.bootstrapSwitchOn = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var valueBinding = valueAccessor();
var options = {
state: valueBinding.value(),
size: null,
animate: true,
disabled: false,
readonly: false,
indeterminate: false,
inverse: false,
radioAllOff: false,
onColor: 'primary',
offColor: 'default',
onText: 'ON',
offText: 'OFF',
labelText: ' ',
handleWidth: 'auto',
labelWidth: 'auto',
baseClass: 'bootstrap-switch',
wrapperClass: 'wrapper',
onInit: function(event, state) { },
onSwitchChange: function (event, state) { }
};
ko.utils.extend(options, valueBinding.options);
$(element).bootstrapSwitch(options);
$(element).on('switchChange.bootstrapSwitch', function (e, data) {
valueBinding.value(data);
}); // Update the model when changed.
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var vStatus = $(element).bootstrapSwitch('state');
var vmStatus = ko.utils.unwrapObservable(valueAccessor().value);
if (vStatus !== vmStatus) {
$(element).bootstrapSwitch('state', vmStatus);
}
}
};
From the error message, it looks like the value property on valueBinding, is not a function/observable.
var options = {
state: valueBinding.value(),
...
}
{ value: Exclude, options: {size: 'mini'} is being passed into the binding, so value is just a reference to Exclude on the object that is being pushed on the AllMedia observable array, i.e. self.AllMedia.push(data[j].Media[a]).
If Exclude is not a function/observable, then it should be accessed as just valueBinding.value. If you don't know if it is an observable or not, you can use ko.unwrap(valueBinding.value) to safely get the value.
Well the answer was easy, it was because of the push that i was doing
self.AllMedia.push(data[j].Media[a]);
self.AllMedia is an observable array and the elements in the Array needs to be functions to make the switch workable.
Solution:
var media = {
Id: ko.observable(data[j].Media[a].Id),
Default: ko.observable(data[j].Media[a].Default)}
and then push media

Make slider steps specific values

I have a slider which has min 1, max 24 and steps of 1. Is it possible I can make the steps as 1, 2, 3, 4, 12, 18, 24? That's all the values that will be shown when the slider changes left or right.
<input id="ex1" data-slider-id="ex1Slider" type="text" data-bind="sliderValue: {value: loanperiod, min:0, max: 24, step: 1, formatter:formatter1}, event: { change: $root.getInvestmentDetailsForBorrower }, valueUpdate: 'afterkeydown' " style="display: none;">
Slider has knockout.js binding.
If needed then I am adding the slider binding here
// Custom binding for slider value
(function ($) {
ko.bindingHandlers.sliderValue = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var params = valueAccessor();
// Check whether the value observable is either placed directly or in the paramaters object.
if (!(ko.isObservable(params) || params['value']))
throw "You need to define an observable value for the sliderValue. Either pass the observable directly or as the 'value' field in the parameters.";
// Identify the value and initialize the slider
var valueObservable;
if (ko.isObservable(params)) {
valueObservable = params;
$(element).slider({value: ko.unwrap(params)});
}
else {
valueObservable = params['value'];
if (!Array.isArray(valueObservable)) {
// Replace the 'value' field in the options object with the actual value
params['value'] = ko.unwrap(valueObservable);
$(element).slider(params);
}
else {
valueObservable = [params['value'][0], params['value'][1]];
params['value'][0] = ko.unwrap(valueObservable[0]);
params['value'][1] = ko.unwrap(valueObservable[1]);
$(element).slider(params);
}
}
// Make sure we update the observable when changing the slider value
$(element).on('slide', function (ev) {
if (!Array.isArray(valueObservable)) {
valueObservable(ev.value);
}
else {
valueObservable[0](ev.value[0]);
valueObservable[1](ev.value[1]);
}
}).on('change', function (ev) {
if (!Array.isArray(valueObservable)) {
valueObservable(ev.value.newValue)
}
else {
valueObservable[0](ev.value.newValue[0]);
valueObservable[1](ev.value.newValue[1]);
}
});
// Clean up
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).slider('destroy');
$(element).off('slide');
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var modelValue = valueAccessor();
var valueObservable;
if (ko.isObservable(modelValue))
valueObservable = modelValue;
else
valueObservable = modelValue['value'];
if (!Array.isArray(valueObservable)) {
$(element).slider('setValue', parseFloat(valueObservable()));
}
else {
$(element).slider('setValue', [parseFloat(valueObservable[0]()),parseFloat(valueObservable[1]())]);
}
}
};
})(jQuery);
// Custom binding for slider
(function ($) {
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
$(element).slider(options);
ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
ko.utils.registerEventHandler(element, "slide", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (isNaN(value)) value = 0;
$(element).slider("value", value);
}
};
})(jQuery);
I think you just want to have a function that takes the raw slider value and translates it to your custom values. Then use the result of that wherever you need the value.
vm = {
sliderValues: [1, 2, 3, 4, 12, 18, 24],
rawSliderValue: ko.observable(1),
sliderValue: ko.pureComputed(function() {
return vm.sliderValues[vm.rawSliderValue() - 1];
})
};
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<input type="range" min="1" data-bind="attr {max: sliderValues.length}, value: rawSliderValue, valueUpdate: 'input'" />
<div data-bind="text: sliderValue"></div>

How to load a KnockoutJS component programmatically

I'm starting to create a ko component for select2 called "select-two", but I realize soon after that I sometimes need to load components after bindings have been registered creating the component programmatically.
I've tried apending a new element but of course it doesnt work, I'm guessing it has to be rebinded.
var sel2 = $("<select-two></select-two>") ;
$("#selectList").append(sel2) ;
There's lot of reference on how to rebind the whole viewmodel, but only just the component? I think this guy has the same problem:
Load knockoutjs component using javascript
Just for reference this is the component code:
define([
'knockout',
'text!./select-two.html',
'select2'
], function (ko, templateMarkup, select2) {
ko.components.register('select-two', {
viewModel: function(params) {
var placeholder = params.placeholder;
var value = params.value;
ko.bindingHandlers.select2 = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
$(element).select2({
placeholder: placeholder,
minimumInputLength: 1,
ajax: {
url: function (term, page) {
return '/models/autores/busqueda/' + term['term']
},
dataType: 'json',
quietMillis: 200,
processResults: function (data) {
return data;
}
},
dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
}
).on('change', function(event){
ds = $(element).select2('data')[0] ;
value['id'](ds['id']) ;
value['text'](ds['text']) ;
});
}
};
return{
}
},
template: templateMarkup
});
});
my template:
<link rel="stylesheet" href="/assets/js/vendor/select2/dist/css/select2.css" />
<select class="form-control" data-bind="select2"></select>
and how I load it:
<select-two id="authorSelect" params="placeholder: 'Pick an Author', value: autorSelectData" ></select-two>
ko.bindingHandlers.select2 = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var params = ko.unwrap(valueAccessor());
$(element).select2({
placeholder: params.placeholder,
minimumInputLength: 1,
data: params.data
}).on('select2:select', function(e) {
params.value(e.params.data.text);
});
$(element).select2('val', params.value); // set initial value
}
};
ko.components.register('select-two', {
viewModel: function(params) {
this.value = params.value;
this.data = params.data;
},
template: '<select class="form-control" style="width: 200px;" data-bind="select2: ' +
'{placeholder: \'Pick a fruit\', value: value, data: data}">' +
'</select>'
});
var app = {
newSelect: function(){
var cont = $("<p></p>") ;
var sel2 = $("<select-two></select-two>") ;
ko.applyBindings({}, sel2[0]) ;
cont.append(sel2) ;
$("#select-list").append(cont) ;
},
autorSelectData: ko.observable(null),
options: ['apple','pear','peach','mango','grape']
};
ko.applyBindings(app);
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
Just the binding: <select class="form-control" style="width: 200px;"
data-bind="select2: {placeholder: 'Pick a fruit', value: autorSelectData, data: options}">
</select><br>
With component: <select-two params="value: autorSelectData, data: options"></select-two>
<p>Adding Selects</p>
<div id="select-list">
</div>
<input type="button" data-bind="click: newSelect()" value="New Select" id="new-select" />
I'm guessing it has to be rebinded.
Please remember that when you think of rebinding as a solution, in 99% of the cases you should reconsider your approach.
creating the component programmatically.
If you follow MVVM/ MVC guidelines, you should never have to do this. All data models/ views' structures are pre-defined; only their content can change.
What shouldn't you be doing?
Registering a custom binding inside a component, which means it will be re-registered every time a component instance is created.
Linking a style sheet inside a component template, which means the stylesheet will be loaded every time a component instance is created (instead of once).
Using the change event while the select2 docs show you have to use select2:select.
Below is a stripped down version of your code (eg., data-array instead of AJAX), showing how it works with (1) just the binding, and (2) with an encapsulatin component. Does it still not work, and if so, what does not work?
ko.bindingHandlers.select2 = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var params = ko.unwrap(valueAccessor());
$(element).select2({
placeholder: params.placeholder,
minimumInputLength: 1,
data: params.data
}).on('select2:select', function(e) {
params.value(e.params.data.text);
});
$(element).select2('val', params.value); // set initial value
}
};
ko.components.register('select-two', {
viewModel: function(params) {
this.value = params.value;
this.data = params.data;
},
template: '<select class="form-control" style="width: 200px;" data-bind="select2: ' +
'{placeholder: \'Pick a fruit\', value: value, data: data}">' +
'</select>'
});
var app = {
autorSelectData: ko.observable(null),
options: ['apple','pear','peach','mango','grape']
};
ko.applyBindings(app);
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
Just the binding: <select class="form-control" style="width: 200px;"
data-bind="select2: {placeholder: 'Pick a fruit', value: autorSelectData, data: options}">
</select><br>
With component: <select-two params="value: autorSelectData, data: options"></select-two>

Knockout DatePicker bound item doesn't disable the DatePicker

I have a custom knockoutJs binding for the date picker.
ko.bindingHandlers.valueAsDatePicker = {...}
When the bound input field status (enabled/disabled) is bound to a KO observable it doesn't enable/disable the datepicker icon.
HTML
<input id="txtRequestedTo" type="text" placeholder="dd/mm/yyyy"
data-bind="valueAsDatePicker: reqDateTo, disable: reqDateFrom().length < 1" />
Custom Binding
ko.bindingHandlers.valueAsDatePicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor);
formatAndSetDateValue(element, valueAccessor, allBindingsAccessor);
// Init UI datepicker
var dateFormat = allBindingsAccessor.dateFormat
$(element).datepicker({
dateFormat: dateFormat,
changeMonth: true,
changeYear: true,
yearRange: '1900:' + new Date().getFullYear(),
maxDate: 0,
showOn: "button",
buttonImage: "Content/images/sprite-base/sprite/icon-calender.png",
buttonImageOnly: true,
constrainInput: false,
buttonText: ""
});
},
update: function(element, valueAccessor, allBindingsAccessor) {
// Use the value binding
ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor);
formatAndSetDateValue(element, valueAccessor, allBindingsAccessor);
valueAccessor().valueHasMutated();
}
};
I want the datepicker to be disabled if the element is disabled, and Vice Versa.
Thanks a million Robert,
Here is the solution that worked.
Using KnockOutJS V3.1
init: function (element, valueAccessor, allBindings) {
...
//Disable the datepicker if the item is disabled or enabled.
if (allBindings.has('disable')) {
if (allBindings.get('disable')()) {
$(element).datepicker('disable');
}
else {
$(element).datepicker('enable');
}
var subscription = allBindings.get('disable').subscribe(function (newValue) {
if (newValue) {
$(element).datepicker('disable');
} else {
$(element).datepicker('enable');
}
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
subscription.dispose();
});
}
}
You have to use
if (allBindings.has('disable')) {
otherwise
allBindings.get('disable')
will be undefined as not all datepicker bound fields in the view have the disabled attribute.
The trick here is to get access to the disabled binding as an observable so you can attach a manual subscription. At the moment the result of the disable expression is passed into bindingHandler ( via allBindingAccessor ).
One you get a subscription you can invoke DatePicker's disable option
HTML
<input id="txtRequestedTo" type="text" placeholder="dd/mm/yyyy"
data-bind="valueAsDatePicker: reqDateTo, disable: reqDateFrom.disabled" />
JAVASCRIPT
var reqDateFrom = ko.observable();
var reqDateTo = ko.observable();
reqDateTo.disabled = ko.computed( function() {
return (reqDateFrom() || '').length === 0;
} );
ko.bindingHandlers.valueAsDatePicker {
init: function(element, valueAccessor, allBindingsAccessor) {
....
// ko 3.0 syntax. For 2.x use allBindingAccessor['disable']
var subscription = allBindingAccessor.get('disable').subscribe( function(newValue) {
$(element).datepicker('option', 'disabled', newValue);
});
// Make sure the subscription is disposed when the element is torn down
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
subscription.dispose();
});
},
update: function(element, valueAccessor, allBindingsAccessor) {
}
}

knockoutjs bindings issue

I'm having issues with my knockoutjs implementation. Seems to be working fine on Chrome and Safari IE and FF have thrown a hissy fit.
The message which I encounter is as follows:
Unable to parse bindings. Message: TypeError: 'AccountName' is
undefined; Bindings value: value: AccountName
The issue is happening within a script tag which serves as a knockout template:
<div id="newAccountDialog" class="dialog" data-bind="dialog: { autoOpen: false, resizable: false, modal: true, width: 350, title: 'Exchange Account'}, template: { name: 'dialogFormTemplate', data: CurrentAccount }, openDialog: IsNew"></div>
<script id="dialogFormTemplate" type="text/html">
<form id="dialogForm">
<h1>Exchange Account Manager</h1>
<p>Add new or edit an existing exchange account settings.</p>
<label for="AccountName">
Account
</label>
<input id="AccountName" name="AccountName" type="text" data-bind="value: AccountName, valueUpdate: 'afterkeydown'" class="ui-widget-content ui-corner-all" />
<div class="buttonsContainer floatRight">
<button id="Save" data-bind="click: $root.SaveAccount, dialogcmd: { id: 'newAccountDialog', cmd: 'close'}, jqButton: { icons: { primary: 'ui-icon-disk' } }">Save & Close</button>
</div>
</form>
</script>
I assume some sort of early binding is being triggered on the template
data : CurrentAccount
where an undefined / null is being passed into CurrentAccount. I have seen this issue outside of script tags, but only if the observable is not defined or null.
My viewmodel looks as following:
var AccountModel = function () {
var self = this;
self.Accounts = ko.observableArray([]);
self.CurrentAccount = ko.observable(null);
self.IsNew = ko.observable(false);
self.LoadAccounts = function () {
$account.invoke("GetAccounts", {}, function (data) {
var mapped = $.map(data, function (item) {
var account = new Account(item);
var innerMapped = $.map(item.Mailboxes, function (mailbox) {
return new Mailbox(mailbox);
});
account.Mailboxes(innerMapped);
return account;
});
self.Accounts(mapped);
});
}
self.EditAccount = function (data) {
self.CurrentAccount(data);
self.IsNew(true);
}
self.SaveAccount = function () {
if (self.CurrentAccount().Id() <= 0) {
$account.invoke('AddAccount', ko.toJS(self.CurrentAccount()), function (data) {
self.Accounts.push(new Account(data));
self.CurrentAccount(new Account(data));
self.IsNew(true);
});
} else {
$account.invoke('UpdateAccount', ko.toJS(self.CurrentAccount()), function (data) {
//self.CurrentAccount(new Account(data));
});
}
}
self.CreateAccount = function () {
self.IsNew(true);
var account = { Id: 0, AccountName: '', IsNTLM: -1, Email: '', Password: '', Domain: 'mydomain', ExchangeVersion: 1, Mailboxes: [] };
self.CurrentAccount(new Account(account));
}
};
My dialog bindingHandler is defined as follows:
ko.bindingHandlers.dialog = {
init: function (element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {};
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).dialog('destroy');
});
$(element).dialog(options);
}
};
I have ommited the Account object, as it is possibly not required in this context.
I would appreciate any help.
Thank you in advance.
There is no "early" binding in Knockout. Everything is bound when you call ko.applyBindings. But certain bindings can stop or delay binding of their descendant elements. template is one of those when you use the if or ifnot options. In your case, you can use the if option like this:
template: { name: 'dialogFormTemplate', data: CurrentAccount, 'if': CurrentAccount }
Note: The quotes around if are required in some older browsers.

Categories

Resources