Setting variable by string name in javascript? - javascript

//window["Fluent"]["Include"]
function setGlobalVariableByName(name,value)
{
var indexes = name.split(".");
var variable = null;
$.each(indexes, function()
{
if (variable == null){
variable = window[this];
}else{
variable = variable[this];
}
});
variable = value;
}
setGlobalVariableByName("Fluent.Include.JqueryPulse",true);
console.log(Fluent.Include.JqueryPulse) // prints false
this doesn't work, obviously. It would work if I just wanted to get the variable's value, but not for setting it.
window["Fluent"]["Include"]["JqueryPulse"] = true;
console.log(Fluent.Include.JqueryPulse) // prints true
how could I achieve something like this without using eval?
I'd need some way to programmatically add array indices to this, I'd guess
The following works, can you suggest a better way to code it in order to make it more DRY?
function setGlobalVariableByName(name,value)
{
var indices = name.split(".");
var parent;
$.each(indices, function(i)
{
if(i==indices.length-1){
if (!parent){
window[this] = value;
}else{
parent[this] = value;
}
}else if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
}
setGlobalVariableByName : function(name, value)
{
var indices = name.split(".");
var last = indices.pop();
var parent;
$.each(indices, function(i)
{
if (!parent){
parent = window[this];
}else{
parent = variable[this];
}
});
if (!parent){
window[last] = value;
}else{
parent[last] = value;
}
}

You need to call
variable[this] = value
somehow. So you need to break the loop of the splited string before reching the last name, and then assign the value.
Ultimatively you need to call:
variable = window['Fluent']['Include']; // build this in a loop
variable['JqueryPulse'] = someValue; // then call this

Ultimately you're just building an object chain and setting the final item in the chain to a value. Also, I would add a check to ensure that items which are already objects do not get overwritten so that their existing properties don't get lost:
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
$.each( names, function()
{
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[this] !== 'object' || currentOp[this] === null )
{
currentOp[this] = {};
}
//move the reference for the next iteration
currentOp = currentOp[this];
} );
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );
However, I would do it without using jQuery, even if you have jQuery available on the page. There is no need for the overhead of executing a function for each index.
//bootstrap the object for demonstration purposes--not necessary to make code work
window.Fluent = {
Include: {
foo: 'bar', //don't want to lose this'
JqueryPulse: false //want to set this to true
}
};
//define function
function setGlobalItemByName( name, value )
{
var names,
finalName,
indexCount,
currentIndex,
currentName,
//no need to figure out if this should be assigned in the loop--assign it now
currentOp = window;
if( typeof name === 'string' && name !== '' )
{
names = name.split( '.' );
//no need to track where we are in the looping--just pull the last off and use it after
finalName = names.pop();
indexCount = names.length;
for( currentIndex = 0; currentIndex < indexCount; currentIndex += 1 )
{
currentName = names[currentIndex];
//If the current item is not an object, make it so. If it is, just leave it alone and use it
if( typeof currentOp[currentName] !== 'object' || currentOp[currentName] === null )
{
currentOp[currentName] = {};
}
//move the reference for the next iteration
currentOp = currentOp[currentName];
}
//object chain build complete, assign final value
currentOp[finalName] = value;
}
}
//use function
setGlobalItemByName( 'Fluent.Include.JqueryPulse', true );
//Check that Fluent.Include.foo did not get lost
console.log( Fluent.Include.foo );
//Check that Fluent.Include.JqueryPulse got set
console.log( Fluent.Include.JqueryPulse );

Related

Object in jquery not set on submit

