subForm, Form et object - reloaded

Hi, I’m back with the Form command for page subForm.
All the context is for developing components, widgets, using subForm.

In my host form, I have an instance like this one
image

And here is my issue :
Inside my subForm, I just want to use the Form everywhere each time I need to use this instance object where I put in it all my options, datas…, rather than using:

$ptr_widget:= **OBJECT Get pointer** (Object subform container)

And from my host, I don’t want to fill in “Variable or expression” as I want to keep it “process variable free”.

If I do that way, some points to take into account.

  1. on On Load event, the Form always give a null and not a {}, as the object still do not exist (will be created later by 4D).
  2. But then, the Form object will be local only to the widget, and not connected to the parent container variable ! There will be two objects! So no link between parent and subForm.

I got surprised discovering it, but at last you can get it in the documentation, as below.

https://doc.4d.com/4Dv17/4D/17.4/Page-subforms.300-4880698.en.html

If you select the Object type (with a specified variable name), you will be able to get or set the properties of this object from within the subform context using the Form command (see Using the subform bound object below).

4D automatically binds an object (C_OBJECT) to each subform. The contents of this object can be read and/or modified from within the context of the subform, allowing you to share values in a local context.

The object can be created automatically or be the parent container variable, if explicitly named and typed as Object (see below). In all cases, the object is returned by the Form command, which can be called directly the subform (using a pointer is useless).

Ok, then to have the Form object and the parent container variable being the same, I need to use an already declared object in the variable. Ok, let’s try with an C_OBJECT process variable, which I don’t like. Do not work too as the component code executes before the container code (not able to initialize that variable). All this seems to be done only for simple subForm with user interface used with objects in the interface (variables). Not for programming as components.

Question
This is a terrible miss : why not being able to get the same object for Form inside the component and in the parent container with

$ptr_wog_text:= **OBJECT Get pointer** (Object current)

I need an already declared object in the parent instance for the widget itself to get a Form being the parent container object variable ! So I need a process variable, I need to initialize this variable before the component, which is not possible as the On Load inside executes first… Mind dead loop…

My only solution

parent instance, deal with

$ptr_wog_text:= **OBJECT Get pointer** (Object current) or Self

Component inside, deal with

$ptr_widget:= **OBJECT Get pointer** (Object subform container)

It would be so easier being able to use Form command…
This Form is really poor in the component situation.

This post is to clarify the situation, perhaps get new tricks, and have 4D aware of this kind of use of the Form command in subForms.

Maybe you can take a look at this widget and get some ideas:

Form is an object which is a reference. If you need a link from the subform to the parent, perhaps you can use a circular reference.

form-form.zip.pdf (11.7 KB)

(remove the .pdf extension after download)

Yes, interesting, but on my “On Load” in the subForm, the object is still Null and I can’t initialize it inside the subForm.

Let’s see, I’ll have a look on your answers.
Thanks !

Vincent,
is it ok to get this on methods ?
image

What is
var$0 : Object
var$1

I’m not familiar with this kind of syntax :thinking:

I am sorry but I think you have to forfeit the idea of “initialising it inside the subform”. The inner content is always created before the outer container. In that sense, the parent-child analogy is incorrect.

You need to CALL SUBFORM CONTAINER or CALL FORM in order to talk to the container. I would not recommend other routes (e.g. using a process variable). For instance, would it work when the subform is inside a subform which is inside a subform?

The subform design is somewhat similar to event propagation in JavaScript (capturing and bubbling). The “On Load” phase is bubbling, so it must start from the subform at the lowest level and work its way up.

Looks like a 18 R4 project. This is a new syntax.

1 Like

Yes for sure !
The idea is to have an object, initialized with default values inside the widget, with the component On Load method.
Then I want to be able to change some parameters locally for a specific instance in the parent subForm method, executing after.

For that, I need Form available inside the widget immediately on On Load.
And to have this Form object the same as the parent instance.
Doing this, I can access in parent method with
$ptr_wog_text:= OBJECT Get pointer (Object current)

I’m not sure to be very clear. I’ll draw a schematic tomorrow :slight_smile:

As far as I know, the only way to do that is to set the container type to something other than “object” (perhaps “undefined”). Then, you have an empty object as Form, because it is no longer connected to the parent form which does not exist at this point. You can connect this object to the container by code, like so:

Form.form:=New object("foo";"bar")
Form.parent:=Null
		
OBJECT Get pointer(Object subform container)->:=Form

The container form can receive this object and attach itself.

Form.subform:=OBJECT Get pointer(Object named;"Subform")->
Form.subform.parent:=Form

I’ve posted an example here:


Your design seem to imply Form:=OBJECT Get pointer(Object named;"Subform")-> but I don’t know if that is allowed.

Yeeeees, not bad :slight_smile:
You gave me plenty of ideas, I’ll deal with it and tell you my final made decision.

Thanks !

Yes, here it what I need :slight_smile:

In widget

$event:=FORM Event

