Knockout-2.2.0, subscribe get value before change AND new value - knockout-2.0

jsfiddle link: http://jsfiddle.net/T8ee7/
When I call Knockout's subscribe method is there a way I can get both the previous and new value? Right now, I can only call get these values separately.
I want to trigger some code if the old and new value are different.
I suppose I could do the following, but it can get messy...
(http://jsfiddle.net/MV3fN/)
var sv = sv || {};
sv.PagedRequest = function (pageNumber, pageSize) {
this.pageNumber = ko.observable(pageNumber || 1);
this.numberOfPages = ko.observable(1);
this.pageSize = ko.observable(pageSize || sv.DefaultPageSize);
};
var _pagedRequest = new sv.PagedRequest();
var oldValue;
_pagedRequest.pageNumber.subscribe(function (previousValue) {
console.log("old: " + previousValue);
oldValue = previousValue;
}, _pagedRequest, "beforeChange");
_pagedRequest.pageNumber.subscribe(function (newValue) {
console.log("new: " + newValue);
if (oldValue != newValue) {
console.log("value changed!");
}
});
_pagedRequest.pageNumber(10);
_pagedRequest.pageNumber(20);
​

I prefer using an observable extender.
http://jsfiddle.net/neonms92/xybGG/
Extender:
ko.extenders.withPrevious = function (target) {
// Define new properties for previous value and whether it's changed
target.previous = ko.observable();
target.changed = ko.computed(function () { return target() !== target.previous(); });
// Subscribe to observable to update previous, before change.
target.subscribe(function (v) {
target.previous(v);
}, null, 'beforeChange');
// Return modified observable
return target;
}
Example Usage:
// Define observable using 'withPrevious' extension
self.hours = ko.observable().extend({ withPrevious: 1 });
// Subscribe to observable like normal
self.hours.subscribe(function () {
if (!self.hours.changed()) return; // Cancel if value hasn't changed
print('Hours changed from ' + self.hours.previous() + ' to ' + self.hours());
});

This seems to work for me
ko.observable.fn.beforeAndAfterSubscribe = function (callback, target) {
var _oldValue;
this.subscribe(function (oldValue) {
_oldValue = oldValue;
}, null, 'beforeChange');
this.subscribe(function (newValue) {
callback.call(target, _oldValue, newValue);
});
};
See more at: http://ideone.com/NPpNcB#sthash.wJn57567.dpuf

http://jsfiddle.net/MV3fN/3/
var sv = sv || {};
sv.PagedRequest = function (pageNumber, pageSize) {
var self = this;
self.pageNumber = ko.observable(pageNumber || 1);
self.numberOfPages = ko.observable(1);
self.pageSize = ko.observable(pageSize || sv.DefaultPageSize);
self.pageNumber.subscribe(function (previousValue) {
console.log(previousValue);
console.log(self.pageNumber.arguments[0]);
if (previousValue != _pagedRequest.pageNumber.arguments[0]) {
console.log('value changed');
}
else {
//This won't get executed because KO doesn't
//call the function if the value doesn't change
console.log('not changed');
}
}, _pagedRequest, "beforeChange");
};
var _pagedRequest = new sv.PagedRequest();
_pagedRequest.pageNumber(10);
_pagedRequest.pageNumber(20);
_pagedRequest.pageNumber(20);
_pagedRequest.pageNumber(5);
I don't know if you're really supposed to use arguments[0], but it seems to work.
You could also set up your own method to accomplish this in a much cleaner way:
http://jsfiddle.net/PXKgr/2/
...
self.setPageNumber = function(page) {
console.log(self.pageNumber());
console.log(page);
if (self.pageNumber() != page) {
console.log('value changed');
}
else {
console.log('not changed');
}
self.pageNumber(page);
};
...
_pagedRequest.setPageNumber(10);
_pagedRequest.setPageNumber(20);
_pagedRequest.setPageNumber(20);
_pagedRequest.setPageNumber(5);

Related

Other product variant is disabled and I can not select it for 2 products (JS)

I am a coding beginner and I am building a store. My problem is that I have a product in different fabrics. Now I can only select one fabric type and the other is disabled and I can not select no matter what I do. Perfect would be if I select the fabric type, the associated products are displayed.
That what i mean
/*============================================================================
Dynamic variant availability
- To disable, set dynamicVariantsEnable to false in theme.liquid
==============================================================================*/
setCurrentVariantAvailability: function(variant) {
var valuesToEnable = {
option1: [],
option2: [],
option3: []
};
// Disable all options to start
this.disableVariantGroup($(selectors.formContainer, this.$container).find('.variant-input-wrap'));
// Combine all available variants
var availableVariants = this.variantsObject.filter(function(el) {
if (variant.id === el.id) {
return false;
}
// Option 1
if (variant.option2 === el.option2 && variant.option3 === el.option3) {
return true;
}
// Option 2
if (variant.option1 === el.option1 && variant.option3 === el.option3) {
return true;
}
// Option 3
if (variant.option1 === el.option1 && variant.option2 === el.option2) {
return true;
}
});
// IE11 can't handle shortform of {variant} so extra step is needed
var variantObject = {
variant: variant
};
availableVariants = Object.assign({}, variantObject, availableVariants);
// Loop through each available variant to gather variant values
for (var property in availableVariants) {
if (availableVariants.hasOwnProperty(property)) {
var item = availableVariants[property];
var option1 = item.option1;
var option2 = item.option2;
var option3 = item.option3;
if (option1) {
if (valuesToEnable.option1.indexOf(option1) === -1) {
valuesToEnable.option1.push(option1);
}
}
if (option2) {
if (valuesToEnable.option2.indexOf(option2) === -1) {
valuesToEnable.option2.push(option2);
}
}
if (option3) {
if (valuesToEnable.option3.indexOf(option3) === -1) {
valuesToEnable.option3.push(option3);
}
}
}
}
// Have values to enable, separated by option index
if (valuesToEnable.option1.length) {
this.enableVariantOptionByValue(valuesToEnable.option1, 'option1');
}
if (valuesToEnable.option2.length) {
this.enableVariantOptionByValue(valuesToEnable.option2, 'option2');
}
if (valuesToEnable.option3.length) {
this.enableVariantOptionByValue(valuesToEnable.option3, 'option3');
}
},
updateVariantAvailability: function(evt, value, index) {
if (value && index) {
var newVal = value;
var optionIndex = index;
} else {
var $el = $(evt.currentTarget);
var newVal = $el.val() ? $el.val() : evt.currentTarget.value;
var optionIndex = $el.data('index');
}
var variants = this.variantsObject.filter(function(el) {
return el[optionIndex] === newVal;
});
// Disable all buttons/dropdown options that aren't the current index
$(selectors.formContainer, this.$container).find('.variant-input-wrap').each(function(index, el) {
var $group = $(el);
var currentOptionIndex = $group.data('index');
if (currentOptionIndex !== optionIndex) {
// Disable all options as a starting point
this.disableVariantGroup($group);
// Loop through legit available options and enable
for (var i = 0; i < variants.length; i++) {
this.enableVariantOption($group, variants[i][currentOptionIndex]);
}
}
}.bind(this));
},
disableVariantGroup: function($group) {
if (this.settings.variantType === 'dropdown') {
$group.find('option').prop('disabled', true)
} else {
$group.find('input').prop('disabled', true);
$group.find('label').toggleClass('disabled', true);
}
},
enableVariantOptionByValue: function(array, index) {
var $group = $(selectors.formContainer, this.$container).find('.variant-input-wrap[data-index="'+ index +'"]');
for (var i = 0; i < array.length; i++) {
this.enableVariantOption($group, array[i]);
}
},
enableVariantOption: function($group, value) {
// Selecting by value so escape it
value = value.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/#])/g,'\\$1');
if (this.settings.variantType === 'dropdown') {
$group.find('option[value="'+ value +'"]').prop('disabled', false);
} else {
var $buttonGroup = $group.find('.variant-input[data-value="'+ value +'"]');
$buttonGroup.find('input').prop('disabled', false);
$buttonGroup.find('label').toggleClass('disabled', false);
}
},
Have already tried various things, but not come to the desired result, even disabling the function ensures that everything is displayed and also clickable.
I hope you can help me.
Best Regards

How to maintain a valid order book in kraken exchange with node,js

How's it going?
I got the example order book code in python (https://support.kraken.com/hc/en-us/articles/360027677512-Example-order-book-code-Python-) and translate it to javascript to run in node. But the book is wrong, it doesn't remove all old prices level. I'm sending my code below. I'd like help to solve this issue.
const websocket = require('ws');
const ws = new websocket('wss://ws.kraken.com');
const api_book = {'bid':[], 'ask':[]};
const api_depth = 10;
const api_output_book = () => {
bid = api_book['bid'].sort((x, y) => parseFloat(y[0])-parseFloat(x[0]));
ask = api_book['ask'].sort((x, y) => parseFloat(x[0])-parseFloat(y[0]));
console.log ('Bid\t\t\t\t\tAsk');
for (let x=0;x<api_depth;x++) {
console.log(`${bid[x][0]} (${bid[x][1]})\t\t\t${ask[x][0]} (${ask[x][1]})`);
}
}
const api_update_book = (side, data) => {
data.forEach((e) => {
let index = api_book[side].findIndex(o => o[0] == e[0]);
if (parseFloat(e[1]) > 0){
if(index < 0){
api_book[side].push([e[0],e[1]]);
} else {
api_book[side][index] = [e[0],e[1]];
}
} else {
api_book[side].splice(index,1);
}
});
if(side=='bid'){
api_book['bid'].sort((x, y) => parseFloat(y[0])-parseFloat(x[0]));
} else if(side=='ask'){
api_book['ask'].sort((x, y) => parseFloat(x[0])-parseFloat(y[0]));
}
}
ws.on('open', open = () => {
ws.send('{"event":"subscribe", "subscription":{"name":"book", "depth":'+api_depth+'}, "pair":["XBT/USD"]}');
console.log('Kraken websocket connected!');
});
ws.on('message', incoming = (data) => {
try {
data = JSON.parse(data.toString('utf8'));
if (data[1]) {
if (data[1]['as']) {
api_update_book('ask', data[1]['as'])
api_update_book('bid', data[1]['bs'])
} else if (data[1]['a'] || data[1]['b']) {
if (data[1]['a']) {
api_update_book('ask', data[1]['a']);
}
if (data[1]['b']) {
api_update_book('bid', data[1]['b']);
}
}
api_output_book();
}
} catch (error) {
console.log(error);
}
});
So I have also been playing around with Kraken's order book and came up with this solution using Angular. I also added a few console logs into the mix so that you can take it and run it in the browser. Hope this helps!
// variables
private ws = new WebSocket('wss://ws.kraken.com')
public asks = [];
public bids = [];
// Web Socket open connection
this.ws.onopen = () => {
this.ws.send(JSON.stringify(this.message));
console.log('Trade WS with Kraken connected')
}
// Fires when new data is received from web socket
this.ws.onmessage = (event) => {
var data = JSON.parse(event.data);
if (!data.event) {
if (data[1]['as']) {
this.asks = data[1]['as'];
this.bids = data[1]['bs'];
console.log('Initialised Book');
console.log(this.asks, this.bids);
} else if (data[1]['a'] || data[1]['b']) {
if (data[1]['a']) {
this.update_book(this.asks, 'ask', data[1]['a']);
}
if (data[1]['b']) {
this.update_book(this.bids, 'bid', data[1]['b']);
}
}
}
}
// Updating Orderbook
update_book (arr, side, data) {
if (data.length > 1) { // If 2 sets of data are received then the first will be deleted and the second will be added
let index = arr.findIndex(o => o[0] == data[0][0]); // Get position of first data
arr.splice(index, 1); // Delete data
arr.push([ data[1][0], data[1][1] ]); // Insert new data
console.log('Delete and Insert');
} else {
let index = arr.findIndex(o => o[0] == data[0][0]);
console.error(index);
if (index > -1) { // If the index matches a price in the list then it is an update message
arr[index] = [data[0][0], data[0][1]]; // Update matching position in the book
console.log('Updated ' + index);
} else { // If the index is -1 then it is a new price that came in
arr.push([data[0][0], data[0][1]]); // Insert new price
this.sort_book(arr, side); // Sort the book with the new price
arr.splice(10, 1); // Delete the 11th entry
console.log('Insert Only');
}
}
this.sort_book(arr, side); // Sort the order book
}
// Sort Orderbook
sort_book (arr, side) {
if (side == 'bid') {
arr.sort((x, y) => parseFloat(y[0]) - parseFloat(x[0]));
} else if (side == 'ask') {
arr.sort((x, y) => parseFloat(x[0]) - parseFloat(y[0]));
}
}
I would also recommend just having a look at this resource:
How to maintain a valid orderbook

How to migrate SSE chat node express to node hapi

I was testing a SSE node express chat in localhost.It was working perfectly. I was including a chat_server in a demo with hapijs as modular server...and it complain about the express syntax. How can I migrate the code to the right syntax in hapijs?
I am trying to solve changing writeHead and write methods because it's complaing about and adding stream package after searching answers in internet.
/*
* Request handlers
*/
function handleGetChat(req, res) {
console.log('handleGetChat received.');
// res(chatStream).code(200).type('text/event-stream').header('Connection', 'keep-alive').header('Cache-Control','no-cache');
// chatStream.write('\n');
(function(clientId) {
clients[clientId] = res;
clientNames[clientId] = req.params.name;
console.log('name {$req.params.name}');
req.on("close", () => {
delete clients[clientId];
actUserName = "";
sendText(clientNames[clientId] + " disconnected!", false);
delete clientNames[clientId];
});
})(++clientId);
sendText(req.params.name + " connected!", false);
let allMates = "";
for (cliId in clientNames) {
allMates += `${clientNames[cliId]}`;
if (cliId < clientId) allMates += " ";
}
sendText(`logged in [${allMates}]`, false);
}
let sendText = (text, showUserName = true) => {
for (clientId in clients) {
allMates += `${clientNames[cliId]}`;
if (cliId < clientId) allMates += " ";
}
sendText(logged in [${allMates}], false);
}
let sendText = (text, showUserName = true) => {
for (clientId in clients) {
let data = "";
let date = new Date();
let timestamp = `[${date.getHours()}:${date.getMinutes()}]`;
if (showUserName) {
data = `data: ${timestamp} <${actUserName}> ${text}\n\n`;
} else {
data = `data: ${timestamp} ${text}\n\n`;
}
//chatStream.push('data: ' + "\n\n");
}
};
function handleWriteChat(req, res) {
actUserName = req.body.name;
sendText(req.body.text);
res.json({ success: true });
}
The commented lines in the code above are the lines with syntax error in hapi. I was already changing the originals write and writeHead with chatstream.

Using do-while loop in NodeJS to check existed in Firebase database

When a user registers a new account, I want to generate a reference code. I'm using the do-while loop for checking exists of the reference code in the database but the loop keeps running at least 2 times although the records in the database are small. When I tried to run the code, it was stuck and keep waiting for the response forever.
Here's my implement:
function makeCode() {
var text = "";
var possible = "ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz0123456789";
for (var i = 0; i < 8; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
console.log("### makeReferenceCode DONE ###")
return text;
}
function genReferenceCode() {
let referencePromise = new Promise((resolve, reject) => {
let referenceRef = db.ref("ref_codes")
var isExisted = true
var code = makeCode().toUpperCase()
console.log("### Generate Reference Code: " + code)
do {
referenceRef.orderByChild("code").equalTo(code).limitToFirst(1).on("value", function (snapshots) {
if(snapshots.numChildren() > 0) {
console.log("### code existed ###")
code = makeCode().toUpperCase()
isExisted = true
} else {
console.log("### code not existed: " + code)
isExisted = false
resolve({success: true, data: code})
}
}, function (error) {
isExisted = true
reject({success: false, error_code: "GEN_REF_CODE_00", error_msg: "Can't generate reference code!"})
})
} while (isExisted === true)
})
return referencePromise;
}

Looking for a way or a plugin that save my marked text and could restore it after loose of focus

like i menthioned in the topic i'm looking for a plugin that save my marked text and could restore it after loose of focus. Like a JS Library called Rangy i used in the past. Is there such a plugin or does anyone has an idea how i could deal with this kind of problem?
Regardings Adrian
function gEBI(id) {
return document.getElementById(id);
}
var savedSel;
var savedSelActiveElement;
function saveSelection() {
if (savedSel) {
// rangy.removeMarkers(savedSel);
}
savedSel = rangy.saveSelection();
savedSelActiveElement = document.activeElement;
}
function restoreSelection() {
if (savedSel) {
rangy.restoreSelection(savedSel, true);
window.setTimeout(function() {
if (savedSelActiveElement && typeof savedSelActiveElement.focus != "undefined") {
savedSelActiveElement.focus();
}
}, 1);
}
}
$(document).ready(function()
{
try {
document.execCommand("MultipleSelection", null, true);
} catch(ex) {}
rangy.init();
// Enable buttons
var saveRestoreModule = rangy.modules.SaveRestore;
if (rangy.supported && saveRestoreModule && saveRestoreModule.supported) {
var saveButton = gEBI("saveButton");
//saveButton.disabled = false;
saveButton.ontouchstart = saveButton.onmousedown = function() {
saveSelection();
return false;
};
$('.EditorTab').mousedown(function(){
saveSelection();
return false;
});
});
The following solution would allow you to store and restore any number of ranges, but it assumes that you have not destroyed the nodes that the ranges are attached to when it comes time to restore those ranges.
var SelectionStore = (function() {
var savedRanges = {};
return {
store: function(saveIdentifier) {
var ranges = rangy.getSelection().getAllRanges();
savedRanges[saveIdentifier] = ranges;
return ranges;
},
restore: function(saveIdentifier) {
var i, selection;
selection = rangy.getSelection();
if (!savedRanges[saveIdentifier]) throw new Error('Invalid saved selection identifier used. Selection not found for ID: ' + saveIdentifier);
selection.removeAllRanges();
for (i in savedRanges[saveIdentifier]) selection.addRange(savedRanges[saveIdentifier][i]);
}
}
})();
Usage examples:
//store the current selection
SelectionStore.store('Tab1');
//restore a selection
SelectionStore.restore('Tab1');

Resources