I would like to get inputs values of a form and place in an object (for an offer).
So i tried to place this code on submit :
$(document).ready(function(){
$('#formOffre').on('submit', function(e) {
e.preventDefault();
console.log(Offre); // give undefined in console
if ( typeof Offre == 'undefined'){
// if undefined, create object
var Offre = {
BuyerID: 1, //I will handle this later
Total: 0,
OffreItem: [] //array with json objects
};
Offre.OffreItem.id = 0;
console.log("object created");
for (i=0; i > Offre.OffreItem.id ; i++) {
Offre.OffreItem.modele = formOffre.modele.value;
Offre.OffreItem.longueur = formOffre.longueur.value;
Offre.OffreItem.hauteur = formOffre.hauteur.value;
Offre.OffreItem.qte = formOffre.qte.value;
Offre.OffreItem.rix = formOffre.prix.value;
console.log("getting parameters of inputs to offer");
}
} else {
//if object exists ony get informations of inputs
Offre.OffreItem.id = 0;
for (i=0; Offre.OffreItem.id < i; i++){
Offre.OffreItem.modele = formOffre.modele.value;
Offre.OffreItem.longueur = formOffre.longueur.value;
Offre.OffreItem.hauteur = formOffre.hauteur.value;
Offre.OffreItem.qte = formOffre.qte.value;
Offre.OffreItem.rix = formOffre.prix.value;
}
}
this is my code. when i click on submit for the first time, it go to the if statement and create the object. But when i click again, I go through the if statement like the object is not set.
i put a console log and in every case the object is undefined.
Can you someone help me please?
Thanks
You are checking Offre out side of scope in which it is defined .
enter coconsole.log(Offre); // give undefined in console
if ( typeof Offre == 'undefined'){
// if undefined, create object
var Offre = { //here is issue this should be above submit function
BuyerID: 1, //I will handle this later
Total: 0,
OffreItem: [] //array with json objects
};
Also make sure your page is maintaining state.
For you I have created example give a look. here
Hoping this will solve your problem.
Fiddle
The problem here is that you're defining your variable inside the function.
To simplify your code:
$('#formOffre').on('submit', function(e) {
if ( typeof Offre == 'undefined'){
var Offre = { }; // This variable is only accessible inside this function
} else {
//
}
}
The var Offre will define a variable within the scope of the function, the next time you run the function, a new variable with that name will be created (Meaning it will always be undefined initially)
To get around this, you can define your variable outside of the function:
var Offre;
$('#formOffre').on('submit', function(e) {
if ( typeof Offre == 'undefined'){
Offre = { }; // Notice that we're not creating a new variable here, just accessing the one defined above
} else {
//
}
}

Trying to check if object exists in Knockout Observable Array

I'm trying to check if an object has the same observable values of other objects with the same observable properties inside an observable array.
I created a foreach loop which evaluates if any of the observables match. The problem I'm having is that condition always evaluates to true, even though these values are different. I'm using typescript and knockout.
Here's the code :
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
var arrayOfHeats = model.mtrPackage.Heats();
var addToHeats = () => model.mtrPackage.Heats.push(koHeat);
var duplicate = false;
arrayOfHeats.forEach(function (koHeat, i) {
if (arrayOfHeats[i].MTRID() == koHeat.MTRID() && arrayOfHeats[i].HeatID() == koHeat.HeatID() && arrayOfHeats[i].PartID() == koHeat.PartID()) {
duplicate = true;
}
else
duplicate = false;
})
if (!!model.mtrPackage.PackageID()) {
if (duplicate) {
var c = confirm("Warning: Duplicate MTR located on current package.Proceed ?")
if (c) {
ServiceMethods.addHeatToPackage(model.mtrPackage.PackageID(), heat.HeatID).done(addToHeats);
}
if (!c) {
return;
}
}
}
}
First problem: Your loop compares each object to itself because you re-use the variable name koHeat. I believe you really wanted to refer to the "outer" koHeat.
Second problem: You overwrite the duplicate variable in every loop iteration. This is probably not what you intend. Instead you want to stop the loop as soon as a duplicate is found.
How about something along those lines?
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
var packageId = model.mtrPackage.PackageID();
koHeat.Include = ko.observable(true);
function equals(a: MTRHeatWithInclude, b: MTRHeatWithInclude): boolean {
return a.MTRID() == b.MTRID() && a.HeatID() == b.HeatID() && a.PartID() == b.PartID();
}
if ( !!packageId && (
!model.mtrPackage.Heats().some(item => equals(item, koHeat)) ||
confirm("Warning: Duplicate MTR located on current package.Proceed ?")
)
) {
ServiceMethods.addHeatToPackage(packageId, heat.HeatID).done(() => {
model.mtrPackage.Heats.push(koHeat);
});
}
}
The equals() function should ideally be a method of the MTRHeatWithInclude class.
I think you're getting a clash between koHeat defined here:
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
And the variable defined within the forEach call. It's always returning true as (within the scope of the forEach) arrayOfHeats[i] === koHeat.
Try this:
export function addPDFToPackage(heat: MTRHeat): void {
var koHeat: MTRHeatWithInclude = ko.mapping.fromJS(heat);
koHeat.Include = ko.observable(true);
var arrayOfHeats = model.mtrPackage.Heats();
var addToHeats = () => model.mtrPackage.Heats.push(koHeat);
var duplicate = false;
arrayOfHeats.forEach(function (koHeat2, i) {
if (koHeat2.MTRID() == koHeat.MTRID() &&
koHeat2.HeatID() == koHeat.HeatID() &&
koHeat2.PartID() == koHeat.PartID()) {
duplicate = true;
}
})
if (!!model.mtrPackage.PackageID()) {
if (duplicate) {
var c = confirm("Warning: Duplicate MTR located on current package.Proceed ?")
if (c) {
ServiceMethods.addHeatToPackage(model.mtrPackage.PackageID(), heat.HeatID).done(addToHeats);
} else {
return;
}
}
}
}

Make Javascript local variable to global for recursive loops

I have a recursive function which has a local variable.
It calls itself on specific condition.
The local variable needs to be updated, but every call it creates a new local variable specific to the current function scope.
How can i reach the local variable for access all recursive loop and not to create a new one?
Something like __Callee.varname?
The code is:
var addAttribute = function(object,elem)
{
var attributes = [];
// only attribute without values
if ( object instanceof Array )
{
for ( var value in object )
{
attributes.push(object[value]);
}
}
// attribute with values
else if ( object instanceof Object )
{
for ( var key in object )
{
if ( object[key] instanceof Array )
{
addAttribute(object[key],elem);
}
else
{
attributes.push(key+'=\''+object[key]+'\'');
}
}
}
// Only one attribute
else if ( typeof object === 'string' )
{
attributes.push('\''+object+'\'');
}
// Invalid parameter
else
{
console.log('Invalid parameter: '+typeof object);
}
console.log('<'+elem+' '+attributes.join(' ').toString()+' />');
}
I do not want to make variable to global because of using this name in other functions and global scope already.
Use a closure
function fn() {
function recursiveFunction() {
// do something with x
recursiveFunction();
}
var x = 0;
recursiveFunction();
}
The usual thing is to pass it into the function, possibly optionally:
var addAttribute = function(object,elem, attributes) {
attributes = attributes || [];
// ....
Then when calling it recursively, pass in the third argument:
addAttribute(object[key], value, attributes);
Here's a much simplified example demonstrating:
function foo(num, array) {
array = array || [];
array.push(num);
console.log("Pushed " + num + ", array = " + JSON.stringify(array));
if (num < 5) {
foo(num + 1, array);
}
}
foo(1);

looping through array of inputs to set value, "cannot set property value of undefined"?

I'm looking for a plain-Javascript way to fix my following code:
function autoFill(response) {
var arr = [];
arr.fn = document.getElementsByName("firstName")[1];
arr.ln = document.getElementsByName("lastName")[1];
arr.em = document.getElementsByName("Email")[1];
arr.pn = document.getElementsByName("phoneNumber")[1];
if(response === false) {
alert('false');
arr.forEach(function(entry){
entry.value = "";
});
}else{
alert('true');
arr.fn.value = response.firstName;
arr.ln.value = response.lastName;
arr.en.value = response.email;
arr.pn.value = response.phone;
}
}
What I'm trying to do:
if response data === false, loop through each element in arr[] and set its text value to empty "".
What's happening:
directly setting the values work (as shown in the else{} block), however looping or iterating through the array throws the error: Uncaught TypeError: Cannot set property 'value' of undefined
question:
how can I loop through a collection of inputs stored in an array and set their values? Why is the undefined error being thrown?
Thanks!
EDIT: changing to the following does nothing; the else{] block still works fine, the loop is the problem I believe.
function autoFill(response) {
var arr = [];
arr["fn"] = document.getElementsByName("firstName")[1];
arr["ln"] = document.getElementsByName("lastName")[1];
arr["em"] = document.getElementsByName("Email")[1];
arr["pn"] = document.getElementsByName("phoneNumber")[1];
if(response === false) {
alert('false');
arr.forEach(function(entry){
entry.value = "";
});
}else{
alert('true');
arr["fn"].value = response.firstName;
arr["ln"].value = response.lastName;
arr["en"].value = response.email;
arr["pn"].value = response.phone;
}
}
You need to use an object not an array and you should use "dot notation" for better clarity.
EDIT
I changed the code because I dont like to say "no you can't" so here's the new function with loop.
function autoFill(response) {
var fill = {};
for (var i=0; i < document.myForm.elements.length; i++) {
var elm = document.myForm.elements[i]; // Get current element.
fill[elm.name] = elm; // Associate fill[input name] with current input.
if (elm.getAttribute("data-autofill") === "undefined" ||
elm.getAttribute("data-autofill") === null) continue; // Continue if property data-autofill is not set.
// If response have the input name as key, we set the value otherwise, it would be an empty string or unchecked.
switch(elm.type) {
case 'checkbox':
case 'radio':
response.hasOwnProperty(elm.name) ? fill[elm.name].checked = response[elm.name] : fill[elm.name].checked = false; // Checked if response if true.
break;
default:
response.hasOwnProperty(elm.name) ? fill[elm.name].value = response[elm.name] : fill[elm.name].value = ""; // Set response text or a empty string.
}
}
return fill; // Return object for later uses.
}
var myForm = autoFill({
firstName: "John",
check: true,
ni: "Not supposed to be set" // Won't set because he doesn't have the property data-autofill.
});
myForm.lastName.value = "Awesome"; // Now we can set values like this because we returned all form elements.
And the jsFiddle.
To reuse the names in each area of the function, add them to an array and iterate over them BUT you need to ensure that the properties in response and your DOM element names match.
function autoFill(response) {
var arr = ['firstName', 'lastName', 'Email', 'phoneNumber'];
var obj = {};
arr.forEach(function (el) {
obj[el] = document.getElementsByName(el)[1];
});
if (response === false) {
alert('false');
for (var k in obj) {
obj[k]['value'] = "";
}
} else {
alert('true');
arr.forEach(function (el) {
obj[el]['value'] = response[el];
});
}
}
You should store input fields into an array as
arr[0], arr[1] and access them with index.
you have declared: var arr = []; then assigned arr.fn, arr.ln n so..
Either declare var arr = {}; or assign arr.fn, arr.ln as arr[0], arr[1].
When you assign,
var arr = [];
arr["fn"] = document.getElementsByName("firstName")[1];
and observe the value of arr, it will be empty. so declare it as var arr= {}.

Jquery Evolution from simple plain javascript

i have been using jquery for a while now but only thing i know about jquery is probably a dozen of functions that get my job done. but i want to understand how jquery evolved from simpl plain javascript i.e how
$("#xyz").val();
is converted to
document.getElementById('xyz').value;
i have searched for my answer on the web but most of the writers are happy to show how you can hook on to different DOM elements with jquery, selector details etc. but nothing can be found about how actually the transition was made. can anyone refer me to some tutorial where i can get my required material?
thanks
jQuery is not a compiler. jQuery does not get compiled into javascript.
.val is a method of an object. The jQuery object.
Specifically it is
function (value) {
if (!arguments.length) {
var elem = this[0];
if (elem) {
if (jQuery.nodeName(elem, "option")) {
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932
var val = elem.attributes.value;
return !val || val.specified ? elem.value : elem.text;
}
// We need to handle select boxes special
if (jQuery.nodeName(elem, "select")) {
var index = elem.selectedIndex,
values = [],
options = elem.options,
one = elem.type === "select-one";
// Nothing was selected
if (index < 0) {
return null;
}
// Loop through all the selected options
for (var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++) {
var option = options[i];
// Don't return options that are disabled or in a disabled optgroup
if (option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && (!option.parentNode.disabled || !jQuery.nodeName(option.parentNode, "optgroup"))) {
// Get the specific value for the option
value = jQuery(option).val();
// We don't need an array for one selects
if (one) {
return value;
}
// Multi-Selects return an array
values.push(value);
}
}
return values;
}
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
if (rradiocheck.test(elem.type) && !jQuery.support.checkOn) {
return elem.getAttribute("value") === null ? "on" : elem.value;
}
// Everything else, we just grab the value
return (elem.value || "").replace(rreturn, "");
}
return undefined;
}
var isFunction = jQuery.isFunction(value);
return this.each(function (i) {
var self = jQuery(this),
val = value;
if (this.nodeType !== 1) {
return;
}
if (isFunction) {
val = value.call(this, i, self.val());
}
// Treat null/undefined as ""; convert numbers to string
if (val == null) {
val = "";
} else if (typeof val === "number") {
val += "";
} else if (jQuery.isArray(val)) {
val = jQuery.map(val, function (value) {
return value == null ? "" : value + "";
});
}
if (jQuery.isArray(val) && rradiocheck.test(this.type)) {
this.checked = jQuery.inArray(self.val(), val) >= 0;
} else if (jQuery.nodeName(this, "select")) {
var values = jQuery.makeArray(val);
jQuery("option", this).each(function () {
this.selected = jQuery.inArray(jQuery(this).val(), values) >= 0;
});
if (!values.length) {
this.selectedIndex = -1;
}
} else {
this.value = val;
}
});
}
If we break the above wall down we can get
function (value) {
if (arguments.length === 0) {
return (this[0].value || "")
}
this.value = val;
return this;
}
Of course jQuery has a lot more code to deal with various edge cases and special things.
In essence jQuery takes a selector. finds the elements. Stores them internally then returns you an object.
This object has all kinds of methods that allow you to mutate the underlying dom objects stored internally. .val is one of them.
There are plenty of articles on how jQuery works (there are screencasts too).
jQuery, as you've noticed, is basically a bunch of methods operating on an array of elements. It is also intended to normalize browser differences under the hood.
Take the basic usage $("#xyz").val();
I can even tell you what jQuery is doing behind the scenes, but I don't think you really want to know. :)
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
// ...
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
// ...
},
// ...
};
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
So basically $(selector) means newjQuery.fn.init(selector), it's just a shortcut for easier typing (and also to prevent the "bug" where fogetting new binds this to the global object, instead of the current instance).
Also, the so-called plug-ins added as jQuery.fn.ext are mapped to jQuery.fn.init.prototype as you can see in the last line, it's another shortcut. So when you call $(selector) everything that is added to jQuery.fn will also be on jQuery.fn.init.prototype and so the new instance will have those methods as $(selector).ext(...).
// as you use it today
jQuery.fn.plugin = function ( ... ) { ... }
$(selector).plugin( ... )
// as it would be without shortcuts
jQuery.fn.init.prototype.plugin = function ( ... ) { ... }
(new jQuery.fn.init(selector)).plugin( ... )

Categories

Resources