Case of 
	: ($event.code=On Load)
		  //Form.form:=New object("foo";"bar")
		  //Form.parent:=Null
		
		OBJECT Get pointer(Object subform container)->:=Form
		Form.foo:="bar"
		
End case 

Then available in widget everywhere with Form.
And in host, available too !

Thanks Keisuke Miyako.
PS which one is your firstname ?

I still stuck…

This is because Form is a command and not an object, so I can’t do

Form:=OB Copy($ob_options_init) // Form unchanged

Then I tried

$ptr_ob_options:=OBJECT Get pointer(Object subform container)
$ptr_ob_options->:=Form // copy reference
$ptr_ob_options->:=OB Copy($ob_options_init) // Form unchanged!

Same ! Even a reference to Form do not update Form when changed.
This is not understandable, and is an issue often for me.

At last, this works (slower) :

C_OBJECT($ob_options_init)
$ob_options_init:=$1

$ptr_ob_options:=OBJECT Get pointer(Object subform container)
$ptr_ob_options->:=Form
//$ptr_ob_options->:=OB Copy($ob_options_init) // NOK

ARRAY TEXT($T_keys;0) // MANUAL OB COPY!
OB GET PROPERTY NAMES($ob_options_init;$T_keys)
$tt:=Size of array($T_keys)
For ($i;1;$tt)
	$key:=$T_keys{$i}
	OB SET(Form;$key;OB Get($ob_options_init;$key))
End for 

Hi,

I fall into the same issue and I found a more interesting approach. The only way to declare your object even before any event occurs, is to create your object before the display of the form and pass it via the formData.

You can do it that way

$form:=New object
$form.subForm_details:=New object
Open form window("myForm")
DIALOG("myForm";$form)

Declaring some Form properties before calling DIALOG save me a lot of issues regarding the order of the on load events of my form objects.

Hope this call help to solve your issue without having to use pointers.

3 Likes

As mentioned into the read me

The main branch continues to evolve as 4D application evolves and now uses, among other things, Classes and Style sheets to allow an adaptation to the look & fill of your database.

The last version v18 compatible is available here

So, yes the main branch is for the next available release.

[edit] you can replace by “classic” declarations “C_xxx”

Hi Miyako,
I think it can be reversed this way:

//host form method
case of
:(on load)
OBJECT SET SUBFORM(...)
end case

I often tend to build dynamically, recently I realized (by chance :hot_face:) that the subform goes after this way.

1 Like

http://blog.bnf.fr/lecteurs/index.php/2012/11/quel-est-lordre-des-noms-au-japon-la-question-sindbad-du-jeudi/

1 Like

Keïsuké is the given name, Miyako is the family name. Full name is typically expressed in the reverse order, last name+first name. No spaces in between, we don’t use spaces at all (like medieval Latin). But we can normally tell which is which so the order doesn’t matter.

There is no concept of “being on first name terms”. We use first names in nursery and elementary school (6 to 12 years old), but after that, it feels kind of childish. That said, It is not inappropriate for adults to call each other by their first name to show affection, especially if they know each other since childhood.

c.f. “friend from infancy (osananajimi)”

Otherwise, we use our last name most of the time. Other asian cultures may take the distinction between first name and last name more seriously. I just prefer MIYAKO because it is easier to memorise for Anglophones.

3 Likes

Thanks for the explication then! I know that asking for given name and family name in an other culture might be a kind of non sense…
Mmmm I like Keisuke as it sounds more like a first name as to express my affection on your devotion to explain and teach us best practices ! Do you mind ? :blush:

Hi Gabriel, I was to try that way, but it is not satisfying my mind. I need to declare in advance for all the instances of all widget objects ready for use inside them ? And then I need to enter it in “Variable or expression” in the parent suForm ? No no !
My solution works well. I did the change in ogTools, it took 10 minutes to use Form everywhere instead of my form object “ob_options” called with

$ptr_ob_options:=OBJECT Get pointer(Object named;"ob_options")

And all the glue to get this object thru

  $ptr:=OBJECT Get pointer(Object named;"ob_options";$formName)

So, now, all is available directly inside each instance by Self->.
We can easily change options parameters directly in the On Load of every instances.

Inside my search box

image

C_LONGINT($evt)
$evt:=Form event
Case of 
	: ($evt=On Load)
		wog_resize 
		
		  // OLD (and still working in NEW) {
		C_OBJECT($ob)
		$ob:=New object
		$ob.is_autoAccept:=True
		$ob.font_size:=12
		$ob.font_name:="Alba"
		wog_setOptions ($ob) // Do a one by one property overload to the current one
		  // }
		  // NEW {
		$ob:=Self-> // Properties are modified directly
		$ob.is_autoAccept:=True
		$ob.font_size:=12
		$ob.font_name:="Alba"
		wog_setOptions   // Just to refresh options changes
		  // }
		wog_setValue_txt ("Auto validate !")
		
	: ($evt=<>on_data_change)
		
End case 

Even in the debugger, it is much easier to see what are your options object filled in with : just add Self… and you get all your values.