Newbies ploh Posted July 5, 2007 Newbies Posted July 5, 2007 Greetings. I am new to FM and am having some difficulty using script parameters. I tried searching prior topics to find the answer I'm looking for, so forgive me if this is redundant. I am trying to use script parameters to pass multiple variables and everything seems to work fine except for passing the value of a field. I have a button that when activated will call a script to go to a different layout than the current one, create a new record, and pass along the primary key to the foreign key. In the script parameter dialog box I have entered the following: "id = "trade:__kp_tradeID"; layoutName = "letter"" "id" is supposed to store the value of the primary key field, __kp_tradeID. I am trying to retrieve the value of "id" with an evaluate function: Evaluate ( "Let ([" & Get(ScriptParameter) & "]; id)") Instead of returning the value of the field, it returns a text value of "trade:__kp_tradeID". How do I pass the value of the field instead of its name? I tried eliminating the " around the field name, but then it returned no value at all. Am I on the right track or am I going about this incorrectly? Thanks, peter
Fenton Posted July 5, 2007 Posted July 5, 2007 (edited) If id is going to be a Variable, then it should have the global variable syntax: $$id It can be set by declaring it in a Let() function in a Script Parameter. Let ( [ $$id = trade:__kp_tradeID; $$layoutName = letter ]; 1 ) The 1 is just to give the calculation something to do; it's unhappy otherwise -] The variables $$id and $$layoutName can then be referenced by the script called (or any other script, or an unstored calculation field) simply by the same syntax; no need for Get ( ScriptParameter ) or Evaluate(). They will be persistent within the file until it is closed or they are reset. Something like that. Edited July 5, 2007 by Guest
Newbies ploh Posted July 5, 2007 Author Newbies Posted July 5, 2007 Hmm. When I try to put that Let function into the script parameter box and hit ok, I get an error saying it can't find the table specified. In my file, I was following the example from the SEU Filemaker 8 book. For passing multiple variables to a script they have you enter the values as a string with the "name/value pairs separated by semicolons." Then within the script you access the values using an Evaluate function. My problem is that they only describe how to assign values that are either numerical or text strings. They don't describe how to assign the value of a field. Is this not possible?
Fenton Posted July 5, 2007 Posted July 5, 2007 Well, I used what you wrote earlier. I think I know what the problem is. It is not a field: letter, it's a text value "letter" I noticed something was wrong, at the edge of my brain, but didn't really reconsider. Basically, open up the Script Parameter, by hitting the Edit button next to it, so you get a real calculation dialog. The SP box is misleading (as is the Script Variable box), because it escapes your quote marks. If you have anything serious to do, open it as a regular calculation dialog. Let ( [ $$id = trade:__kp_tradeID; $$layoutName = "letter" ]; 1 )
garbanzito Posted July 19, 2007 Posted July 19, 2007 I am trying to use script parameters to pass multiple variables and everything seems to work fine except for passing the value of a field. [...] In the script parameter dialog box I have entered the following: "id = "trade:__kp_tradeID"; layoutName = "letter"" "id" is supposed to store the value of the primary key field, __kp_tradeID. I am trying to retrieve the value of "id" with an evaluate function: Evaluate ( "Let ([" & Get(ScriptParameter) & "]; id)") [...] Am I on the right track or am I going about this incorrectly? just noticed this post while searching for something, and want to record the correct answer for posterity, if not for those involved sorry, but Fenton's suggestion is not very good -- yes you can set script vars in a Let(), and it's very powerful (see my parameter technique for how to really use this feature), but if you want to set global script vars, don't stuff this into the script parameter dialog, just use the Set Variable script step; in general, though, i'd avoid using globals for parameters, since they will collide with other scripts using the same parameter name; just fix the conventional approach you are using or use my advanced technique cited above anyhow, yes--you are on the right track, you just haven't structured your expression quite right; you need FMP to evaluate the field reference while it constructs the parameter string; but instead you are quoting the field reference; the proper expression is: "id = "" & trade::__kp_tradeID & ""; layoutName = "letter"" actually, you don't have to quote numeric values in such eval strings, so this would work too: "id = " & trade::__kp_tradeID & "; layoutName = "letter"" that should do it; sorry Fenton :)
comment Posted July 19, 2007 Posted July 19, 2007 I'd suggest that instead of just calling a suggestion "not very good", you explain what you think is wrong with it. in general, though, i'd avoid using globals for parameters, since they will collide with other scripts using the same parameter name I am not sure what "parameter name" means in this context. If you meant that global variables defined in one place can collide with similarly named variables defined elsewhere - well, then let's not use them at all. :confused:
garbanzito Posted July 20, 2007 Posted July 20, 2007 I'd suggest that instead of just calling a suggestion "not very good", you explain what you think is wrong with it. i feel i did explain but i'll be more detailed; using Let() assign a value to a global script variable in the parameter expression is simply burying the assignment, not really following a parameter-passing paradigm; it may save a line of script code, but it is now harder to maintain and understand; if you must use globals, just use Set Variable instead if you really want to pass parameters similarly to other languages, it's more useful to follow the common approach ploh was attempting, or use the technique i referenced (which automatically creates local variables on script entry); this way once you enter the script you'll have a local value that won't collide with other scripts and supports recursive calls, etc. I am not sure what "parameter name" means in this context. If you meant that global variables defined in one place can collide with similarly named variables defined elsewhere - well, then let's not use them at all. yes, that's what i meant; the paradigm we are simulating is having "named parameters", so the "parameter name" is the name of the Let() variable or script variable you create; using globals as parameters is old style FileMaker-think, and doesn't improve much on using global fields for the same purpose so yes, you're on the right track when you suggest not using globals at all; you might enjoy reading one of the original treatises on the subject, however i don't know where to read that for free; here's another decent paper i pulled from a quick search; here's another ... do your own search! in practice, i suggest using global variables very sparingly, mainly to get around some of FileMaker's limitations; i'd use them perhaps to keep the global state of an application, and especially to simulate certain dynamic techniques that can't be done with global fields (like creating an associative-array type storage by using Let() to make global variables with dynamic names) ... there are simply better ways to pass parameters
comment Posted July 20, 2007 Posted July 20, 2007 yes, you're on the right track when you suggest not using globals at all; I was being sarcastic. I see nothing wrong with using global variables - provided one knows what one is doing. But this caveat applies to any tool, so there's nothing special about global variables in this aspect. I can't see anything wrong in declaring a few variables while defining the script parameter, either. If you're afraid they will collide with other variables, then either adopt some kind of a system to manage your variables, or don't do it - but then it's no more than a personal preference.
garbanzito Posted July 21, 2007 Posted July 21, 2007 I was being sarcastic. i know you were being sarcastic, but i found it useful to deadpan it read the references i listed and find more of your own; the example of the crackpot who wrote a system with 5000 globals was an extreme example, but highlights the trouble one will brew by using globals to pass parameters; there are much better ways, especially if one ever wants someone else to decipher a solution
The Shadow Posted July 21, 2007 Posted July 21, 2007 Also, global variables aren't going to work if the script is in a different file, as the globals ($$ vars) are only global to a single file.
comment Posted July 21, 2007 Posted July 21, 2007 I haven't read your references, but I have read arguments against globals before, and rejected them as I would any such sweeping dismissals (have you ever heard "Never use repeating fields"?). I don't know if 5000 globals necessarily indicate a crackpot, but if they do, it wasn't the globals' fault. I will agree that using local variables, if possible, is preferable to indiscriminate declaration of global ones. I will also agree that naming SCRIPT variables is best done in the script itself, for maintenance reasons. But this criticism applies to your method just as well, so in this aspect it is equally "not very good". For lurkers: Related thread
Ender Posted July 21, 2007 Posted July 21, 2007 At the risk of stepping out of my glass house to toss a few rocks, this sounds like a debate we've had around here before. I tend to agree with garbanzo about not liking the idea of populating lots of global variables. Somehow the code seems cleaner if values only persists as long as needed (maybe it's some of my early procedural programming discipline poking out). Unfortunately, I've been too long away from procedural programming, and I'm susceptible to trying neat tricks like declaring variables in a script parameter. My saving grace is that I've already got the script parameter parsing stuff down and standardized, so I haven't gotten around to using the let() technique. I know for years it was difficult to make "clean" code in FileMaker. Lots of globals for holding temporary values and counters. How much clean up is necessary after their use? Is it safe to reuse this global or that? Fortunately, with script parameters in FM7 and variables in FM8, it's become easier to do things in a clean, modular way. But maybe old habits die hard, and we tend to use the easy way instead of the modular way. Declare variables wherever because we can. Not worry about them after the script finishes. Sometimes FileMaker makes it too easy to be undisciplined. :
Ender Posted July 21, 2007 Posted July 21, 2007 I will agree that using local variables, if possible, is preferable to indiscriminate declaration of global ones. I will also agree that naming SCRIPT variables is best done in the script itself, for maintenance reasons. Hey, how about that, you came around to my way of thinking even before my post! Boy, you're easy! For lurkers: Related thread I thought this stuff sounded familiar. You'll note how I'm smarter now in my old age. :
comment Posted July 21, 2007 Posted July 21, 2007 you came around to my way of thinking I don't think I did, but I am glad you think so - if you're going to toss rocks.
garbanzito Posted July 21, 2007 Posted July 21, 2007 I haven't read your references, but I have read arguments against globals before, and rejected them as I would any such sweeping dismissals i didn't give you a sweeping dismissal, but your rejection sure sounds like one (have you ever heard "Never use repeating fields"?). I don't know if 5000 globals necessarily indicate a crackpot, but if they do, it wasn't the globals' fault. it would help if you had read it before commenting; but yeah, massive overuse of a weak technique is not the technique's fault I will agree that using local variables, if possible, is preferable to indiscriminate declaration of global ones. I will also agree that naming SCRIPT variables is best done in the script itself, for maintenance reasons. But this criticism applies to your method just as well, so in this aspect it is equally "not very good". well, don't take the term "script variable" too literally, but yes, the best place to define parameters is in the definition of the procedure; FileMaker doesn't do this naturally, and doesn't allow multiple parameters at all, so part of my technique is to put the parameter names into the script name, approximating the conventional programming technique and serving as a reference to help avoid mistakes; another part of my technique is to predeclare the variables by setting them to default values before Let() does its thing; both of these put responsibility for parameter names as you suggest: squarely on the called script, rather than the caller this technique has been extremely helpful for me in developing large systems with multiple developers -- it's all about managing complexity, which is something you have to be very serious about to be effective building large FileMaker solutions even so, these techniques are still workarounds, and still a bit ugly compared to more evolved languages, like, um, Pascal in 1970 : For lurkers: Related thread some of that thread strikes me as folks gobsmacked by an eye-opening, but ultimately misguided, technique; it's always fun to see how much you can get done in one line of code, but in the case of stuffing a global-setting Let() into the parameter expression, there's really no point (i was being polite in my replies to the Tokerud blog post)
comment Posted July 21, 2007 Posted July 21, 2007 i didn't give you a sweeping dismissal, but your rejection sure sounds like one This type of argument never leads anywhere, and I'd suggest you refrain from it. best place to define parameters is in the definition of the procedure Well, then we are more or less agreed. FileMaker doesn't do this naturally, and doesn't allow multiple parameters at all I am hard-pressed to think of an example where multiple Set Variable[] steps in the script itself would not have access to the same data that a button definition has; the only thing that comes to mind is calling another script in another file. Even then, I expect in most cases there would be a relationship back that would allow loading the rest of the data, after passing the key as a single parameter. some of that thread strikes me as folks gobsmacked by an eye-opening, but ultimately misguided, technique; What a nice thing to say. Actually, I referred to that thread because it shows basically the same technique as the one suggested by you - except it's implemented a bit more elegantly (IMHO).
garbanzito Posted July 21, 2007 Posted July 21, 2007 This type of argument never leads anywhere, and I'd suggest you refrain from it. take your own advice, then I am hard-pressed to think of an example where multiple Set Variable[] steps in the script itself would not have access to the same data that a button definition has perhaps we develop different kinds of solutions; for example i've got a system consisting of about 15 modules; each module has a set of TOs (a squid) independent of the others, though some of the same tables are shared; a script manages the navigation between modules, and takes as arguments the name of the module, the primary key of the record the user wants to visit (or may be zero if the user should be taken instead into Find mode), and whether we should reload the saved mode of the module we are entering (used for returns from submodules) there are three parameters right there, none of which can be obtained by a relationship, and all are contextual since the script is reused in dozens of contexts; what careful design, including astute parameterization, makes possible is writing fewer scripts and building an abstract model of an application so that for example if the navigation has to be fixed, it may be fixed in one place rather than in 40 places What a nice thing to say. Actually, I referred to that thread because it shows basically the same technique as the one suggested by you - except it's implemented a bit more elegantly (IMHO). sorry, : i missed the download example (but at least i read most of what you linked to); anyhow, now we're talking! the Tokerud technique is basically worthless, it gains you nothing, but the download example is actually exactly the same technique i've described in the Dunning link, building local variables with Let() and Evaluate(), except for two things: it makes the developer rebuild the Let() and Evaluate() calls for each script call (where my custom function encapsulates them), and it doesn't return a success flag (which can be very helpful for debugging); i do think my version of that technique is easier to use and makes for clearer parameter strings, but like most things FileMaker, i won't claim it is elegant also when calling a subscript i wouldn't pass the same parameter string, i would explicitly rebuild the parameters so as to not couple multiple scripts to exact the same parameter definitions still, most in that thread didn't seem to have grokked the download example
comment Posted July 21, 2007 Posted July 21, 2007 Some of your points may be worth further discussion, but the atmosphere you insist upon makes it impossible for me to do so. Have a nice day.
JesseSFR Posted July 21, 2007 Posted July 21, 2007 This was the most entertaining read I think I have ever found on fmforums. Both sides have valid points in their own right. I would like to through my own two cents in there just for fun. This whole discussion is built upon FileMakers inability to allow multiple parameters in the first place so most of the solutions can considered to be bascially hacks. 1. I complete agree with comment on the fact that assigning local variables using the let statement isn't the best way to go. It definitely achieves two of your goals. The ability to keep the scope of the variable within the script you are sending it to and the ability to get the parameters by referencing them by a name. The problem with it, as previously mentioned, is that the let statement is hard to maintain and the parameters names and values aren't completely transparent 2. I personally refrain from global variables at all costs because I think, for the most part, there are better ways to solve problems then through their use. Also, as Shadow stated global variables are indeed only accessible in a single file which makes them even less useful. As an aside, Server specific variables next release??? I wouldn't be the one who had to write that code. All this said, I still believe that there are ways of getting the same result that garbanzito suggested using a name/value pair parameter solution. Of course, I wrote it so I am probably a little biased. : The moral of the story is that FileMaker definitely should include some type of named parameter ability in their next release. This will help developers like ourselves to easily exchanging data between scripts and maintain the scope/context of each script easily. Local variables are a nice step but there is definitely work to be done. Let the lambasting begin.
garbanzito Posted July 22, 2007 Posted July 22, 2007 The problem with it, as previously mentioned, is that the let statement is hard to maintain and the parameters names and values aren't completely transparent i just want to point out that in my technique the Let() call is in a custom function -- no maintenance needed; building the parameter string uses a natural syntax which is very clear to read, though constructing the string with embedded quotes takes a little thought the first couple of times; still it's easy to debug and very efficient to code with in practice All this said, I still believe that there are ways of getting the same result that garbanzito suggested using a name/value pair parameter solution. Of course, I wrote it so I am probably a little biased. : that doesn't really get the same result, but it could ... any way to treat the parameter as name/value pairs is an improvement, though, at least in concept, over using globals; i think your particular setup sells best as part of a general dictionary serialization solution; i'll put some comments expanding on that on your blog
Genx Posted July 22, 2007 Posted July 22, 2007 Wouldn't it be more prudent to allow the user to pass something like... "$test='foo';$testTwo='bar' & someTable::SomeField & '_someBar'" ...Instead of having them escape quotes any time they wanted to include a literal value?
garbanzito Posted July 22, 2007 Posted July 22, 2007 Wouldn't it be more prudent to allow the user to pass something like... "$test='foo';$testTwo='bar' & someTable::SomeField & '_someBar'" ...Instead of having them escape quotes any time they wanted to include a literal value? i don't find escaping that cumbersome, even though it's required also for non-literal text values (numeric values need not be quoted); but your idea would be easy to add to my technique; it would require munging of the parameter string before Evaluate(), so it wouldn't be quite as lightweight, at which point you might as well also auto-escape quotes within non-literal values, which cause Evaluate() to return null, and returns, which Evaluate() converts to spaces; and i guess you'd also have to auto-escape single quotes within values too ...
Genx Posted July 22, 2007 Posted July 22, 2007 ... what's the point of a lightweight custom function (used once in a script i might mention) that doesn't do much? Custom functions aren't worth hauling around all your files if it would be just as easy to type: Evaluate( "Let([" & Get(ScriptParameter) & "]; 1 )" )
garbanzito Posted July 22, 2007 Posted July 22, 2007 yeah you could do that; that's what i was doing before i built the custom function; any way to use name/value pairs and local variables in passing parameters is going to help manage complexity; but i'm a fast typist, and know the syntax well, and it really wasn't just as easy to type; in my solutions all the scripts are in one file, so it's much less trouble to add the custom function compared to extra verbiage in dozens of scripts; in a 25-file, rather than a 25-table solution, it might be a different story overall, my strategy is encapsulation ... that get_params() is lightweight and results in a shorter, clearer expression would be good enough for me, but it does more: 1) my version returns a flag for success or failure of the expression to evaluate; if the flag is false, i log an error and catch bugs sooner rather than later 2) if i want to change how the parameter passing works, such as adding auto-escaping of special characters like returns, i could easily do it later by simply changing the custom function rather than every script and 3) if get_params() is used consistently at the top of every script, it's available for refactoring what happens at script entry; in one system i did this -- i renamed "get_params()" to "script_entry()" and added the feature of setting a global $$SCRIPT to Get (ScriptName); my system's logging script knows this, and retrieves the value of $$SCRIPT before calling script_entry() itself to get the log entry params, and thus every log entry automatically specifies what script made the entry; this change didn't involve changing any scripts (except the logging script); as i noted toward the top of this thread, globals are useful for system-wide state variables
Genx Posted July 22, 2007 Posted July 22, 2007 Okay, I have no clue what you're on about, but that's cool if it works for you. But just before I leave you to your CF's, seeing as your clearly a fan of light weight CF's and not typing what isn't necessary, just a hint: If ( Expression=True ; True ; False ) is really the same as writing Expression which will return true if its true and nothing (i.e. false) if its not true.
garbanzito Posted July 23, 2007 Posted July 23, 2007 If ( Expression=True ; True ; False ) is really the same as writing Expression which will return true if its true and nothing (i.e. false) if its not true. thanks for noting that -- i hadn't reexamined that apart in a while; your expression will indeed work for this purpose in FileMaker, and point 1 above is moot my habits were learned elsewhere so i've been casting nulls explicitly to zero; i don't think i'd ever really need it in this case, but it helps when i want to do something like use a 1/0 value list to drive a radio button set, or pass a boolean to a less forgiving external system; in another version i used IsEmpty() to do the same thing; not sure why i chose to post it to dunning with an =1 test
Recommended Posts
This topic is 6335 days old. Please don't post here. Open a new topic instead.
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now