I'm rusty with delegates and closures in JavaScript, and think I came across a situation where I'd like to try to use one or both.
I have a web app that behaves a lot like a forms app, with fields hitting a server to change data on every onBlur or onChange (depending on the form element). I use ASP.NET 3.5's Web Services and jQuery to do most of the work.
What you need to know for the example:
isBlocking()
is a simple mechanism to form some functions to be synchronous (like a mutex)isDirty(el)
checks to make sure the value of the element actually changed before wasting a call to the serverAgent()
returns a singleton instance of the WebService proxy classgetApplicationState()
passes a base-64 encoded string to the web service. This string represents the state of the application -- the value of the element and the state are passed to a service that does some calculations. The onSuccess function of the web service call returns the new state, which the client processes and updates the entire screen.waitForCallback()
sets a flag that isBlocking()
checks for the mutexHere's an example of one of about 50 very similar functions:
function Field1_Changed(el) {
if (isBlocking()) return false;
if (isDirty(el)) {
Agent().Field1_Changed($j(el).val(), getApplicationState());
waitForCallback();
}
}
The big problem is that the Agent().Field_X_Changed
methods can accept a different number of parameters, but it's usually just the value and the state. So, writing these functions gets repetitive. I have done this so far to try out using delegates:
function Field_Changed(el, updateFunction, checkForDirty) {
if (isBlocking()) return false;
var isDirty = true; // assume true
if (checkForDirty === true) {
isDirty = IsDirty(el);
}
if (isDirty) {
updateFunction(el);
waitForCallback();
}
}
function Field1_Changed(el) {
Field_Changed(el, function(el) {
Agent().Field1_Changed($j(el).val(), getTransactionState());
}, true);
}
This is ok, but sometimes I could have many parameters:
...
Agent().Field2_Changed($j(el).val(), index, count, getApplicationState());
....
What I'd ultimately like to do is make one-linen calls, something like this (notice no getTransactionState()
calls -- I would like that automated somehow):
// Typical case: 1 value parameter
function Field1_Changed(el) {
Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val()), true);
}
// Rare case: multiple value parameters
function Field2_Changed(el, index, count) {
Field_Changed(el, delegate(Agent().Field1_Changed, $j(el).val(), index, count), true);
}
function Field_Changed(el, theDelegate, checkIsDirty) {
???
}
function delegate(method) {
/* create the change delegate */
???
}
Ok, my first question is: Is this all worth it? Is this harder to read but easier to maintain or the other way around? This is a pretty good undertaking, so I may end up putting a bounty on this one, but I'd appreciate any help you could offer. Thanks!
UPDATE
So, I've accepted an answer based on the fact that it pointed me in the right direction. I thought I'd come back and post my solution so that others who may just be starting out with delegates have something to model from. I'm also posting it to see if anybody wants to try an optimize it or make suggestions. Here's the common Field_Changed()
method I came up with, with checkForDirty
and omitState
being optional parameters:
function Field_Changed(el, args, delegate, checkForDirty, omitState) {
if (isBlocking()) return false;
if (!$j.isArray(args) || args.length == 0) {
alert('The "args" parameter in Field_Changed() must be an array.');
return false;
}
checkForDirty = checkForDirty || true; // assume true if not passed
var isDirty = true; // assume true for updates that don't require this check
if (checkForDirty === true) {
isDirty = fieldIsDirty(el);
}
if (isDirty) {
omitState = omitState || false; // assume false if not passed
if (!omitState) {
var state = getTransactionState();
args.push(state);
}
delegate.apply(this, args);
waitForCallback();
}
}
It handles everything I need it to (check for dirty, applying the application state when I need it to, and forcing synchronous webservice calls. I use it like this:
function TransactionAmount_Changed(el) {
Field_Changed(el, [cleanDigits($j(el).val())], Agent().TransactionAmount_Changed, true);
}
cleanDigits
strips out junk characters the user may have tried to type in. So, thanks to everyone, and happy coding!
OK, few things:
Function.apply
lets you call a function with an array of arguments.
So you can write it this way
function Field_Changed(delegate, args)
{
if (isBlocking()) return false;
if (isDirty(args[0])) { //args[0] is el
delegate.apply(this, args);
waitForCallback();
}
}
And call it as:
Field_Changed(Agent().Field2_Changed, [el, getApplicationState(), whatever...]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With