Last active
August 8, 2022 07:28
-
-
Save agarciadelrio/4a41a1460e9b946839d02e3d29a67454 to your computer and use it in GitHub Desktop.
KnockoutJS Extenders Collection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* <div data-bind="blockUI: IsLoading">...</div> */ | |
ko.bindingHandlers.blockUI = { | |
update: function (element, valueAccessor, allBindingsAccessor) { | |
var value = ko.utils.unwrapObservable(valueAccessor()); | |
if (value) | |
$(element).block(); | |
else | |
$(element).unblock(); | |
} | |
}; | |
ko.bindingHandlers.sortable = { | |
init: function (element, valueAccessor, allBindingsAccessor, viewModel) { | |
var config = valueAccessor(); | |
if (!config) { | |
return; | |
} | |
var allBindings = allBindingsAccessor(); | |
var array = allBindings.foreach || allBindings.template.foreach; | |
var $list = jQuery(element); | |
$list | |
.data('ko-sort-array', array) | |
.on('sortstart', function (event, ui) { | |
ui.item.data('ko-sort-array', array); | |
ui.item.data('ko-sort-index', ui.item.index()); | |
}) | |
.on('sortupdate', function (event, ui) { | |
//this.object_has_changed(true) | |
//console.log('sortable this', this) | |
//console.log('sortable viewModel', viewModel ) | |
//console.log('sortable allBindingsAccessor', allBindingsAccessor ) | |
if (viewModel.object_has_changed) { | |
viewModel.object_has_changed(true); | |
} | |
var $newList = ui.item.parent(); | |
if ($newList[0] != $list[0]) { | |
return; | |
} | |
var oldArray = ui.item.data('ko-sort-array'); | |
var oldIndex = ui.item.data('ko-sort-index'); | |
var newArray = $newList.data('ko-sort-array'); | |
var newIndex = ui.item.index(); | |
var item = oldArray.splice(oldIndex, 1)[0]; | |
newArray.splice(newIndex, 0, item); | |
}) | |
.sortable(config); | |
} | |
}; | |
ko.bindingHandlers.dump = { | |
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var context = valueAccessor(); | |
var allBindings = allBindingsAccessor(); | |
var pre = document.createElement('pre'); | |
element.appendChild(pre); | |
var dumpJSON = ko.computed({ | |
read: function () { | |
var en = allBindings.enabled === undefined || allBindings.enabled; | |
return en ? ko.toJSON(context, null, 2) : ''; | |
}, | |
disposeWhenNodeIsRemoved: element | |
}); | |
ko.applyBindingsToNode(pre, { | |
text: dumpJSON, | |
visible: dumpJSON | |
}); | |
return { controlsDescendentBindings: true }; | |
} | |
}; | |
ko.bindingHandlers.summernote = { | |
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var value = ko.unwrap(valueAccessor()); | |
var $element = $(element); | |
$element.html(value).summernote({ | |
lang: 'es-ES', | |
callbacks: { | |
onChange: function (contents) { | |
valueAccessor()(contents); | |
} | |
} | |
}); | |
} | |
}; | |
ko.bindingHandlers.fadeVisible = { | |
init: function (element, valueAccessor) { | |
// Initially set the element to be instantly visible/hidden depending on the value | |
var value = valueAccessor(); | |
$(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable | |
}, | |
update: function (element, valueAccessor) { | |
// Whenever the value subsequently changes, slowly fade the element in or out | |
var value = valueAccessor(); | |
ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut(); | |
} | |
}; | |
ko.bindingHandlers.let = { | |
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
// Make a modified binding context, with extra properties, and apply it to descendant elements | |
var innerContext = bindingContext.extend(valueAccessor); | |
ko.applyBindingsToDescendants(innerContext, element); | |
return { controlsDescendantBindings: true }; | |
} | |
}; | |
ko.virtualElements.allowedBindings.let = true; | |
//example <div data-bind="let: {url : file_name().replace('.json','')} "> | |
ko.bindingHandlers.initialValue = { | |
init: function(element, valueAccessor, allBindingsAccessor) { | |
//console.log('INIT initialValue'); | |
//console.log('element',element); | |
//console.log('valueAccessor',valueAccessor); | |
var $e = $(element); | |
var val = $e.data('value'); | |
$e.val(val).trigger('change'); | |
} | |
}; | |
ko.bindingHandlers.currency = { | |
update: function(element, valueAccessor){ | |
//console.log('CURRENCY'); | |
var value = ko.utils.unwrapObservable(valueAccessor()) || 0; | |
value = + value; | |
//var formattedText = "$" + value.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, "$1,"); | |
//$(element).text(formattedText); | |
$(element).text( parseFloat(value).toFixed(2).replace('.',',') + '€' ); | |
} | |
}; | |
ko.bindingHandlers.timepicker = { | |
init: function(element, valueAccessor, allBindingsAccessor) { | |
console.log('timepicker.'); | |
var $e = $(element); | |
var options = allBindingsAccessor().timepickerOptions || {}; | |
$e.timepicker(options); | |
$e.timepicker().update(function() { | |
if($e.hasClass('recogida')) { | |
$('.recogida-h').val($e.val()).trigger('change'); | |
} | |
if($e.hasClass('devolucion')) { | |
$('.devolucion-h').val($e.val()).trigger('change'); | |
} | |
}); | |
} | |
}; | |
ko.bindingHandlers.datepicker = { | |
init: function(element, valueAccessor, allBindingsAccessor) { | |
//initialize datepicker with some optional options | |
var options = allBindingsAccessor().datepickerOptions || { | |
minDate: 0, | |
showOtherMonths: true, | |
selectOtherMonths: true, | |
dateFormat: ((window.aviacar && window.aviacar.dateFormat) ? window.aviacar.dateFormat : 'dd/mm/yy'), | |
}; | |
//console.log('dateFormat', window.aviacar.dateFormat); | |
$(element).datepicker(options); | |
//handle the field changing | |
ko.utils.registerEventHandler(element, "change", function () { | |
var observable = valueAccessor(); | |
//console.log('elemt', $(element).val()); | |
if($(element).hasClass('from')) { | |
if($('.form-control-date.to').datepicker('getDate') < $(element).datepicker('getDate')) { | |
$('.form-control-date.to').val($(element).val()).datepicker( "option", "minDate", $(element).datepicker("getDate") ); | |
$('.form-control-date.to').trigger('change'); | |
} | |
} | |
//observable($(element).datepicker("getDate")); | |
observable($(element).val()); | |
}); | |
//handle disposal (if KO removes by the template binding) | |
ko.utils.domNodeDisposal.addDisposeCallback(element, function() { | |
$(element).datepicker("destroy"); | |
}); | |
}, | |
//update the control when the view model changes | |
update: function(element, valueAccessor) { | |
//console.log('DATEPICKER UPDATE'); | |
var value = ko.utils.unwrapObservable(valueAccessor()); | |
//current = $(element).datepicker("getDate"); | |
var current = $(element).val(); | |
if (value - current !== 0) { | |
$(element).datepicker("setDate", value); | |
} | |
} | |
}; | |
ko.bindingHandlers.popUp = { | |
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
var attribute = ko.utils.unwrapObservable(valueAccessor()); | |
var templateContent = attribute.content; | |
var popOverTemplate = "<div class='popOverClass' id='" + attribute.id + "-popover'>" + $(templateContent).html() + "</div>"; | |
$(element).popover({ | |
placement: 'bottom', | |
content: popOverTemplate, | |
html: true, | |
trigger: 'manual' | |
}); | |
$(element).attr('id', "popover" + attribute.id + "_click"); | |
$(element).on('click', function () { | |
$(".popOverClass").popover("hide"); | |
$(this).popover('toggle'); | |
var thePopover = document.getElementById(attribute.id + "-popover"); | |
childBindingContext = bindingContext.createChildContext(viewModel); | |
ko.cleanNode(thePopover); | |
ko.applyBindingsToDescendants(childBindingContext, thePopover); | |
}); | |
} | |
}; | |
ko.bindingHandlers.readonly = { | |
update: function (element, valueAccessor) { | |
if (valueAccessor()) { | |
$(element).attr("readonly", "readonly"); | |
$(element).addClass("disabled"); | |
} else { | |
$(element).removeAttr("readonly"); | |
$(element).removeClass("disabled"); | |
} | |
} | |
}; | |
ko.bindingHandlers.fadeVisible = { | |
init: function(element, valueAccessor) { | |
// Initially set the element to be instantly visible/hidden depending on the value | |
var value = valueAccessor(); | |
$(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable | |
}, | |
update: function(element, valueAccessor) { | |
// Whenever the value subsequently changes, slowly fade the element in or out | |
var value = valueAccessor(); | |
return ko.unwrap(value) ? $(element).fadeIn() : $(element).fadeOut(); | |
} | |
}; | |
ko.bindingHandlers.slideVisible = { | |
init: function(element, valueAccessor) { | |
// Initially set the element to be instantly visible/hidden depending on the value | |
var value = valueAccessor(); | |
$(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable | |
}, | |
update: function(element, valueAccessor) { | |
// Whenever the value subsequently changes, slowly fade the element in or out | |
var value = valueAccessor(); | |
return ko.unwrap(value) ? $(element).slideDown() : $(element).slideUp(); | |
} | |
}; | |
ko.bindingHandlers.option = { | |
update: function(element, valueAccessor) { | |
var value = ko.utils.unwrapObservable(valueAccessor()); | |
//console.log('value', element, valueAccessor); | |
ko.selectExtensions.writeValue(element, value); | |
} | |
}; | |
// VERSION 0.1 | |
ko.bindingHandlers.contentEditable = { | |
update: function (element, valueAccessor) { | |
var value = valueAccessor(); | |
var valueUnwrapped = ko.unwrap(value); | |
element.innerHTML = valueUnwrapped; | |
element.addEventListener('blur', function() { | |
var observable = valueAccessor(); | |
observable(element.innerHTML); | |
}) | |
} | |
}; | |
// VERSION 0.2 | |
ko.bindingHandlers.contentEditable = { | |
update: function(element, valueAccessor) { | |
var value = valueAccessor(); | |
function onBlur(){ | |
if (ko.isWriteableObservable(value)) { | |
value(this.innerHTML); | |
} | |
}; | |
element.innerHTML = value(); //set initial value | |
element.contentEditable = true; //mark contentEditable true | |
element.addEventListener('blur', onBlur); //add blur listener | |
} | |
}; | |
// VERSION OK | |
ko.bindingHandlers.contentEditable = { | |
init: function(element, valueAccessor) { | |
var value = valueAccessor(); | |
function onBlur(){ | |
if (ko.isWriteableObservable(value)) { | |
value(this.innerHTML); | |
} | |
} | |
element.contentEditable = 'true'; //mark contentEditable true | |
element.addEventListener('blur', onBlur); //add blur listener | |
}, | |
update: function(element, valueAccessor) { | |
element.innerHTML = ko.unwrap(valueAccessor()); //set initial value | |
} | |
}; | |
// https://riptutorial.com/knockout-js/example/22504/custom-binding-handler | |
ko.bindingHandlers.href = { | |
update: function(element, valueAccessor) { | |
element.href = ko.utils.unwrapObservable(valueAccessor()); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ko.components.register('like-widget', { | |
viewModel: function (params) { | |
// Data: value is either null, 'like', or 'dislike' | |
this.chosenValue = params.value; | |
// Behaviors | |
this.like = function () { this.chosenValue('like'); }.bind(this); | |
this.dislike = function () { this.chosenValue('dislike'); }.bind(this); | |
}, | |
template: | |
`<div class="like-or-dislike" data-bind="visible: !chosenValue()"> | |
<button data-bind="click: like">Like it</button> | |
<button data-bind="click: dislike">Dislike it</button> | |
</div> | |
<div class="result" data-bind="visible: chosenValue"> | |
You <strong data-bind="text: chosenValue"></strong> it | |
</div>` | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Persist data on Local Storage | |
ko.extenders.persist = function (target, key) { | |
var initialValue = target(); | |
// Load existing value from localStorage if set | |
if (key && localStorage.getItem(key) !== null) { | |
try { | |
initialValue = JSON.parse(localStorage.getItem(key)); | |
} | |
catch (e) { } | |
} | |
target(initialValue); | |
// Subscribe to new values and add them to localStorage | |
target.subscribe(function (newValue) { localStorage.setItem(key, ko.toJSON(newValue)); }); | |
return target; | |
// ejemplo de uso: o = ko.observable('').extend({ persist: key }); | |
}; | |
ko.extenders.notnull = function(target, key) { | |
target.subscribe(function(newValue) { | |
if(newValue=='') newValue=key | |
target(newValue) | |
}) | |
return target | |
} | |
// Track if observable is changed | |
ko.extenders.trackChange = function (target, track) { | |
if (track) { | |
target.isDirty = ko.observable(false); | |
target.originalValue = target(); | |
target.setOriginalValue = function(startingValue) { | |
target.originalValue = startingValue; | |
}; | |
target.subscribe(function (newValue) { | |
// use != not !== so numbers will equate naturally | |
target.isDirty(newValue != target.originalValue); | |
}); | |
} | |
return target; | |
}; | |
ko.extenders.parentSaveChanges = function(target, parent) { | |
if(parent) { | |
target.subscribe(function(new_value) { | |
parent.saveChanges(); | |
}); | |
} | |
return target; | |
}; | |
// Autocast to Float Number | |
ko.extenders.autoCast = function (target, activated) { | |
if (activated) { | |
var result = ko.pureComputed({ | |
read: target, | |
write: function (newValue) { | |
var current = target(); | |
var valueToWrite; | |
if (newValue === undefined) { | |
valueToWrite = null; | |
} | |
else if (isNaN(newValue)) { | |
if (newValue == NaN) { | |
valueToWrite = null; | |
} | |
else if (newValue === 'true' || newValue === 'false') { | |
valueToWrite = newValue === 'true' ? true : false; | |
} | |
else { | |
valueToWrite = newValue; | |
} | |
} | |
else { | |
if (newValue === true || newValue === false) { | |
valueToWrite = newValue; | |
} | |
else { | |
valueToWrite = parseFloat(newValue); | |
} | |
if (isNaN(valueToWrite)) { | |
valueToWrite = null; | |
} | |
} | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} | |
else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
//initialize with current value to make sure it is rounded appropriately | |
result(target()); | |
//return the new computed observable | |
return result; | |
} | |
}; | |
// Cast to Boolean | |
ko.extenders.toBoolean = function (target, activated) { | |
//create a writable computed observable to intercept writes to our observable | |
if (activated) { | |
var result = ko.pureComputed({ | |
read: target, | |
write: function (newValue) { | |
//console.log('cambios en input') | |
var current = target(); | |
var valueToWrite = newValue == '1' ? true : false; | |
//only write if it changed | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} | |
else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
//initialize with current value to make sure it is rounded appropriately | |
result(target()); | |
//return the new computed observable | |
return result; | |
} | |
}; | |
// Cast to Integer Number | |
ko.extenders.toNumber = function(target, number) { | |
//create a writable computed observable to intercept writes to our observable | |
if(number) { | |
var result = ko.pureComputed({ | |
read: target, //always return the original observables value | |
write: function(newValue) { | |
//console.log('cambios en input') | |
var current = target(); | |
var valueToWrite = parseInt(newValue,10); | |
//only write if it changed | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
//initialize with current value to make sure it is rounded appropriately | |
result(target()); | |
//return the new computed observable | |
return result ? result : 0; | |
} | |
}; | |
ko.extenders.currency = function(target) { | |
var result = ko.pureComputed({ | |
read : target, | |
write : function(newValue) { | |
var current = target(), | |
roundingMultipler = Math.pow(10, 2), | |
newValueAsNum = isNaN(+newValue) ? 0 : parseFloat(newValue.toString()), | |
valueToWrite = Math.round(newValueAsNum * roundingMultipler) / roundingMultipler; | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} | |
else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
result(target()); | |
return result; | |
} | |
ko.extenders.camelCase = function (target, activated) { | |
//Require lodash | |
//create a writable computed observable to intercept writes to our observable | |
if (activated) { | |
var result = ko.pureComputed({ | |
read: target, | |
write: function (newValue) { | |
//console.log('cambios en input') | |
var current = target(); | |
var valueToWrite = _.upperFirst(_.camelCase(newValue)); | |
//only write if it changed | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} | |
else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
//initialize with current value to make sure it is rounded appropriately | |
result(target()); | |
//return the new computed observable | |
return result; | |
} | |
}; | |
ko.extenders.snakeCase = function (target, activated) { | |
//Require lodash | |
//create a writable computed observable to intercept writes to our observable | |
if (activated) { | |
var result = ko.pureComputed({ | |
read: target, | |
write: function (newValue) { | |
//console.log('cambios en input') | |
var current = target(); | |
var valueToWrite = _.snakeCase(newValue); | |
//only write if it changed | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} | |
else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
//initialize with current value to make sure it is rounded appropriately | |
result(target()); | |
//return the new computed observable | |
return result; | |
} | |
}; | |
ko.extenders.upperSnakeCase = function (target, activated) { | |
//Require lodash | |
if (activated) { | |
var result = ko.pureComputed({ | |
read: target, | |
write: function (newValue) { | |
//console.log('cambios en input') | |
var current = target(); | |
var valueToWrite = _.snakeCase(newValue).toUpperCase(); | |
//only write if it changed | |
if (valueToWrite !== current) { | |
target(valueToWrite); | |
} | |
else { | |
if (newValue !== current) { | |
target.notifySubscribers(valueToWrite); | |
} | |
} | |
} | |
}).extend({ notify: 'always' }); | |
//initialize with current value to make sure it is rounded appropriately | |
result(target()); | |
//return the new computed observable | |
return result; | |
} | |
}; | |
// Validate email | |
ko.extenders.email = function(target, overrideMessage) { | |
target.hasError = ko.observable(); | |
target.validationMessage = ko.observable(); | |
function validate(newValue) { | |
newValue = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(newValue); | |
target.hasError(newValue ? false : true); | |
target.validationMessage(newValue ? "" : overrideMessage || "Email incorrecto"); | |
} | |
validate(target()); | |
target.subscribe(validate); | |
return target; | |
}; | |
// Check for Required value | |
ko.extenders.required = function(target, overrideMessage) { | |
//add some sub-observables to our observable | |
target.hasError = ko.observable(); | |
target.validationMessage = ko.observable(); | |
//define a function to do validation | |
function validate(newValue) { | |
target.hasError(newValue ? false : true); | |
target.validationMessage(newValue ? "" : overrideMessage || "Campo requerido"); | |
} | |
//initial validation | |
validate(target()); | |
//validate whenever the value changes | |
target.subscribe(validate); | |
//return the original observable | |
return target; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ko.computed(function() { | |
return ko.toJSON(complexObject); | |
}).subscribe(function() { | |
// called whenever any of the properties of complexObject changes | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment