How to get a UI variable out of a function - javascript

I'm sorry if this is basic. I'm self taught and stuggle with what are probably simple features of Javascript programming. Can anyone show me what I should be searching for to find the right kind of answer, OR help me in general with this one?
I have a function inside a UI window to quickly populate it with panels. But I can't seem to get the values entered into each panel get OUT of the UI and into the main "DoSomething" function.
The variable (dlg.tryThis) won't update? Why? What do I need to do?
// making a simple UI
function UI_Builder(doSomething)
{
var dlg = new Window( "dialog", "options" );
dlg.mainGroup = dlg.add( "group", undefined,"main" );
//Created a simple function so I can re-create panels and values very easy
function makePanel (panelName, val) {
var newPanel = eval("dlg.mainGroup." + panelName);
newPanel = dlg.mainGroup.add( 'panel', undefined, panelName );
newPanel.Start = newPanel.add("edittext", [0,0,30,20], val);
dlg.Start = newPanel.Start.text
newPanel.Start.onChanging = function()
{
dlg.Start = newPanel.Start.text
alert("Changed " + panelName + " to: " + dlg.Start) // THIS alerts correctly. It know what value I changed and where I changed it.
return dlg.Start;
}
return dlg.Start;
}
// calling the function to create panels.
dlg.tryThis = makePanel("tryThis", 4);
dlg.anotherOne = makePanel("anotherOne", 3);
dlg.again = makePanel("again", 2);
dlg.mainGroup.btnCancel = dlg.mainGroup.add( "button",[0,0,130,70], "doSomething" );
dlg.mainGroup.btnCancel.onClick = function()
{
doSomething();
}
return dlg
}
// all the UI builder is doing is showing the UI
var dlg = UI_Builder(doSomething);
dlg.show();
function doSomething()
{
dlg.close();
alert(dlg.tryThis) // this is not the NEW value? How do I get it to be the NEW value updated by the ".onChanging" function?
}

try passing dlg to the event handler instead of declaring it globally:
dlg.mainGroup.btnCancel.onClick = function() {
doSomething(dlg);
}
function doSomething(dlg) {
dlg.close();
alert(dlg.tryThis);
}
If you take this approach you should not declare var dlb globally, to avoid confusion. (Shadowing variables is generally a bad practice)

You're calling doSomething without any context.
Use func.apply:
dlg.mainGroup.btnCancel.onClick = function()
{
doSomething.apply(this, []);
}
And handle the object as 'this':
function doSomething()
{
this.close();
alert(this.tryThis) // this is not the NEW value? How do I get it to be the NEW value updated by the ".onChanging" function?
}

Related

Whats the quickest way to append or edit a function in an object?

