Forums

Find answers, ask questions, and connect with our
community all around the world.

Home Forum Omnis General Forum Subform questions

Tagged: ,

  • Subform questions

    Posted by Mischa on July 5, 2023 at 11:20 am

    Dear all,

    I’m currently trying to convert windows/subwindows to remotforms/subforms and running against unexpected limitations – so hopefully an expert here is able to help out.

    To avoid confusions – ‘subform‘ means the remote form ($classname) that is used within the subform object of a main remote form.

    1. How do I get the contents of a field assigned to a subform object ($dataname) into a subform? In a subwindow, you simply would do something like

    Calculate $cinst.$objs.Contents.$dataname as $cinst.$dataname

    … where Contents would be an entry field within the subwindow

    2. How do I call a public method of a subform object from within a subform ? In subwindows, you simply would do something like

    Do $cinst.$mymethod

    thanks a lot

    Mischa

    Andreas Pfeiffer replied 4 months, 3 weeks ago 2 Members · 17 Replies
  • 17 Replies
  • Andreas Pfeiffer

    Administrator
    July 5, 2023 at 11:45 am

    Hi Mischa,

    You can take subforms as separate instances and thus they ought to be and work independently. This is why I would not use the technic to access a field from the main form directly. That would mean a subform can only be used in the context of a single main form.

    That said you can obviously build interfaces between the subform and a main form. For example when a subform is constructed it might want to know the ID of the main form. Therefore I would possible have a superclass that contains a $getID method and the can then get the ID of the main form like this:

    Do $cinst.$load($cinst.$container().$getID())

    This could be in the $construct of the subform (or its superclass) that then calls its own $load using the ID of the container form.

    I especially like this technique when the $multipleclass of the subform component is set to kFalse. That makes it easier to just load the subform each time another subform is loaded.

    The other direction is also possible, calling a method within the subform from the main form:

    Do $cinst.$objs.mySubform.$subinst().$load($cinst.$getID())

    However the problem here is that you can only do this once the subform is fully loaded. Some people try to assign a new $classname and in the same stack they then try to call a method within the subform. That will not work because the stack has to be finished before you can access that instance.

    You can check this out with the little app I designed for the Madagascar event: https://www.omnis.net/community/forums/forum/discussion/contacts-web-application/

    In that application each subform has the same superclass and the superclass will then call its $load method when the form constructs. The readme file says that you would need to copy the htm files from that source. If using Omnis Studio 11, don’t do this as the htm source is slightly different. This was just because of the different font that I was using.

    I hope this helps.

    Best,

    Andreas

    Contacts Web Application

    • Mischa

      Member
      July 6, 2023 at 1:09 am

      Hi Andreas,

      thank you very much for your reply and your demo – where I’m still trying to get my head around 😉

      You can take subforms as separate instances and thus they ought to be and work independently.

      Not sure if I understand this correct – so the subform is not part of the main form as subwindow is in the fat client?

      Do $cinst.$load($cinst.$container().$getID())

      What I don’t like with this approach (when I understand it correct) is, that the subform needs to know about its main form, while the subwindow inherently knows about the methods behind the subwindow object, regardless how the main window is structured and how deep the subwindow object is buried in panes, groups or other subwindows.

      Maybe it’s easier to show what I want to achieve:

      Ok, seems that the forum software cannot display pictures as inline graphics, so please refer to the attached picture.

      I designed a country picker, because this is something you come across all the time, often within the same window/form, where you need to pick a nationality for a person, and in another stage, a home address.

      In the fat client, I can simply drag the country picker from the component store into the window and assign a variable to $dataname, e.g. iClient.Nationality – that’s all I need to make it work. The subwindow automatically adopts the style of the window – a property I have not discovered for subforms yet (?) – and the main window does not need to know anything about the country picker, it just provides the field containing the country code, that is filled, altered and displayed in the subwindow. I can add the country picker es many times as I want into the main window – providing different datanames is all it takes to keep them apart – no separate callbacks/returns for each picker in the main window.

      However, I’m be bit lost how to achieve the same thing with subforms?

      thank you!

      Mischa

      • Andreas Pfeiffer

        Administrator
        July 10, 2023 at 7:29 am

        Hi Mischa,

        The subform is part of the main form – yes and no. Looking at it from an OO perspective it is its own object (instance) and therefore communication between those two supposed to be via a public interface (public methods).

        In case of your example you could do the following:

        Place a public method behind your subform field, e.g. $getValue which returns the data you need to deal within your subform field.

        Then in the $construct of the subform you can access this method like this:

        Do $cinst.$field().$getValue() Returns iValue

        So instead using the $dataname you would then have a public method that allows you to communicate with the main form.

        I hope this helps.

        Best,

        Andreas

        • Mischa

          Member
          July 18, 2023 at 6:38 am

          Hi Andreas,

          thanks a lot for your reply and please excuse my late answer – the $field() property was exactly, what I was looking for, and from there on I did a lot of experiments to utilize the $dataname property of the subform field. Seems that Omnis simply forgot about it, but I finally succeeded with a lot of tricks.

          While functionally it now works as intended, the GUI is a mess – please look at the picture attached.

          On top you see the design of the subform in Omnis, and below the result. At the bottom you see, how it should look like.

          thanks a lot

          Mischa

  • Andreas Pfeiffer

    Administrator
    July 18, 2023 at 7:00 am

    Hi Mischa,

    How many layout breakpoints does the subform have? Maybe worth deleting the bigger one?

    Best,

    Andreas

    • Mischa

      Member
      July 19, 2023 at 6:42 am

      Hi Andreas,

      ahhh – this was it, thanks a lot! Since I’m a total newbie to jsClient – may I ask, why Omnis chooses the 320 over the 768 layout in this case? The subform field has a width of 422, so it should be well over the 320 breakpoint.

      And another difficulty I run into – the label field (the field that contains the country flag and name) does not react when it’s clicked, although it is enabled and $events is set to evClick.

      The $getval and $setval functions of the subform field are created dynamically from within the $construct of the subform – this works nice, the only problem was to get the $dataname, where the values are coming from and written to – it’s nowhere in the subform instance (why?), but looking into $cclass was the solution 😉

      Thanks a lot

      Mischa

  • Andreas Pfeiffer

    Administrator
    July 19, 2023 at 7:31 am

    Hi Mischa,

    The layout breakpoint is the minimum size of your layout. That means when the width of the browser reaches the next layout breakpoint it will change to the next layout breakpoint. So for example if you have a 320 and a 768 layout breakpoint then the 320 is shown until the browser reaches 768. Once the browser widths is 768 minimum it will use that one.

    Regarding the automated creation of a method it actually has two sides of a coin. I would assume that this code is harder to maintain. You could think of the Omnis class that the method definitions will already be stored in the library and the elegance of Omnis is that it exposes the methods graphically that are linked to the object. When you do this dynamically you remove this feature. But anyhow, I am glad that it works for you now. 🙂 There is also another downside to this. If you need to make a method client executed then you cannot create it dynamically because Omnis translates those methods into JavaScript in that moment when the class is saved (Cmd/Ctr-S).

    Regarding the click event behind the label field please check if the label is active and enabled.

    I hope this helps.

    Best,

    Andreas

  • Mischa

    Member
    July 19, 2023 at 10:34 am

    Hi Andreas,

    So for example if you have a 320 and a 768 layout breakpoint then the 320 is shown until the browser reaches 768

    I guess, here on my desktop the browser should be way beyond 320 pixel (even when they are logical)? However, since the country picker should look like a standard entry field, everything is done with floating attributes, so actually no layout breakpoint is needed.

    Regarding the automated creation of a method it actually has two sides
    of a coin. I would assume that this code is harder to maintain

    I general you’re absolutely right, and I normally would not work with dynamical methods. Also, they mess with the new method editor (since $OS 10), breakpoints do not work anymore.

    But here we have two totally simple methods only, and they stay the same for every subform, where this technique is used (that’s every subform in my case 😉).

    $getval -> Quit method MyVar
    $setval -> Calculate MyVar as pValue

    The obvious problem here is that the name of MyVar is unknown to the subform instance, this is why I was so keen to utilize the $dataname property of the subform field. It’s the logical place to assign a variable to a field – you do it in every other field type, too.

    So I assign the variable MyVar to the $dataname of the subform field, and add another line to the generated methods and do it like that:

    Calculate mDataname as $cclass.$objs.[$cinst.$cfield.$name()].$dataname 
    Quit method [mDataname]

    Regarding the click event behind the label field please check if the label is active and enabled

    <div>ahhh – I missed the active flag. Maybe it should be placed into the Visibility & State section, too.</div><div>

    Thanks a lot, this was great help!
    Mischa

    </div>

    
    

  • Andreas Pfeiffer

    Administrator
    July 19, 2023 at 2:47 pm

    Hi Mischa,

    If the subform field width is smaller than 768 it will take the next smaller layout breakpoint.

    Regarding your code generation I thought it might be worth to put an interface into a superclass of your subforms. Without any coding in the main form you could probably do something like this:

    in the $construct of your subform (or better its superclass) put the following code:

    Set reference lClassRef to $cinst.$container().$class

    Calculate iDataName as lClassRef.$objs.[$cinst.$field].$dataname

    Now you have the $dataname which could be used like this:

    Calculate iTest as $cinst.$container().[iDataName]

    From now on you could use $cinst.$container().[iDataName] to get or set the value.

    I hope that makes any sense.

    Best,

    Andreas

  • Mischa

    Member
    July 20, 2023 at 9:26 am

    Hi Andreas,

    If the subform field width is smaller than 768 it will take the next smaller layout breakpoint

    Now I got it. So when I provide only one layout breakpoint, it will be always used?

    Calculate iTest as $cinst.$container().[iDataName]

    This is really lovely! Thanks a lot, no more hassle with dynamic methods 😉

    Calculate iDataName as lClassRef.$objs.[$cinst.$field].$dataname

    It seems that [$cinst.$field] resolves to kTrue, so I’m always getting the $dataname of the first object in the container form – but [$cinst.$field.$name()] does the trick.

    Still need to do some tests if the $class approach (either dynamic or your solution) also works, when the subform field is buried within containers like panes or complex grids.

    best greetings and thanks a lot!

    Mischa

    • Andreas Pfeiffer

      Administrator
      July 20, 2023 at 9:37 am

      Hi Mischa,

      Yes – if you have only one layout breakpoint it will always use this one.

      Before Omnis Studio 11 the minimum number of layout breakpoints was two. For this purpose you could have changed the $layouttype property to kLayoutTypeSingle. However I think with Omnis Studio 11 it is kinda obsolete now because we can now have only one layout even though the $layouttype is set to kLayoutTypeResponsive. The beauty of this approach is that your subforms can share the same super classes. If the layout type is not the same you can actually not.

      One more about $cclass. It will work – but when it comes to inheritance you might get the wrong class. So $cinst.$class is probably what you want to get the reference of the class. Otherwise if your code is within the superclass you would get the reference to the superclass.

      I also tested making a reference variable that points to the variable that is used in the main form in the $dataname of the subform component. Unfortunately this is not supported. You cannot assign an item reference to the $dataname of a component. However you can have the reference and then have some code to assign or read from that item reference.

      I am glad that your issues are now resolved.

      Best regards,

      Andreas

      • Mischa

        Member
        July 25, 2023 at 2:27 am

        Hi Andreas,

        as already suspected, the $class approach does not work, when the subform field is within another container like a tab pane.

        Also, there appears to be no easy way to drill down the object path from within the subform instance, since the instance has a flat hierarchy. I ended up with creating a dynamic method behind the subform field:

        Calculate mDataname as $cfield.$container().$name
        While mDataname<>''
        Calculate mPath as con(mPath,mDataname,'.$objs.')
        Calculate mDataname as $cinst.$objs.[mDataname].$container().$name
        End While

        Calculate mPath as con(mPath,$cfield().$name)
        Calculate mDataname as $cclass.$objs.[mPath].$dataname
        Quit method mDataname

        This works, however I really would love to see the $dataname property being available also in subform instances – this would make it much easier and more elegant. May I suggest this enhancement request?

        thanks

        Mischa

        • Mischa

          Member
          July 25, 2023 at 8:43 am

          ooops the first line in the While loop should read

          Calculate mPath as con(mDataname,'.$objs.',mPath)

          😉

          • Andreas Pfeiffer

            Administrator
            July 25, 2023 at 1:35 pm

            Hi Mischa,

            I did enter an ER.

            Best,

            Andreas

            • Mischa

              Member
              July 25, 2023 at 7:49 pm

              Thanks Andreas!

            • Mischa

              Member
              February 17, 2024 at 11:24 am

              Hi Andreas,

              *ggg* found out that the $dataname property actually is available within a client method – kinda makes sense 😉

              best greetings
              Mischa

            • Andreas Pfeiffer

              Administrator
              February 19, 2024 at 10:47 am

              Hi Mischa,

              Yes – all properties but $name need to be read within a client executed method.

              Best,

              Andreas

Log in to reply.