i am taking first steps in agularJS and got stuck trying to create a static variable in a function. the function gets a variable called cron_format and i want the function to save that value. thee function checks if user changed the value of cron_format and if so, posts the data.
this is the function:
$scope.saveCron = function(userId,scriptId,cronFormat,letter,comp,index){
//split cronFormat to match the value in the table, and than save changes
var newCron = $scope.split(cronFormat);
newCron[index] = letter;
//remove commas to match the cron's format
newCron = newCron.toString().replace(/,/g, " ");
if(letter!=comp) {
$.post("updateCronChange.php", "user_id=" + userId + "&script_id=" + scriptId + "&cron_format=" + newCron, function (data) {
console.log("cron changed to: "+newCron);
});
}
}
i want to store the variable newCron so that next time function being called, cronFormat will be equal to newCron from last execution...any help plz?
Use the fact that the function "closes over" the outer scope variables, and create one to store your previous value.
var prevCron; // Define a variable to hold the value between function calls.
$scope.saveCron = function(userId,scriptId,cronFormat,letter,comp,index){
//split cronFormat to match the value in the table, and than save changes
var newCron = $scope.split(cronFormat);
newCron[index] = letter;
//remove commas to match the cron's format
newCron = newCron.toString().replace(/,/g, " ");
if(letter!=comp) {
$.post("updateCronChange.php", "user_id=" + userId + "&script_id=" + scriptId + "&cron_format=" + newCron, function (data) {
console.log("cron changed to: "+newCron);
});
}
// Use the outer variable to store our current value.
prevCron = newCron;
}
Related
I am trying to loop an array using for loop. As soon as the variable goes inside the firebase query, only the last value is displayed. Any idea why this is happening?
const handleClose = () => {
var tpList = TP;
for (var v in tpList) {
var tp = tpList[v];
console.log(tp);
Firebase.database().ref("Student").child(tp + "/Name/").once('value').then((res) => {
console.log("Name of " + tp + " is " + res.val());
})
}
The first console log works perfeclty fine, showing the correct values from index 0-5. However, the second console log shows only the index value of 5. Any suggestions what could be wrong here?
As mentioned in the comment, you'd use let instead of var in the loop:
const handleClose = () => {
var tpList = TP;
for (let v in tpList) { // Use let here
let tp = tpList[v]; // (optional) Also here to keep the scope inside the loop
console.log(tp);
Firebase.database().ref("Student").child(tp + "/Name/").once('value').then((res) => {
console.log("Name of " + tp + " is " + res.val());
})
}
The reasons for this is that in ES6 when using let in a loop, the declared variable will get a new binding for each iteration of the loop, so the closure will correctly capture each value of v, instead of capturing only the last one as it happens when using var.
I'm using pouchDb and to query the database it requires the creation of a map function (which is standard practice for couchDB)
This version is working:
function (doc) {
if (doc.type) {
emit(doc.type)
}
}.toString()
and it results in:
"function mapFunction(doc) {
if (doc.type) {
emit(doc.type);
}
}"
However, I'm trying to change my function call to be more dynamic so I can pass a field through that the map function should be built on. With that in mind, I have a variable called field and I change my map function to this:
var field = '_id'
function (doc) {
if (doc[field]) {
emit(doc[field)
}
}.toString()
the problem is, the string that's generated is like so:
"function mapFunction(doc) {
if (doc[field]) {
emit(doc[field]);
}
}"
but I need to it to be:
"function mapFunction(doc) {
if (doc['_id']) { //or doc._id (I don't mind)
emit(doc['_id']);
}
}"
Is it possible to achieve this?
Edit: Worse case scenario, I write it as a string and do it that way but would prefer to have it as a readable function.
Perhaps a generator that takes a function, a variable name and a value and creates the string you want would do.
Something like
function functionGenerator(func, variable, value){
var r = new RegExp(variable,'gi');
return func.toString().replace(r, value);
}
function mapFunction(doc) {
if (doc[field]) {
emit(doc[field]);
}
}
var map = functionGenerator(mapFunction, 'field','\'_id\'');
console.log(map);
You could define a new method on the Function prototype that performs a toString, but allows to pass a collection of variables in an object format -- where each key is the variable to use. Those variables are injected in the string representation of the function, as var declarations right after the function body opens with a brace.
Each variable gets the JSON representation of its original value. This, of course, has some limitations, as not all values can be represented as JSON (cyclic references, objects with methods, ...etc). But it certainly works with primitive values such as strings:
Function.prototype.toStringWith = function(vars) {
return this.toString().replace(/(\)\s*{)/,
'$1\n var ' + Object.keys(vars)
.map( key => key + ' = ' + JSON.stringify(vars[key]) )
.join(',\n ') + ';');
}
// Demo
var field = '_id'
var s = function mapFunction(doc) {
if (doc[field]) {
emit(doc[field])
}
}.toStringWith({field}); // ES6 shortcut notation
console.log(s);
If you would have more variables that the function needs to "know", like size, weight, brand, then you call .toStringWith({field, size, weight, brand}), ...etc.
NB: solutions that search for the variable name in the function source and replace it with the literal value will need to be careful: the variable name could occur in a quoted string (between single quotes, doubles quotes), or template literals, or be part of a larger name, where it should not be replaced.
I think the easiest solution is a simple regexp.
var field = '_id';
var a = function (doc) {
if (doc[field]) {
emit(doc[field])
}
}.toString();
console.log(a.replace(/field/gi, field));
As I've commented, that's broad because you need to parse and re-generate this stringified function. I can't believe a plugin will force someone to stringify a function.
Since it's broad to do that replacement from field to __id (because of other identifiers, etc.) you can only re-declare this field with its initial value in the stringified function (assign its value at the top).
Not related-advice:
(Remind: var statement declares a variable in the entire scope, so the variable can be assigned before the var statement is present, too.)
//////////////////////////////////////////////
//////////////// References //////////////////
//////////////////////////////////////////////
var _stringify = JSON.stringify
//////////////////////////////////////////////
//////////////// Variables //////////////////
//////////////////////////////////////////////
var field = '__id'
/* Store the variables to be copied in the top here */
var locals = { field: field }
/* String to contain the variables */
var stringified_locals = '',
// thanks to this var length notation
// we'll separate the variables by commas
len = 0
/* Calculate the length of vars */
for (let prop in locals)
++len
/* Also useful for the variable separation */
i = 0; var i
/* Only declare the 'var' statement if there's at least
* ONE var */
if (len)
stringified_locals = 'var '
/* Now generate the string of variables */
for (let prop in locals) {
let value = _stringify(locals[prop])
stringified_locals += prop + ' = ' + value
/* Add comma separator if neccessary */
if (i++ < (len - 1))
stringified_locals += ', '
}
/* And the complete stringified function */
stringified_locals + '\r\n' +
(function (doc) {
if (doc.type) {
emit(doc.type)
}
}).toString()
Got result:
`var field = "__id"
function (doc) {
if (doc.type) {
emit(doc.type)
}
}`
You could do this:
"(function() {\n" +
"var field = " + JSON.stringify(field) + ";\n" +
"return " + mapFunction.toString() + ";" +
"})()"
Caveat: There are rare cases where JSON.stringify doesn't produce valid javascript. I don't know exactly what those cases are or whether it would be possible for a malicious user to take advantage of them in some way. (Do you trust whoever is supplying the value of field?)
Enviroment: I am using isml for view (demandware templating), browser is chrome.
What I am trying is to store the shopping store ID:
function cacheStores(data) {
cachedStores = {};
for (var i = data.length - 1; i >= 0; i--) {
var store = data[i];
console.log(store);
cachedStores[store.physicalId] = store;
}
It works when I am on the page, but when I leave the page (to next page) and try to go back to it, the value is empty.
ISML:
<input type="hidden" name="dwfrm_billing_shippingAddress_addressFields_houseNumber" value=>
That is the function setting name and value
function setShippingField(name, value) {
jQuery('input[name=' + name + ']', addressContainer).val(value);
}
//only set if housenumber is a field
if ($houseNumber.length) {
var matches = shippingAddress1.match(/\d+/);
if (matches) {
shippingHouseNum = matches[0];
shippingAddress1 = shippingAddress1.replace(shippingHouseNum, '');
setShippingField(checkout.initFields.s_houseNumber, shippingHouseNum);
}
}
// set hidden fields
setShippingField(checkout.initFields.s_title, billingTitle);
setShippingField(checkout.initFields.s_lastName, shippingLastName);
setShippingField(checkout.initFields.s_firstName, shippingFirstName);
setShippingField(checkout.initFields.s_address1, shippingAddress1.substr(0, MAX_ADDRESS1_LEN));
setShippingField(checkout.initFields.s_address2, shippingAddress2);
setShippingField(checkout.initFields.s_address3, store.physicalId);
setShippingField(checkout.initFields.s_zip, store.address.postalCode);
setShippingField(checkout.initFields.s_city, shippingCity);
setShippingField(checkout.initFields.s_country, billingCountry);
// set visible labels
jQuery('#customer', addressContainer).text(billingTitle + " " + shippingAddress2);
jQuery('#shopname', addressContainer).text(shippingLastName + ' ' + shippingFirstName);
jQuery('#shopstreet', addressContainer).text(shippingAddress1 + ' ' + shippingHouseNum); // includes house number
jQuery('#shopid', addressContainer).text(store.physicalId); // we're not using shop ids here
jQuery('#shopzipcity', addressContainer).text(store.address.postalCode + ' ' + shippingCity);
window.top.close();
}
It looks like client-side issue, the one you are having. I do not think it relates to the server-side logic you are using.
setShippingField(checkout.initFields.s_address3, store.physicalId);
Should not this be with something different than s_address3 field as key?
From your code snippet it is not clear what exactly you have in the checkout variable and in its initFields member. Also what exactly you pass to cacheStores function's data parameter.
On a side note, it is bad practice to use same identifier for different things in different scopes (cacheStores in one scope is function, and in another scope is object.)
I can't figure out how to pass an argument by value in JavaScript to onClick-function. I have been trying with closure functions but it doesn't seem to work!! aah
...
for(var i=0; i<=15; i++){
blockNr++;
meal = getMealByCategory(category, i);
place.innerHTML += "<div class='container-fluid' name='blockName' id='blockName" +
blockNr + "' onClick='addPrice(meal, 20)'></div>"; //here, always the last meal is sent
var itemPlace = document.getElementById("blockName"+blockNr);
itemPlace.innerHTML = meal; //this works fine for all meals
}
What you're doing is creating a global variable and overwriting it every time you add to the .innerHTML. Code in a string can't reference local variables, so what you want isn't possible.
One option would be to concatenate the value into the string.
place.innerHTML += "<div class='container-fluid' name='blockName' id='blockName" +
blockNr + "' onClick='addPrice(\"" + meal + "\", 20)'></div>";
This should work, but is pretty unpleasant. Instead, create your elements with DOM creation methods, and assign a function to the onclick property. That function will be able to reference the local meal.
And be sure to create a local scope, and declare meal with var.
var meal;
for(var i=0; i<=15; i++) {
meal = getMealByCategory(category, i);
createDiv(meal);
}
function createDiv(meal) {
blockNr++;
var div = document.createElement("div");
div.className = "container-fluid"
div.name = "blockName"
div.id = "blockName" + blockNr;
div.onclick = function() {
addPrice(meal, 20);
}
place.appendChild(div);
}
That is because by the time you run the on-click event the loop has fully executed and the value for meal is static as the last calculated value. If this needs to change for each element, you should pass the parameter value to the function, not pass the variable name. So something like:
"' onClick='addPrice('" + meal + "', 20)'></div>";
Take a look at my code :
// is_array function
function is_array(input){ return typeof(input)=='object'&&(input instanceof Array); }
// Check if cos_in is an array. If is not, create him
if(!is_array(cos_in))
{
var cos_in = new Array();
}
// Onclick function
function cos(pret,box,configuratie)
{
// Create a value (is different on every click; using different box)
cos_in[box] = box + '|||' + pret + '|||' + configuratie + '||||';
// Insert values from array in some div with #cos id
$("#cos").html(cos_in.join('||||'));
}
My problem is that the div with id #cos has from start value "test-empty", and for each time onclick function is executed, the div should have value from function. But is returns an empty div.
Some help please?
Although this code can be improved a lot I tried to fix your first immediate problem here.
Do you want to append the result every time you click? Where is the join for?
Are you trying to join the keys or the values? I assume for now you want the value and not the key.
window.cos_in = window.cos_in && window.cos_in instanceof Array ? window.cos_in : []
// Onclick function
function cos(pret,box,configuratie)
{
// Create a value (is different on every click; using different box)
cos_in.push(box + '|||' + pret + '|||' + configuratie + '||||');
// Insert values from array in some div with #cos id
$("#cos").html(cos_in.join('||||'));
}
Let me iterate a bit to get to something readable/understandable.
Here is a cleaner example of what you're doing. To improve it more I need to know where you're going with your links and parameters.
var cos = (function (cos_in) {
return function cos(pret, box, configuratie) {
// Create a value (is different on every click; using different box)
cos_in.push(box + '|||' + pret + '|||' + configuratie + '||||');
// Insert values from array in some div with #cos id
$("#cos").text(cos_in.join('||||'));
};
}([]));
Here is an example of an object version instead of an array...
var cos = (function (cos_in) {
return function cos(pret, box, configuratie) {
// Create a value (is different on every click; using different box)
cos_in[box] = (box + '|||' + pret + '|||' + configuratie + '||||');
// Insert values from array in some div with #cos id
$("#cos").text(Object.keys(cos_in).join('||||'));
};
}({}));
This is a simple wrapper you could use:
function join(input, str) {
if(typeof(input) === 'object') {
if(input instanceof Array) {
return input.join(str);
} else {
var tmp = [];
for(var x in input) {
if(input.hasOwnProperty(x)) {
tmp.push(input[x]);
}
}
return tmp.join(str);
}
}
return input;
}
/* ... */
$("#cos").html( join(cos_in, '||||') );
However, you really need to differ between languages. JavaScript might not work as you expect it, at least in comparison with PHP.