In FoxInCloud desktop and web applications, developer can easily adapt the visual display to the context at runtime by either hiding/showing, enabling/disabling, or adding/removing objects.

FoxInCloud supports all these scenarios, with some nuances explained below.

Hide/show, enable/disable objects at run time

Hiding/showing and enabling/disabling objects at runtime only requires that the visible and/or enabled property is part of the list in the .wcPropSave property of the corresponding object(s).

This can be done either at design time, at any class level or in the object itself, or at runtime, in the class or object .Init() method, with the help of the awPublic.prg!wcPropSaveEdit() function:

procedure Init
local success
success = dodefault()
...
wcPropSaveEdit(this, 'Visible') && adds 'visible' to this.wcPropSave
...

return m.success
endproc

Note that you can add some properties to .wcPropSave at some class level, and remove them (for optimization), at a lower level of the class and/or object hierarchy if the property always keeps the same value at runtime:

define class parentClass as someClass
procedure Init
local success
success = dodefault()
wcPropSaveEdit(this, 'Visible') && adds 'visible' to this.wcPropSave
return m.success
endproc

define class childClass as parentClass
procedure Init
local success
success = dodefault()
wcPropSaveEdit(this, 'Visible', .T.)  && removes 'visible' from this.wcPropSave
return m.success
endproc

This principle applies to other properties altering object’s aspect such as .Font*, .Fore*, .Back*, .Disabled*, .Border*, etc.

Adding/removing objects at runtime

FoxInCloud supports adding/removing objects at runtime with a behavior slightly different in desktop mode and web mode.

Adding/removing objects at .Init()

If you .addObject() and/or .removeObject() in any .Init() method, these objects will or won’t appear in the rendered HTML.

As an alternative to .removeObject(), you can simply return .F. or return .T. in the target object.Init() depending on the situation; eg remove an object in desktop mode while keeping it in web mode:

procedure someObject.Init
return wlWeb() and dodefault() && wlWeb(): web mode execution (modify command awPublic)

Please note that any member you add or remove when form.wlInitFirst or by return .F. in members.Init() will be shared by ALL users; eg. you can’t use a user-related function (such as a role/security function) to determine whether a control should appear or not at runtime.

The form.Init() case

Note that in web mode, form.Init() has a slightly different behavior from the other base classes.

form.Init() executes:

  • at first time a form is used: FoxInCloud Server instantiates the form and keeps a reference to it for later re-use by other users; in this case, form.Init() executes without parameter and should always return .T.
  • each time a user needs a form, FoxInCloud Server executes form.Init() with whatever parameter and context (eg parenForm.dataSessionID when using .dataSession=1 && Default data session) for this specific user case. In some sense, form.Init() should more be viewed as a user event than a ‘static’ event.

You can determine whether form.Init() execution is ‘blank’ or ‘user-based’ by testing thisForm.wlInitFirst:

procedure form.Init

dodefault()

if this.wlInitFirst
  this.removeObject('someObject')
  this.addObject('someOtherObject')
  return
endif

&& ... regular code ...

endproc

Adding/removing objects during a user event

note: in what follows, 'container' means any VFP class that can contain other objects such as form, container, page, etc.

Whenever you need to add or remove objects during a user event, based on some dynamic condition such as the user profile or any other contextual condition, you can take advantage of .wlContentDynamic:

  • set .wlContentDynamic = .T. either at design time or in container.Init()
  • for a container with .wlContentDynamic = .T., calling .addObject() or .removeObject() automatically orders FoxInCloud Application Server to re-generate the HTML and JS of this container’s contents, and refresh browser display accordingly:
procedure someUserEvent && eg Click()

if (Type('m.thisForm.wlHTMLgen') == 'L' AND m.thisForm.wlHTMLgen)
  return
endif

with someContainer_wlContentDynamic

  local oObject, cObject, success

  for each oObject in .Objects foxobject
    .removeObject(m.oObject.Name)
  endfor

  select someAlias
  scan
    cObject = 'obj' + transform(someAlias.ID)
    success = .addObject(m.cObject, 'classOfDynamicObject', someAlias.field1, field2, ..., fieldN)
    assert m.success && for debugging
    if m.success
      store .T. to ('.' + m.cObject + '.Visible') && by default, VFP sets .Visible = .F.
    endif
  endscan
endwith

Be aware that this process implies some overhead:

  • addObject() and removeObject() per se
  • re-generating the HTML

note: if your container implements .addObject() and/or .removeObject() methods, make sure to call dodefault() after your code

Scope of .wlContentDynamic support

.wlContentDynamic are mainly designed for containers populated with static objects with a limited user interaction (eg segments in a Gantt Chart, list of users, etc.).

If you do need user interaction on such dynamically added objects, make sure to implement a JavaScript handler executing a method of some form member that is permanent – in other words, that all users share –, eg the dynamic container:

procedure classOfDynamicObject.someEvent && eg Click

if (Type('m.thisForm.wlHTMLgen') == 'L' AND m.thisForm.wlHTMLgen)
  local cScript as String
  text to cScript textmerge noshow flag 1 pretext 3
    FoxInCloud.MethExec(/* {en} Executes a method of a VFP form member object {fr} Exécute une méthode sur un objet membre d'un formulaire VFP */
      event /* {en} event or event source object {fr} événement ou son objet source */
    , <<this.Parent.wcID>> /* {en} id of HTML object matching VFP object holding the method {fr} id HTML de l'objet VFP détenant la méthode */
    , 'someMethod' /* {en} method of this.Parent {fr} méthode de of this.Parent */
    , <<this.someProperty>> /* {en} Value to be passed as method parameter - undefined for none {fr} Valeur à passer en paramètre à la méthode - undefined n'en passe aucun */
    );
  endtext
  && see FoxInCloud.js for more parameters to FoxInCloud.MethExec()

  return m.cScript
endif

endproc

note: the FoxInCloud base classes (aw.vcx) implementation of .addObject() and .removeObject() makes sure the object exists or not before attempting to add or remove it; this avoids useless errors in web mode.