David Schwartz 426 Posted November 6, 2022 (edited) I'm building an app that uses a REST API. The API itself is rather simple. There are a dozen optional parameters that you can send to a few of the endpoints as 'xyz=<boolean>' where <boolean> is a string of either 'true' or 'false', along with a few parameters that take one of a few strings or a number. I created a unit with API-related methods in it and it's called from the main form when needed. That much is working with default parameter settings. Now I want to add a simple way to let the user select API options and have them passed to the API unit without the API unit knowing anything about the UI, and without requiring a ton of arguments on one setter method or a ton of properties to set every possible parameter. (These are all inputs to the API calls; the output is a JSON packet.) The app right now is a VCL app, and it may change. I have created a Page Control with several tabs that have checkboxes on them with an explanation next to each one that says what it's for. I made the caption on the checkboxes the same as the name of the API setting, like 'xyz' above, with the thought that I could use it somehow in the settings if I collect the components into an array. The challenge is that you need to clear parameters before each API call, and add a few locally (URL, login name and pwd) that are buried in the API unit. (I suppose they could be moved into the Main unit if it helps.) So when I call the API method, it calls an internal method to clear the Parameters, adds the URL, login and pwd params, and its at THIS POINT where I need to add the OTHER params selected by the user on the UI side. Then I call RESTRequest.Execute and wait for the response. I can think of several different ways to do this, all of which are fairly convoluted and inelegant. Even if I just have a bunch of setters that need to be called, I'd need to pass in a callback method that's called to add them to the list of parameters at exactly the right point. All of the examples I can find tend to be very simplistic, mixing the UI and the API calls in the same unit; I'm trying to keep them separate, but SOMETHING needs to be used to get the values from the UI elements and either save them for later, or inject them directly as parameters into the POST call at the point where it's being created. This would seem like it would be a fairly consistent pattern when you have an app with a UI and one or more units with API-specific logic in them, and you don't want to have the API units having to know anything about what's in the UI, yet it needs to include the values of settings made in the UI at a place in the logic flow that's quite inconvenient for the API's client to access. I'm curious if anybody here has come up with a clean and fairly simple solution to this problem that they'd care to share? Edited November 6, 2022 by David Schwartz Share this post Link to post
Attila Kovacs 629 Posted November 6, 2022 in this case you need a wrapper unit with either making a gateway or extending the API with helpers Share this post Link to post
PhilPlus 6 Posted November 7, 2022 Hello I'm not sure I understand your problems. I have an application with params used in API & VCL interface, every param is declared in a Table (from Database or Inifile) and is used dynanically to construct the param VCL Form AND the API Swagger file. For xyz param the table store its name (xyz) its label ("My parm xyz") its type (integer) and osme other informations (default value...) If in the interface xyz is set to '1' the API (here a GET method) become http://127.0.0.1/api/param?xyz=1& Share this post Link to post
David Schwartz 426 Posted November 9, 2022 (edited) I've decided to just have a callback method at the point where the parameters can be added before calling the API that sends a stringlist with the names of the parameters that particular call can accept (eg, <name>), and it gets back '<name>=<value>' for each <name> in the list. This is as opposed to what I typically see which is a long list of .AddParam( 'somevar', Form1.Edit1.Text ) calls that reach back into the UI unit (eg, Form1) and grab the values from the Date/Edit/Checkbox/Memo fields directly. Why should the API unit have intimate knowledge of what's on a form? As much as people love to talk about the principles of OOP, it baffles me that so many Delphi programmers still seem to toss all of that out the window when it comes to Delphi Forms. It's like, "Oh, Forms ... yeah, they're objects, but ... meh ... it's too much trouble to define properties and DI and stuff. That 'encapsulation' nonsense is for library files, not our own units." Edited November 9, 2022 by David Schwartz Share this post Link to post
Pat Foley 51 Posted November 9, 2022 (edited) /*----In EventModule---*/ Edits: Tlist<TtextPair>; // business logic here /*----elsewhere in UI realm--*/ EB.Edits.add(TtextPair.create(uf.Edit2,uf.lbleditErrors)); //uf ~ TForm //source Remu Lebeau response https://stackoverflow.com/questions/64145742/cannot-change-tedit-text-in-delphi. TtextPair = Tpair<TComponent, TComponent>; //edit Timer scans the cached edits length and does stuff. OT I been running some JS thanks for those posts on TMS and TypeScript. Edited November 9, 2022 by Pat Foley add TPair 1 Share this post Link to post
haley 2 Posted November 10, 2022 4 hours ago, David Schwartz said: As much as people love to talk about the principles of OOP, it baffles me that so many Delphi programmers still seem to toss all of that out the window when it comes to Delphi Forms. It's like, "Oh, Forms ... yeah, they're objects, but ... meh ... it's too much trouble to define properties and DI and stuff. That 'encapsulation' nonsense is for library files, not our own units." I know right? I'm like that too when it comes to Delphi, LOL. Usually when I'm using C# or Angular, creating classes are so easy, you can write one up so quickly that you don't have to think about it. In Delphi it's a whole lot more verbose, and annoying, so I try and avoid it and end up working around it. That's just my opinion anyway. 1 Share this post Link to post
David Schwartz 426 Posted November 19, 2022 On 11/9/2022 at 5:07 PM, haley said: I know right? I'm like that too when it comes to Delphi, LOL. Usually when I'm using C# or Angular, creating classes are so easy, you can write one up so quickly that you don't have to think about it. In Delphi it's a whole lot more verbose, and annoying, so I try and avoid it and end up working around it. That's just my opinion anyway. hmmm ... yeah .... you need to type a LOT of stuff that should be available at the click of a button, given a few clues. I discovered there's a function in the JSON library named JsonToObject that I thought initially would read a JSON packet and generate a class then fill it with data. It turns out you need to define a class yourself, then pass it a JSON packet and a class reference; it will read the JSON, create an instance of the class and then fill the fields it finds that match -- which means the fields need to have the same names as in the JSON field names. This is a fair trade-off to me. (The class can have other stuff as well; that method just looks for fields in the JSON file that match in the class definition and copies the values over. I haven't tried using lists or arrays or things like that, so I'm not sure how it handles them, tho.) It should be that easy! Share this post Link to post
PhilPlus 6 Posted November 19, 2022 Hello David Again I don't see the real problem. When you call the API just send a function which parse the parameter TTABSheet and return a tstringlist (or a string or an array). function sendData(MyTab: TTabSheet): tstringList; var comp: TComponent; begin for comp in MyTab do begin if (comp is Tcheckbox) and then Result.add('"' + comp.Name + '":"' + booltostr(Tcheckbox(comp).checked)) +'"' //or other logic else begin // other comp logic; end; end; end; After you just have to add the TstringList.commatext into your JSON Request. 1 Share this post Link to post