Announcement

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

    Dynamic changing of layout contents + TileLayout usage

    We are currently evaluating SmartGWT as a platform for developing an internal parts catalog. While playing with the product I have the following problem:

    I have a VLayout with a label and a TileLayout. The tile layout contains parts icons and descriptions. By clicking on a tile, the user drills down to the next level of the catalog so the label and the tiles must completely change with the new data.

    My first idea was to create a TileLayout in the constructor and then on a page update to clear() it and populate it with new tiles. However I have found out that a call to
    clear() doesn’t remove the old tiles from a TileLayout. I have then tried to remove the already existing tiles by calling removeTile() but this method has always removed false (both with a tile index and a tile object as an argument). By the way, wouldn’t it be convenient to have removeTiles() and getTiles() methods?

    Finally, I have discovered that a call to the clear() method of the parent VLayout itself doesn’t seem to remove layout members and on subsequent calls I have ended up with many labels and tile layouts in the same VLayout.

    The only way I have managed to get working so far is to destoy() all VLayout members on every call and then re-draw the whole thing. However I suspect that this brutal solution is very inefficient and probably wrong.

    Here’s a code snippet:

    public class CatalogWidget extends VLayout
    {
    public CatalogWidget()
    {
    setWidth100();
    setHeight100();
    }

    ...

    private void loadSubnodes(long nodeId)
    {
    EdhCatService.Util.getInstance().getSubnodes(lang, nodeId, null, new AsyncCallback<NodeInfo[]>()
    {
    public void onSuccess(NodeInfo[] result)
    {
    TileLayout subnodesPanel = new TileLayout();

    // clear(); // doesn’t clear the members
    // subnodePanel.clear(); // doesn’t clear the tiles

    // subnodePanel.removeTiles(); // doesn’t exist

    // always returns false, even if tiles are there
    // while (subnodePanel.removeTile(0));

    clearLayout();

    subnodesPanel.setWidth100();
    subnodesPanel.setHeight100();
    subnodesPanel.setTileWidth(120);
    subnodesPanel.setTileHeight(150);
    subnodesPanel.setTileHMargin(10);
    subnodesPanel.setTileVMargin(20);
    subnodesPanel.setOverflow(Overflow.VISIBLE);

    for (int i=0; (result != null) && (i < result.length); i++)
    {
    if ((i == 0) && (result[i].getParentId() > EdhCat.MAX_ROOT_NODE))
    addMember(new Label(result[i].getParentName()));
    subnodesPanel.addTile(new CategoryWidget(result[i]));
    }

    addMember(subnodesPanel);
    draw();
    }

    public void onFailure(Throwable caught)
    {
    // TODO
    EdhCatErrorHandler.handleError(caught);
    }
    });

    }

    private void clearLayout()
    {
    Canvas[] members = getMembers();

    for (int i = 0; (members != null) && (i <
    members.length); i++)
    members[i].destroy();
    }

    ...

    }

    Therefore here are three questions to everybody who is experienced with SmartGwt:

    1. What is the most correct way to clear a layout from members and populate it with new members?

    2. How to repopulate a TileLayout with a new set of tiles?

    3. Suppose we have a layout whose different members are visible at different times (e.g. tiles view, table view, detailed view and possibly a collapsible filter panel visible only on certain conditions...). What would be a correct way to show/hide the members? Would a call to hide()/show() be enough or it is necessary to do other steps as well (e.g. draw() or reflow() )?

    Thanks in advance for answering!

    #2
    clear() is totally unrelated to what you're doing (see the docs for that method).

    To remove tiles from a TileLayout, call removeTitle(). However, it seems that what you are basically doing is connecting a tiled view to a dataset, and for that, TileGrid is a better choice. TileGrid connects to a DataSource and automatically manages creation of tiles based on data.

    show()/hide() is all you need to show and hide.

    Comment


      #3
      How to connect TileGrid to an GWT-RPC data source?

      Thank you for the tip, TileGrid indeed looks like a better choice for what we are doing. However I have trouble connecting TileGrid to a GWT-RPC data source.

      I have created my data source using the GWT-RPC data source base class from the sticky thread (see code below).

      The data source works fine with a list grid however when I try to connect it to the TileGrid I get the following exception:

      ERROR] Uncaught exception escaped
      java.lang.ClassCastException: com.smartgwt.client.widgets.grid.ListGridRecord
      at com.smartgwt.client.widgets.tile.TileRecord.getOrCreateRef(TileRecord.java:69)
      ...

      The problem is that the setData() method of the data source wouldn't accept an array of TileRecords, so I seem to be stuck again. Any tips please?

      Code:
      package cern.edh.edhcat.client;
      
      import cern.edh.edhcat.client.utils.EdhCatService;
      import cern.edh.edhcat.client.utils.NodeInfo;
      
      import com.google.gwt.user.client.rpc.AsyncCallback;
      import com.smartgwt.client.data.*;
      import com.smartgwt.client.data.fields.*;
      import com.smartgwt.client.rpc.RPCResponse;
      import com.smartgwt.client.widgets.grid.ListGridRecord;
      
      public class CatalogDataSource extends GwtRpcDataSource
      {
      	private ListGridRecord[] records = null;
      	
      	private String lang	= EdhCatService.Util.getLang();
      	
      	public CatalogDataSource()
      	{
      		super();
          DataSourceField field;
          field = new DataSourceIntegerField ("id", "ID");
          field.setPrimaryKey (true);
          field.setRequired (false);
          addField (field);
          field = new DataSourceTextField ("name", "Name");
          field.setRequired (false);
          addField (field);
          field = new DataSourceIntegerField ("imageId", "Image ID");
          field.setRequired (false);
          addField (field);
        }
      	
      	@Override
      	protected void executeAdd(String requestId, DSRequest request, DSResponse response)
      	{
      		System.out.println("executeAdd");
      	}
      
      
      
      	@Override
      	protected void executeFetch(String requestId, DSRequest request, DSResponse response)
      	{
      		System.out.println("executeFetch");
      		retrieve(requestId, request, response);
      	}
      
      
      
      	@Override
      	protected void executeRemove(String requestId, DSRequest request, DSResponse response)
      	{
      		System.out.println("executeRemove");
      	}
      
      
      
      	@Override
      	protected void executeUpdate(String requestId, DSRequest request, DSResponse response)
      	{
      		System.out.println("executeUpdate");
      	}
      
      	private void retrieve(final String requestId, final DSRequest request, final DSResponse response)
      	{
      		EdhCatService.Util.getInstance().getSubnodes(lang, 6, null, new AsyncCallback<NodeInfo[]>()
      				{
      					public void onSuccess(NodeInfo[] result)
      					{
      						populateData(result);
                  response.setData (records);
                  processResponse (requestId, response);
      					}
      
      					public void onFailure(Throwable caught)
      					{
                  response.setStatus (RPCResponse.STATUS_FAILURE);
                  processResponse (requestId, response);
      					}
      				});
      	}
      	
      	private void populateData(NodeInfo[] result)
      	{
      		try
      		{
      			records = new ListGridRecord[(result != null) ? result.length : 0];
      			
      			for (int i = 0; i < records.length; i++)
      			{
      				records[i] = new ListGridRecord();
      				records[i].setAttribute("id", result[i].getId());
      				records[i].setAttribute("name", (result[i].getName() != null) ? result[i].getName() : "");
      				records[i].setAttribute("imageId", result[i].getImageId());
      			}
      			
      			System.out.println("data retrieved");
      		}
      		catch (Exception e)
      		{
      			e.printStackTrace();
      		}
      	}
      
      }

      Comment


        #4
        Go ahead and use ListGridRecord. In reality both wrap the same underlying object type. Any TileGridRecord property you want to set can be set on a ListGridRecord with setAttribute().

        Comment


          #5
          I'm sorry but i didn't get it. I'm facing the same problem, DSResponse.setData() accepts only ListGridRecords however the TileGrid fails on a cast exception when we pass ListGridRecords (is expectinf TileGridRecords).
          Has anyone figured a workaround, please?

          Comment


            #6
            If you provide ListGridRecords at the DataSource level, this will just work for the TileGrid if you setDataSource() and fetchData(). In that use case there is no place where you would have to call a TileGrid API with a ListGridRecord because the communication is all going on automatically between the TileGrid and DataSource, which will not generate ClassCastExceptions.

            If you're seeing something different, please post code.

            Comment


              #7
              Here's the code. Data is returned ok and i pass ListGridRecords:

              TileGrid tilesDoc = new TileGrid();
              tilesDoc.setAutoFetchData(true);
              tilesDoc.setDataSource(assetsData);
              ....
              assetsData source:

              public class AssetsDataSource extends GwtRpcDataSource {

              protected void executeFetch(final String requestId, final DSRequest request, final DSResponse response) {
              StatusServiceAsync service = StatusService.StatusServiceFactory.getInstance();
              AsyncCallback callBck = new AsyncCallback<List<AssetInfo>>() {
              public void onFailure(Throwable t) {
              response.setStatus(RPCResponse.STATUS_FAILURE);
              processResponse(requestId, response);
              }
              public void onSuccess(List<AssetInfo> result) {
              response.setData(toListGridRecords(result));
              processResponse(requestId, response);
              }
              };
              service.getAssets(anId,assetCategory,callBck);
              }
              .....
              private static ListGridRecord[] toListGridRecords(List<AssetInfo> inLst) {
              ListGridRecord[] outLst = new ListGridRecord[inLst.size()];
              ListGridRecord aRec;
              AssetInfo aDoc;
              for (int i = 0; i < outLst.length; i++) {
              aDoc=inLst.get(i);
              aRec = new ListGridRecord();
              aRec.setAttribute("id", aDoc.getId());
              aRec.setAttribute("name", aDoc.getName());
              aRec.setAttribute("icon", aDoc.getIcon());
              outLst[i] = aRec;
              }
              return outLst;
              }
              }

              Comment


                #8
                So are you saying that with this code you see a ClassCastException not directly caused by your code? Can you show the full stack trace?

                Comment


                  #9
                  Correct, here's the stack:
                  [ERROR] Uncaught exception escaped
                  java.lang.ClassCastException: com.smartgwt.client.widgets.grid.ListGridRecord cannot be cast to com.smartgwt.client.widgets.tile.TileRecord
                  at com.smartgwt.client.widgets.tile.TileRecord.getOrCreateRef(TileRecord.java:69)
                  at com.smartgwt.client.data.DataSource.processResponse(Native Method)
                  at mm.ui.client.AssetsDataSource$1.onSuccess(AssetsDataSource.java:51)
                  at mm.ui.client.AssetsDataSource$1.onSuccess(AssetsDataSource.java:1)
                  at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:215)
                  at com.google.gwt.http.client.Request.fireOnResponseReceivedImpl(Request.java:254)
                  at com.google.gwt.http.client.Request.fireOnResponseReceivedAndCatch(Request.java:226)
                  at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:217)

                  Comment


                    #10
                    This trace doesn't look possible unless you've overridden processResponse.

                    Comment


                      #11
                      No, did not touch processResponse.
                      The DS just extends GwtRpcDataSource. Here's again the code(thx again to the author):

                      public GwtRpcDataSource () {
                      setDataProtocol(DSProtocol.CLIENTCUSTOM);
                      setDataFormat(DSDataFormat.CUSTOM);
                      setClientOnly (false);
                      //setClientOnly (true);
                      }

                      /**
                      * Executes request to server.
                      *
                      * @param request <code>DSRequest</code> being processed.
                      * @return <code>Object</code> data from original request.
                      */
                      @Override
                      protected Object transformRequest (DSRequest request) {
                      String requestId = request.getRequestId ();
                      DSResponse response = new DSResponse ();
                      response.setAttribute ("clientContext", request.getAttributeAsObject ("clientContext"));
                      response.setStatus (0); // asume success !!
                      switch (request.getOperationType ()) {
                      case FETCH:
                      executeFetch (requestId, request, response);
                      break;
                      case ADD:
                      executeAdd (requestId, request, response);
                      break;
                      case UPDATE:
                      executeUpdate (requestId, request, response);
                      break;
                      case REMOVE:
                      executeRemove (requestId, request, response);
                      break;
                      default:
                      break;
                      }
                      return request.getData ();
                      }

                      Comment


                        #12
                        It seems as though something is not right about your report. There is no code related to the TileGrid or TileRecord anywhere in SmartGWT's DataSource layer or the GwtRpcDataSource. Your stack trace suggests that simply calling DataSource.fetchData(), with no TileGrid involved, would lead to this exception as well - why don't you try that, and if you get a similar error, then you know you've got some TileGrid/TileRecord code still laying around that needs to be removed.

                        If that doesn't solve it, please post a standalone test case, because at this point your sample code is just like the databound TileGrid example.

                        Comment


                          #13
                          Hmm, i just used the same DS to feed a ListGrid and got no exception.
                          The stack shows the err in TileRecord.getOrCreateRef(TileRecord.java:69).
                          java.lang.ClassCastException: com.smartgwt.client.widgets.grid.ListGridRecord cannot be cast to com.smartgwt.client.widgets.tile.TileRecord
                          at com.smartgwt.client.widgets.tile.TileRecord.getOrCreateRef(TileRecord.java:69)

                          I'll try to look again.

                          Comment


                            #14
                            Just checked the trunk source, here's the piece that throws the err:

                            public static TileRecord getOrCreateRef(JavaScriptObject jsObj) {
                            if(jsObj == null) return null;
                            RefDataClass obj = RefDataClass.getRef(jsObj);
                            if(obj != null) {
                            obj.setJsObj(jsObj);
                            return (TileRecord) obj; // cast exception here !!
                            } else {
                            return new TileRecord(jsObj);
                            }
                            }

                            Comment


                              #15
                              Yes, the line number of the error is clear, the question is, how could this method actually be called from processResponse? It seems you have to override it.

                              Again, if TileGrids didn't work with DataSources in general, this sample would not work. Somehow your DataSource is introducing the TileRecord class into the call chain.

                              Comment

                              Working...
                              X