Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Accessing getter and setter methods in custom SmartGWT components from Javascript

    I have a feeling the answer to this is staring me in the face, but clearly I haven't yet put the pieces together in the right order to make it just so. So I apologize in advance. This is all in SmartGWT Power 11.1

    Let's keep it simple: my goal is to be able to set a custom property on a custom component upon an event occurring within a UI defined in ComponentXML.

    I have created my custom component class (IpListGridEditor), exposed it via the BeanFactory.MetaFactory, constructed the corresponding Component Schema and referenced it in a ComponentXML screen definition. This all works great, and we've been doing stuff like this for a long time now.

    I can even set my custom property (parentKeys) within the ComponentXML without a problem.

    Code:
    <IpListGridEditor 
        dataSource="PackingList" 
        autoFetchData="true" 
        ID="PackingList_PackingListGrid" 
        name="PackingList_PackingListGrid" 
        autoDraw="false"
         selectionType="single"
         showResizeBar="true" 
        dockEditor="bottom"    
        listEndEditAction="next"     
        canEdit="true"
        editByCell="true"    
        >
        <parentKeys><JS>{id:1}</JS></parentKeys>
    </IpListGridEditor>
    However, upon let's say a recordClick event I wish to set my custom property with a line along the lines of:

    Code:
    <recordClick>this.setParentKeys({packingListId:record.id});</recordClick>
    I'm unclear whether or not use of the BeanFactory class should mean that I can access the getter and setter methods of parentKeys, but the method isn't recognized. Neither is the property itself directly accessible to me, although I did include reference to it in IpListGridEditor.ds.xml:

    Code:
    <DataSource serverType="component" ID="IpListGridEditor"
        inheritsFrom="IpListGrid" instanceConstructor="com.islandpacific.gui.client.base.IpListGridEditor">
    
        <fields>
            <field name="parentKeys" type="AdvancedCriteria" basic="true"/>
        </fields>
    
    </DataSource>
    Perhaps I need to access the property through the static BeanFactory methods mentioned in the documentation? Or maybe I have to export the getter and setter methods via a JSNI method? Like I said, I'm pretty sure some combination of all that I've read will get me what I need but the combination of elements has so far eluded me. Can someone please supply me with a couple of pointers to set me on the right track? I won't necessarily need to be led there by the hand - just something to get me headed in the right direction.

    Thanks!

    #2
    You would indeed need to take your custom getter/setter methods and make them accessible to JS, by adding hand-coded JSNI methods that do essentially what our wrapper layer does but in reverse (going from JavaScript to Java). In this case, it's going to be relatively straightforward, since there's an AdvancedCriteria SGWT constructor that takes a JavaScriptObject. Bear in mind also that while your declaration of "parentKeys" in Component XML worked to assign the property to the class, your setter wasn't called; the value was simply copied onto the object like it was a custom property.

    Overall, you may want to carefully consider whether to go down this path - you are, at this point, basically writing part of your app in SmartClient and part of your application in SmartGWT, and in order to do this you will end up writing quite a bit of JSNI, some of it tricky. It may make sense to switch the whole stack to SmartClient, at least in this area of your application. After all, SmartClient now has more type safety (via our upcoming TypeScript support), and if you are writing JavaScript and JSNI in lots of places just to call methods you've written in GWT Java, it cannot be said that you are actually benefiting from Java's type safety and compile time checking anymore.

    Comment


      #3
      Thank you so much for the response. I am glad to read that I hadn't missed something obvious.

      As of this morning I determined that I can read the value of parentkeys via (the undocumented):

      Code:
      PackingList_PackingListGrid.getAttribute("parentKeys);
      I can also see the parentKeys attribute name listed in the attributes returned by (the also undocumented):

      Code:
      PackingList_PackingListGrid.getSGWTAttributes();
      So I thought I was onto something there, although there is no corresponding setAttribute() method available to me.

      Anyway, your response makes a lot of sense. I would jump right on it and construct the component using SmartClient if we didn't have such an amount of code dependent on it (all of it in SmartGWT except for the screens we have implemented via ComponentXML). The component itself is also just over 4,000 lines (I know - we should probably refactor it), so all in all that's not a small task.

      However, I've been looking at your ListGrid.java in conjunction with ISC_Grid.js and I think I see some of the patterns I need to adopt to get what we need right now. If nothing else it will be a great learning exercise.

      Thanks again for your guidance.

      Comment


        #4
        Well, it may not be my place to disagree - but I do.

        Where you say "Bear in mind also that while your declaration of "parentKeys" in Component XML worked to assign the property to the class, your setter wasn't called", I have found that not to be so. If I put a breakpoint in my setter, I see that it is actually really is called when my object is created. The call originates from isc.ClassFactory.newInstance("com.islandpacific.gui.client.base.IpListGridEditor", ...) which is returned when I call RPCManager.screenLoader(). From there, create() calls this.sgwtModule.newInstance() which goes through the BeanFactory to ultimately call BeanMethod.setPropertyOnBean().

        Now, I'd noticed this yesterday and even got closer this morning when I found getSGWTAttributes(), but it wasn't until half an hour ago that I stumbled upon "a few convenience methods on SmartClient instances so that they can reflect a little on their SmartGWT equivalents", including the very useful getSGWTProperty() and of course setSGWTProperties().

        A quick test and I can confirm that I can (albeit unsafely, since it's undocumented) trigger the getter and setter methods of all my component's properties exposed by the BeanFactory. There's no need to add any JSNI methods. This is ideal (or as ideal as I'm going to get for now, anyway)! [This is where you tell me: "Don't do that!- it's not documented and is neither safe nor future-proof..."]

        For reference, here is my modified recordClick:

        Code:
        <recordClick><![CDATA[ this.setSGWTProperties({parentKeys: {packingListId:record.id}}); ]]></recordClick>
        ALTERNATIVE #1
        An alternative that I tried and which also worked was to modify my getter and setter methods to set and retrieve properties on the object. These properties are readily exposed and modified in Javascript. I'm not invoking the getter and setter methods this way, but I can certainly access and modify the property value (this seems to be closer to what you were suggesting was happening in your response).

        ALTERNATIVE #2 (with question)
        The approach I spent much of my day on is what is most likely considered the "right"(est) approach - exposing the getter and setter methods through JSNI. And this is where I notice some behavior which is intuitive but feels like it sits at odds with the general guidance on how to expose Java methods.

        I'm sure you're familiar with the pattern for exposing instance methods described at http://www.gwtproject.org/doc/latest...I.html#sharing. I'll start by saying it works - and it works as I would expect it to work. But it doesn't work HOW I want it to work and this might be where you can tell me I've overlooked something or done something wrong.

        Upon each instantiation of the IpListGridEditor component, the constructor calls my method that exports the native call to the setter method. During that export, a function is created in global scope called setParentKeys(). This function is associated with the current instance of the custom IpListGridEditor component.

        However, as you might imagine my custom component is used in multiple places throughout our application. Whichever one is instantiated last is the one associated with the setParentKeys() method. That means that when I call setParentKeys(), it only ever affects the IpListGridEditor that most recently exported the setParentKeys() method.

        I tried to get a reference to this within the JSNI method, but I stalled and ended up stumbling on the first approach (using setSGWTProperties()) which I will use for now. But for my future reference (because there are methods other than setters and getters that I'd like to call on specific instances of IpListGridEditor), I wonder if you can tell me what I'm overlooking here. Should I continue trying to pass a reference to the component I wish to affect (in which case, why wouldn't I just make it static...?), or should I be creating a collection of setParentKey() functions and invoke the appropriate one...?

        I know that's more a question of GWT than SmartGWT, but if you could again point me the right way I'll be happy enough to continue to research further.

        Here's my current export method:

        Code:
            /**
             * Setup a globally accessible method (called setParentKeys) that can be called from Javascript
             * (e.g. from ComponentXML, such as on a recordClick event).  
             * See http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html#sharing.
             */
            public native void exportSetParentKeys() /*-{
                var that = this;
                $wnd.setParentKeys = $entry(function(myself, parentKeys) {
                   that.@com.islandpacific.gui.client.base.IpListGridEditor::setParentKeys(Ljava/util/LinkedHashMap;)(parentKeys);
                });
            }-*/;
        Thanks for listening.
        Last edited by godonnell_ip; 22 Feb 2018, 16:39.

        Comment

        Working...
        X