Add command to receive data source of form object as expression (was: Add This. to form entry objects)

The Form object is very powerful and is a welcomed addition, however, when using it as an expression in a form object, it is impossible to address the data source (expression) generically.

Adding this feature will allow the application of entry rules to form objects generically rather than coding explicitly for each object.

This is needed for any entry object, not just entity members.

Yes! Indeed, the support of objects in forms is very weak. Hopefully there will come new features soon!

Just for the challenge (and because of COVID 19), thanks to the 18R3 classes:

I’ve made a class “input”

Class constructor
	
	C_TEXT($1)
	
	This.dataSource:=$1
	This.value:=Formula from string($1).call()
	
Function this
	
	C_VARIANT($0)
	$0:=Formula from string(This.dataSource).call()
	
Function value
	
	C_VARIANT($0)
	$0:=This.value
	
Function setValue
	
	C_VARIANT($1)
	This.value:=$1
	EXECUTE FORMULA(This.dataSource+":=This.value")

then created a form with an input where expression is “Form.testThis” and write during the on load form event

Form.testThis:="Hello"
Form.test:= cs.input.new ("Form.testThis")

I also put into the form a button to get the value with this code:

ALERT(Form.test.this())

and another one to set the value:

Form.test.setValue("Hello World")
1 Like

Lahav, +1000. I tried to give your suggestion a thumbs up like. But for some reason there is no thumbs up button available on your message. There is on Christian and Vincent’s replies tho. Weird.

Vincent, Your idea could work, but it requires initialisation and a shadow copy of input objects just to manage the getting/setting of their value. To me this just adds complexity. I need to remember that Form.test.setValue() sets the value of Form.testThis. Huh? That is going to get very messy very quickly.

I’d like to extend this Feature Request by also requesting 2 new commands Object Get Value and Object Set Value. With these 2 commands I don’t need to know if an Object is bound to a variable or an object property, it should just work the same.

I know that form objects can be bound to read only values, eg. Current Date, so Object Set Value makes no sense. It’s up to me the developer not to try and set a value in this situation.

1 Like

This is very slightly off topic, unless you consider that This is just a way of getting/setting a Form Object’s value.

My frustration is that there is no easy way to get/set a value for a Form Object that is bound to a property of a C_OBJECT.

I’ve found a way, with a bit of a hack, to get a Form Object’s data source/expression by using FORM Convert to dynamic. Once you have this you can use EXECUTE FORMULA to get/set the value. Like I said, it’s a bit of a hack, but it does work. However, it doesn’t work for objects created with OBJECT DUPLICATE, so it’s not 100%, but until 4D gives us native commands it’s the best I can do.

FORM Convert to dynamic is an “expensive” command to run, so it probably makes sense to cache the results, but I haven’t done that yet.

//Method: Object Get Expression

//Returns the Expression / Data Source of the Form Object named $1
//(I would have named this method "Object Get Data Source", however, there is a 4D Command named that, 
//but it only returns a Pointer to a Variable, if the the Data Source is a Variable)


C_TEXT($0)  //Object Expression / Data Source
C_TEXT($1)  //Optional, Object Name.  Default = the Current Object

C_LONGINT($l_Page)
C_OBJECT($o_Form)
C_TEXT($t_ObjectName)

Case of 
  : (Count parameters<1)
  	$t_ObjectName:=OBJECT Get name(Object current)
  : ($1="")
  	$t_ObjectName:=OBJECT Get name(Object current)
  Else 
  	$t_ObjectName:=$1
End case 