I have an example object like such:
var shooter = {fire : function(){shootRightHandGun("shotgun");}}
However say the shooter finds a new gun and we want to set shooters fire function to this:
{fire : function(){shootRightHandGun("shotgun"); shootLeftHandGun("handgun");}}
What would be the best/quickest way to achieve this? Helper functions, a function array in the object, anything. I'm totally open to suggestions.
You shouldn't be replacing functions for that, instead keep track of the items separately, it will probably help you in a long run.
You might need to reference the current items being held by the shooter in a different place in code, do various checks and validations.
Don't just replace functions like this.
var shooter = {
leftGun: null,
rightGun: "shotgun",
fire: function() {
if(this.rightGun != null) {
shootRightHandGun(this.rightGun);
}
if(this.leftGun != null) {
shootLeftHandGun(this.leftGun);
}
}
}
shooter.leftGun = "handgun";
You can later easily extend the code with proper setters and getters, easily add some additional checks and so:
getRightGun: function() { return this.rightGun; }
setRightGun: function(newRightGun) {
if(isProperGun(newRightGun)) { // some kind of a check
this.rightGun = newRightGun;
}
}
Technically you can edit functions by getting the function's source code via the .toString() method then do string manipulations on it using regexp etc. But it would be very, very messy and I don't recommend it.
Instead, give the object a bit more structure. First separate out right and left hand weapons:
var shooter = {
rightHand : function () {},
leftHand : function () {},
fire : function () {}
}
Now make the .fire() method shoot (or use) all weapons:
var shooter = {
rightHandWeapon : function () {},
leftHandWeapon : function () {},
fire : function () {
this.rightHandWeapon();
this.leftHandWeapon();
}
}
Now, the code above does nothing (since both functions do nothing) which means that the code above is for an unarmed shooter.
Now you can implement weapons as functions:
function shotgun () {
/* code to fire shotgun */
}
function handgun () {
/* code to fire handgun */
}
To be complete we can define the following function as well:
function unarmed () {};
Now you can arm the shooter by giving him the weapons:
// Armed with shotgun
shooter.rightHandWeapon = shotgun;
shooter.fire();
// Armed with shotgun and handgun:
shooter.rightHandWeapon = shotgun;
shooter.leftHandWeapon = handgun;
shooter.fire();
// Armed with TWO shotguns:
shooter.rightHandWeapon = shotgun;
shooter.leftHandWeapon = shotgun;
shooter.fire();
// Disarm the shooter:
shooter.rightHandWeapon = unarmed;
shooter.leftHandWeapon = unarmed;
shooter.fire(); // does nothing
To append
var temp = shooter.fire;
shooter.fire = (function(t){
return function(){
t();
shootLeftHandGun("handgun");
}
})(temp);
To edit
shooter.fire = function(){shootLeftHandGun("handgun");};
Fiddle: https://jsfiddle.net/trex005/4rhq7zxj/
If user can find better gun, a good way to do this is to use inheritance for Gun class.
Example in es6, but you can easily do this in a es5 using prototypes:
class SimpleGun {
fire: function() {
shootLeftHandGun("handgun");
}
}
class BetterGun extends SimpleGun {
fire: function() {
super.fire();
shootRightHandGun("handgun");
}
}
so, when user find another gun, just do something like this:
user.setGun(new BetterGun())
As long as your object is not a function constructor and is just a simple object like this, you can simply add whatever you want (properties, methods) to it directly:
shooter.shootLeftHandGun = fucntion() {// Your code here. }
But if your shooter has been created from a function constructor (which is not in your case), You can do this easily through your object's prototype.
shooter.prototype.shootLeftHandGun = function() {// Your code here.}
instead of simply create your object on the fly, try using this:]
function shooter() {
this.shootRightHandGun = function() {
// Code
}
this.name = "default name"
}
var newShooter = new shooter();
newShooter.prototype.shootLeftHandGun = function() { // Your new stuff.}
This is one more alternative:
var shooter = {
guns:[{side:'right',type:'shootgun'}],
fire : function(){
for(var i = 0; i < this.guns.length; i++){
this.shoot(this.guns[i]);
}
},
gainGun : function(side, type){
this.guns.push({side:side, type:type})
},
shoot:function(gun){
console.log(gun)
}
}
It looks like everyone is piling on here, which is cool. Here's my attempt. If a gun is already equipped in a hand, then that hand is occupied and the user is told about it. I also added a fireAllGuns function for the fun of it.
var shooter = {
guns: {},
fire: function(hand) {
if (this.guns[hand]) {
console.log("Firing " + this.guns[hand] + " from " + hand + " hand!");
} else {
console.log("There is no gun in that hand!")
}
},
fireAllGuns: function() {
for (var key in this.guns) {
if (this.guns.hasOwnProperty(key)) {
console.log("Firing " + this.guns[key] + " from " + key + " hand!");
}
}
},
equipGun: function(hand, gun_name) {
if (!this.guns[hand]) {
this.guns[hand] = gun_name;
console.log("Equipped " + gun_name + " in " + hand + " hand");
} else {
console.log("That hand is already occupied!")
}
}
};
shooter.fire("left");
// There is no gun in that hand!
shooter.equipGun("left", "pistol");
// Equipped pistol in left hand
shooter.fire("left");
// Firing pistol from left hand!
shooter.fire("right");
// There is no gun in that hand!
shooter.equipGun("right", "bazooka");
// Equipped bazooka in right hand
shooter.fire("right");
// Firing bazooka from right hand!
shooter.fireAllGuns();
// Firing pistol from left hand!
// Firing bazooka from right hand!

How to invoke a function from both an outside and a sibling function in javascript / google app script

Basic question but I can't figure it out :(. A solution to one makes the other one break. Here is the specific case narrowed down, any help is appreciated.
function onOpen() { // first entry point
var helper = new level1Function();
helper.level2FunctionA();
}
function onFormSubmit() { // second entry point
var helper = new level1Function();
helper.level2FunctionC();
}
function level1Function() {
this.level2FunctionA = function() {
console.log('hi');
}
function level2FunctionB() {
// how do I invoke level2FunctionA from here w/o breaking onOpen entry point?
}
this.level2FunctionC = function() {
level2FunctionB();
}
}
onOpen();
onFormSubmit();
// looking for 2 hi's to the console, one through each flow
create a reference to a variable self, assign to this at the top of the function body
function level1Function() {
var self = this;
this.level2FunctionA = function() {
console.log('hi');
}
function level2FunctionB() {
self.level2FunctionA();
}
this.level2FunctionC = function() {
level2FunctionB();
}
}
Another solution, instead of creating a reference to self as that is error-prone in many situations, you could use Function.prototype.bind and create a function boundLevel2FunctionB, which has this bound to the current level1Function instance (I see you're calling it using the new keyword).
Code:
[...] // level2Function body
function level2FunctionB() {
this.level2FunctionA();
}
var boundLevel2FunctionB = level2FunctionB.bind(this);
this.level2FunctionC = function() {
boundLevel2FunctionB();
}
[...]
Cheers!

How do you handle with control binding in jquery

I have a question about jquery and DOM manipulation. How do you handle with DOM controls for e.g.
I have to get value from text input so I could this in ways:
var SomeClass = function() {
var control;
this.setControl = function(c) {
control = c;
}
this.getValue = function() {
return control.val();
}
}
$(document).ready(function() {
var sc = new SomeClass(); // of course control could be passed in contructor as well
sc.setControl($('#CONTROL'));
console.log(sc.getValue());
});
OR
var SomeClass = function() {
var control = $('#CONTROL');
this.getValue = function() {
return control.val();
}
}
$(document).ready(function() {
var sc = new SomeClass();
console.log(sc.getValue());
});
what is your opinion? What is better or maybe this is pile of trash therefore what is the best solution. Plz dont send me to backbone, spine and so on Im interesed in only in jquery.
best!
EDIT:
do you separate logic from UI or you are mixing it?
more complicated example
in js file you have a class that uses text control and in the secound js file also you need values from this input. What you are doing? you just call everytime $('#control') or create a third js file where would be a separated "class" to manipulate this input?
It would make more sense to move the setValue() inside the constructor:
SomeClass = function(c) {
var control = c;
return {
getValue: function() {
return control.val();
}
}
}
var x = new SomeClass($('input'));
alert(x.getValue());
However, I'm not sure how valuable this kind of information hiding will be. Perhaps as some kind of view wrapper.
In many cases you wouldn't need this wrapper, so just:
var $x = $('input'); // keep reference to a bunch of <input> elements.

cannot access function within function in javascript

I need to know what I am doing wrong because I cannot call the internal functions show or hide?
(function()
{
var Fresh = {
notify:function()
{
var timeout = 20000;
$("#notify-container div").get(0).id.substr(7,1) == "1" && (show(),setTimeout(hide(),timeout));
var show = function ()
{
$("body").animate({marginTop: "2.5em"}, "fast", "linear");
$("#notify-container div:eq(0)").fadeIn("slow");
},
hide = function()
{
$("#notify-container div").hide();
}
}//END notify
}
window.Fresh = Fresh;
})();
Fresh.notify();
thanks, Richard
UPDATE
If you wanted to be able to do something like: Fresh.notify.showMessage(), all you need to do is assign a property to the function notify:
var Fresh = {notify:function(){return 'notify called';}};
Fresh.notify.showMessage = function () { return this() + ' and showMessage, too!';};
Fresh.notify();//notify called
Fresh.notify.showMessage();//notify called and showMessage, too!
This will point to the function object here, and can be called as such (this() === Fresh.notify();). That's all there is too it.
There's a number of issues with this code. First of all: it's great that you're trying to use closures. But you're not using them to the fullest, if you don't mind my saying. For example: the notify method is packed with function declarations and jQuery selectors. This means that each time the method is invoked, new function objects will be created and the selectors will cause the dom to be searched time and time again. It's better to just keep the functions and the dom elements referenced in the closure scope:
(function()
{
var body = $("body");
var notifyDiv = $("#notify-container div")[0];
var notifyDivEq0 = $("#notify-container div:eq(0)");
var show = function ()
{
body.animate({marginTop: "2.5em"}, "fast", "linear");
notifyDivEq0.fadeIn("slow");
};
var hide = function()
{//notifyDiv is not a jQ object, just pass it to jQ again:
$(notifyDiv).hide();
};
var timeout = 20000;
var Fresh = {
notify:function()
{
//this doesn't really make sense to me...
//notifyDiv.id.substr(7,1) == "1" && (show(),setTimeout(hide,timeout));
//I think this is what you want:
if (notifyDiv.id.charAt(6) === '1')
{
show();
setTimeout(hide,timeout);//pass function reference
//setTimeout(hide(),timeout); calls return value of hide, which is undefined here
}
}//END notify
}
window.Fresh = Fresh;
})();
Fresh.notify();
It's hard to make suggestions in this case, though because, on its own, this code doesn't really make much sense. I'd suggest you set up a fiddle so we can see the code at work (or see the code fail :P)
First, you're trying to use show value when it's not defined yet (though show variable does exist in that scope):
function test() {
show(); // TypeError: show is not a function
var show = function() { console.log(42); };
}
It's easily fixable with moving var show line above the point where it'll be called:
function test() {
var show = function() { console.log(42); };
show();
}
test(); // 42
... or if you define functions in more 'traditional' way (with function show() { ... } notation).
function test() {
show();
function show() { console.log(42); };
}
test(); // 42
Second, you should use this instead:
... && (show(), setTimeout(hide, timeout) );
... as it's the function name, and not the function result, that should be passed to setTimeout as the first argument.
You have to define show and hide before, also change the hide() as they said.
The result will be something like this:
(function()
{
var Fresh = {
notify:function()
{
var show = function()
{
$("body").animate({marginTop: "2.5em"}, "fast", "linear");
$("#notify-container div:eq(0)").fadeIn("slow");
},
hide = function()
{
$("#notify-container div").hide();
},
timeout = 20000;
$("#notify-container div").get(0).id.substr(7,1) == "1" && ( show(), setTimeout(hide,timeout) );
}//END notify
}
window.Fresh = Fresh;
})();
Fresh.notify();
I think order of calling show , hide is the matter . I have modified your code . It works fine . Please visit the link
http://jsfiddle.net/dzZe3/1/
the
(show(),setTimeout(hide(),timeout));
needs to at least be
(show(),setTimeout(function() {hide()},timeout));
or
(show(),setTimeout(hide,timeout));

call function inside a nested jquery plugin

There are many topics related to my question and i have been through most of them, but i haven't got it right. The closest post to my question is the following:
How to call functions that are nested inside a JQuery Plugin?
Below is the jquery plugin i am using. On resize, the element sizes are recalculated. I am now trying to call the function resizeBind() from outside of the jquery plugin and it gives me error
I tried the following combinations to call the function
$.fn.splitter().resizeBind()
$.fn.splitter.resizeBind()
Any ideas, where i am getting wrong?
;(function($){
$.fn.splitter = function(args){
//Other functions ......
$(window).bind("resize", function(){
resizeBind();
});
function resizeBind(){
var top = splitter.offset().top;
var wh = $(window).height();
var ww = $(window).width();
var sh = 0; // scrollbar height
if (ww <0 && !jQuery.browser.msie )
sh = 17;
var footer = parseInt($("#footer").css("height")) || 26;
splitter.css("height", wh-top-footer-sh+"px");
$("#tabsRight").css("height", splitter.height()-30+"px");
$(".contentTabs").css("height", splitter.height()-70+"px");
}
return this.each(function() {
});
};
})(jQuery);
I had the same problem. Those answers on related posts didn't work for my case either. I solved it in a round about way using events.
The example below demonstrates calling a function that multiplies three internal data values by a given multiplier, and returns the result. To call the function, you trigger an event. The handler in turn triggers another event that contains the result. You need to set up a listener for the result event.
Here's the plugin - mostly standard jQuery plugin architecture created by an online wizard:
(function($){
$.foo = function(el, options){
// To avoid scope issues, use 'base' instead of 'this'
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("foo", base);
base.init = function(){
base.options = $.extend({},$.foo.defaultOptions, options);
// create private data and copy in the options hash
base.private_obj = {};
base.private_obj.value1 = (base.options.opt1);
base.private_obj.value2 = (base.options.opt2);
base.private_obj.value3 = (base.options.opt3);
// make a little element to dump the results into
var ui_element = $('<p>').attr("id","my_paragraph").html(base.private_obj.value1 +" "+ base.private_obj.value2+" " +base.private_obj.value3);
base.$el.append(ui_element);
// this is the handler for the 'get_multiplied_data_please' event.
base.$el.bind('get_multiplied_data_please', function(e,mult) {
bar = {};
bar.v1 = base.private_obj.value1 *mult;
bar.v2 = base.private_obj.value2 *mult;
bar.v3 = base.private_obj.value3 *mult;
base.$el.trigger("here_is_the_multiplied_data", bar);
});
};
base.init();
}
$.foo.defaultOptions = {
opt1: 150,
opt2: 30,
opt3: 100
};
$.fn.foo = function(options){
return this.each(function(){
(new $.foo(this, options));
});
};
})(jQuery);
So, you can attach the object to an element as usual when the document is ready. And at the same time set up a handler for the result event.
$(document).ready(function(){
$('body').foo();
$('body').live('here_is_the_multiplied_data', function(e, data){
console.log("val1:" +data.v1);
console.log("val2:" +data.v2);
console.log("val3:" +data.v3);
$("#my_paragraph").html(data.v1 +" "+ data.v2+" " +data.v3);
});
})
All that's left is to trigger the event and pass it a multiplier value
You could type this into the console - or trigger it from a button that picks out the multiplier from another UI element
$('body').trigger('get_multiplied_data_please', 7);
Disclaimer ;) - I'm quite new to jQuery - sorry if this is using a hammer to crack a nut.
resizeBind function is defined as private so you cannot access it from outside of it's scope. If you want to use it in other scopes you need to define it like that
$.fn.resizeBind = function() { ... }
Then you would call it like that $(selector').resizeBind()
You have defined the resizeBind function in a scope that is different from the global scope. If you dont'use another javascript framework or anything else that uses the $ function (to prevent conflict) you can delete the
(function($){
...
})(jQuery);
statement and in this way the function will be callable everywhere without errors
I didn't test it:
this.resizeBind = function() { .... }

Categories

Resources