If ($t_ObjectName#"")
  If (Current form table=Null)
  	$o_Form:=FORM Convert to dynamic(Current form name)
  Else 
  	$o_Form:=FORM Convert to dynamic(Current form table->;Current form name)
  End if 
  
  If ($o_Form.pages#Null)
  	$0:=String($o_Form.pages[FORM Get current page].objects[$t_ObjectName].dataSource)  //The Object is most likely to be on the Current Page, so start looking there
  	
  	If ($0="")  //Not on the Current Page, so look at all Pages
  		For ($l_Page;0;$o_Form.pages.length-1)
  			If ($l_Page#FORM Get current page)  //We've already checked the Current Page, so no need to check it again
  				$0:=String($o_Form.pages[$l_Page].objects[$t_ObjectName].dataSource)
  				
  				If ($0#"")
  					$l_Page:=999
  				End if 
  			End if 
  		End for 
  	End if 
  End if 
End if
//Method: Object Get Vaule

C_VARIANT($0)  //Object Value
C_TEXT($1)  //Object Name.  Default = the Current Object

C_TEXT($t_DataSource;$t_ObjectName)

C_VARIANT(__ObjectValue)  //Required for EXECUTE FORMULA


Case of 
  : (Count parameters<1)
  	$t_ObjectName:=OBJECT Get name(Object current)
  : ($1="")
  	$t_ObjectName:=OBJECT Get name(Object current)
  Else 
  	$t_ObjectName:=$1
End case 


If ($t_ObjectName#"")
  $t_DataSource:=Object Get Expression ($t_ObjectName)
  
  If ($t_DataSource#"")
  	EXECUTE FORMULA("__ObjectValue:="+$t_DataSource)
  	$0:=__ObjectValue  //Params and Locals can't be used in EXECUTE FORMULA
  End if 
End if
//Method: Object Set Vaule

C_TEXT($1)  //Object Name
C_VARIANT($2)  //Value

C_TEXT($t_DataSource;$t_ObjectName)

C_VARIANT(__ObjectValue)  //Required for EXECUTE FORMULA


If ($1="")
  $t_ObjectName:=OBJECT Get name(Object current)
Else 
  $t_ObjectName:=$1
End if 


If ($t_ObjectName#"")
  $t_DataSource:=Object Get Expression ($t_ObjectName)
  
  If ($t_DataSource#"")
  	__ObjectValue:=$2  //Params and Locals can't be used in EXECUTE FORMULA
  	EXECUTE FORMULA($t_DataSource+":=__ObjectValue")
  End if 
End if

Hey Peter,

Can you create a feature request for Object Get Value and Object set Value?, I think they are probably a better solution to this issue as they are more complete.

Cheers,

Lahav

Hi Lahav. Good idea. Done

1 Like

(( this one i write before i see last posts …sorry… :wink:

Maybe it is useful when 4D creates new cmds or cmd-features.

  • $txtObjDataSrc:=OBJECT Get data source(*;$objName;Get text result)
  • $ptrObjDataSrc:=OBJECT Get data source(*;$objName;Get pointer result)
  • NEWcmd: OBJECT Get value(*;$objName)
  • NEWcmd: OBJECT Set value(*;$objName)

Take care, maybe “FORM Convert to dynamic” works only in binary-db,
but not in project-db.

In 4D docu i can not find any info about cmd-restrictions.
Yes minimum interpreted-mode in binary-db returns successful
a json-obj represent the form and its form-objects
(maybe this work too in compiled-mode in binary-db, i did not test).
Interpreted-mode in project-db returns always Object-Null,
looks like this command do not work in a project-mode.
It is so important to know all cmd-restrictions exactly
before use a command in your app/programmStruct.
Before i decide to build a bigger complex
based in central on a command, i must know
this command runs in all my existing solution types
and is flexible enough to switch or used in both binary+project.
When using such commands without knowing it restrictions outside
of my most usual work-space, i investigate time to integrate a solution
which maybe later must be total rebuild in a total other way
when i see “ooh this do no work in any of my destination-spaces”.
When i start concepting and developing, it is very helpful
to know cmd-restrictions, so i can decide
“good enough for my destinations” OR “not acceptable restrictions”.
To 4D,
please remove so much restrictions where it is possible can be done
or when restrictions exists write in documentation clearly
which restrictions for the command exists
(sometimes 4D did this, but not in all cmds with all restrictions listed always).

https://doc.4d.com/4Dv18R3/4D/18-R3/FORM-Convert-to-dynamic.301-4900966.en.html

Peter, you are right, it was just to change my mind during this confined period and because I am trying to change my way of thinking about code with 4D.

Hi Lutz,

Yes, I know. As I said my “solution” is very much a hack and doesn’t work in all situations. I wrote it out of desperation because there was no other way I could see to make Form Objects bound to a C_OBJECT work generically. It works for me for at the moment because I’m not using Project mode, but when/if 4D give us a native way to do this I’ll refactor all of my code to use it.

Hi Vincent,me too. I’m also trying to change the way I work with 4D by fully embracing using ORDA and the Form command extensively. But it’s so difficult when you can’t get at the value for a Form Object generically via its name. I don’t understand why we can’t get the Data Source or the Value of a Form Object bound to a C_OBJECT. To me it just seems like a huge missing feature. We’ve been able to do this for Form Objects bound to variables for years.

Even the brand new Formula command now has a Formula.soruce() function. I can’t even think of a situation I’d use that. But there are thousands of places I’d like to know the Data Source and/or Value of a Form object from it’s Name.

Would it be correct to modify the title of the request to “Add command to receive data source of form object as expression”?

Does that describe your need? Then I would like to modify the title to make sure we read, find and follow this feature correctly.

1 Like

I guess it would help a lot (for me) to create a new command: OBJECT Get Formula to get the formula-object expression of the object.

Hi Thomas,

If the change you propose gives us the ability to also assign values back to the data source, then yes, the subject can change.

My intent in requesting this feature is to unable us to write something like “This:=do_somthing(This)” in an object method.

I also like Peter’s suggestion (and feature request) for Object Get Value and Object set Value commands which would essentially do the same, but would have a wider use possibility.

If it makes sense to combine the two feature requests into one, I’d support it.

Thanks

1 Like

Hi Peter

First, thanks to you for coding a workarround solution and share it with us in forum,
sharing and discussing our thinking and coding helps us all always (direct or indirect, immediatly or later).

(Hi to all…)
Disscusing about missing objectGetPointer(Form.myobj)
(and objectGetDataSource, objectSetDataSource with Form.myobj
is not new in 4DForum. Other ForumMembers did this as long as Form.myobj
Feature exist and i did this too.
Take a look at my old post, yes its too with “FORM Convert to dynamic”,
because it was the only way to fetch these kinds of datasource.
https://forums.4d.com/Post/EN/29925997/1/29942011#29942011
(sorry i have three forum accounts, and i ask 4D to put all posts in my new account what are created from 4DForumAdmin).

Alternatives are, let datasource empty then 4D auto create a dynFormVariable
which can fetched with objectGetPointer (returns a pointer to dynFormVariable).
But this is not a solution for all, because most of time you need Form.myCollection
and This.myCollectionColumn or to adress any other subItems…

Remember for objectGetPointer, pointers can only point to a variable, field or table.
Dot-Notation “Form.myA” or “$obj.myA” is not a variable, it is a expression
and you can not have a pointer to a expression!

Datasource can always be a expression like “Form.collectionA”
or any other expression for example “Current time”.
With objectSetValue($objName) you can not set the value for those expression :wink:
but you can always do a objectGetValue($objName).
So objectSetValue is a little bit difficult to done in a generic/dynamic coding routine.
Not all datasource-definitions are containers which can receive/store a value.
When 4D create new command objectSetValue,
then 4D internaly must check "is expression value-set-able (this is not easy to detect).
I did not know 4D can solve this…

What 4D easier can do is to give objectGetDataSource a new option
to return its result as text or as pointer. So as text you can get any datasource-expression
and detecting with own code is-this-datasource-value-set-able.

Maybe 4D found a way to generate auto dynFormVariables (like when datasource empty)
for all datasource-definitions which syntax is like “Form.myA.myX”
and for syntax like “This.myX”.

  // OBJECT Get pointer() give as result a pointer back.
  // Pointer can only point to a variable, field or table!
  // To Point to a expression or anything else is not possible.
C_POINTER($ptr)
C_OBJECT($obj)
Form.myA:="test"
$obj:=Form
If (False)
	  // error can not be done
	$ptr:=->Form  // ptr to a command/expression not possible 
	$ptr:=->Form.myA  // ptr to a expression not possible 
	$ptr:=->$obj.myA  // ptr to a expression not possible 
Else 
	  // ok, can be done
	$ptr:=->$obj  // ptr to variable $obj which is a representation of the form-object
	$ptr->myA:="anyNewValue"
End if

I understand, while I’m not sure how readable it is.
I also got the impression it is about generic coding.

So thinking more, i started to think this is not only about an object method.
It is about any kind of data source which was before a variable (=Pointer) and can be now any kind of expression, including an object.

An object on screen could have the expression:
Current Time

this makes no sense for that - and you still want to know the data source, right?
A listbox might have an assigned form.selection expression - how to read or change that?

Summary: so far 4D allows to set/get datasource for an object if that can be done via a pointer. I read that thread as there is a need to set/get datasource for any kind of expression as formula, not as pointer. And that for any kind of data source of an object, including listbox selection sources or listbox column footer source, etc. This need is independent of a context, not only in the object method or at a given event, but any time, any where…

1 Like

yes, i think so too, “add This” is not a full solution at all, we need a solution for anywhere and not only inner objectMethod. Nice when This can made, but than we need more too for using outside of objectMethod.

In Debugger/RuntimeExplorer i found a feature where i can
manually get/set a value of every formObject (listed with objectNames).
So 4D build inner this tool a solution to do this.
Please when it is possible, build a integrations of this cool feature in programming interface…

1 Like

Hi Thomas and Lutz.

I get what you;re saying about a Form Object’s data source being read only, e.g. Current Time, and that it can only be get, not set. But why would I ever want to set a read only Form Object? As developers we’re still 100% in control of how we use these commands. So, I don’t think that 4D should need to “know” that a Form Object’s data source is read only. That’s the developers job, and if they try to set the value then 4D should just throw an error. I view it as the same as writing Current Date:=!2020-05-05!. It makes no sense, and 4D would error on it. (I know this would be caught at compile time, and a Form Object being set would be caught at runtime, but you know what I mean)

Thomas said;

Summary: so far 4D allows to set/get datasource for an object if that can be done via a pointer. I read that thread as there is a need to set/get datasource for any kind of expression as formula, not as pointer. And that for any kind of data source of an object, including listbox selection sources or listbox column footer source, etc. This need is independent of a context, not only in the object method or at a given event, but any time, any where…

Yes, that’s exactly right. We’d like to have commands to get and set (if appropriate) a Form Object’s value based on its Name. If this worked with List boxes that would be awesome, but if we could just have this for scalar Form Objects, that would still be a major step forward.