Announcement

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

    Asynchronous requests with DataSource

    Hi

    I'm having quite some trouble getting a task done and unfortunately i couldn't find any solution by searching the forum or the web.

    So here's my problem:


    I'm using a listgrid to display my data and a datasource to retrieve them.
    my listgrid displays the following fields:

    type | published | name | directory


    My XML i get as a response from the server looks the following:
    Code:
    <feed 
        xmlns="http://www.w3.org/2005/Atom">
        <title type="text">Server log feed</title>
        <id>someId</id>
        <updated>timestamp</updated>
        <author>
            <name>author</name>
            <uri>uri</uri>
        </author>
        <link rel="self" type="application/atom+xml;type=feed" href="http://127.0.0.1/status.feed"/>
        <entry>
            <id>ID</id>
            <title type="text">SERVER_ID.log</title>
            <published>timestamp</published>
            <updated>timestamp</updated>
            <link rel="self" type="someType" href="someLink"/>
    	<link rel="model" type="someType" title="title" href="someLink"/>
            <category term="FINISHSTATE_SUCCESS"/>
            <content type="someType" src="http://127.0.0.1/status/ID.payload"/>
        </entry>
        <entry>
             another entry...
        </entry>
    </feed>
    the datasource fields look like this:
    Code:
    DataSourceTextField type = new DataSourceTextField("type", "Type");
    recentType.setValueXPath("./atom:link[@rel=\"model\"]/@title");
    
    DataSourceDateTimeField published = new DataSourceDateTimeField("published", "Published");
    recentDate.setValueXPath("./atom:published");
    
    DataSourceTextField name = new DataSourceTextField("name", "Name");
    
    DataSourceTextField directory = new DataSourceTextField("directory", "Directory");

    so here's the point where it gets complicated:

    the values for the fields 'name' and 'published' are not contained in the feed.
    to get them, i have to load the payload from the URL specified in in the 'src' attribute of the <content> tag.

    what I've done so far is:
    Code:
    final DataSource dataSource = new DataSource(UrlBuilder.getUrl("status.feed")) {
    		
    	@Override
    	protected void transformResponse(DSResponse response, DSRequest request, Object data) {
    			
    		for (final Record record : response.getData()) {
    			String contentString = record.getAttribute("content");
    			if (contentString != null && !contentString.isEmpty()) {
    				JavaScriptObject jsObject = JSOHelper.getAttributeAsJavaScriptObject(record.getJsObj(), "content");
    				String url = JSOHelper.getAttribute(jsObject, "src");
    				
    				new HttpConnection().getXml(url, new XmlValueHandler() {
    
    					@Override
    					public void onXmlValue(String path, String value) {
    						Payload payload = new Payload();
    						payload.fromXml(value);
    						record.setAttribute("name", payload.get("name"));
    						record.setAttribute("directory", payload.get("directory"));
    					}
    
    					@Override
    					public void onFailure(Response response) {							
    					}
    
    					@Override
    					public void onError(Throwable exception) {							
    					}
    				});
    
    			}
    		}
    	}
    };
    dataSource.setDataFormat(DSDataFormat.XML);
    dataSource.setXmlNamespaces(...); //set atom namespace
    dataSource.setRecordXPath("/atom:feed/atom:entry");
    This results in the 'name' and 'directory' field of my listgrid beeing empty after the first fetch,
    because the response of the asynchronous requests arrive after the GUI has been drawn.

    The XML I get from the server is standardised and fixed. I do not have the option to change anything there;

    Versions are GWT 2.6.1 and SmartGWT 4.1.

    So does anyone have an idea on how to resolve this issue best?

    Thanks in advance!

    #2
    The best user experience may indeed be to show partial data and have a momentary delay while the remaining data is retrieved. You could even show placeholder values in the grid (like a greyed-out "Loading..." string).

    However if you want to avoid this, use dataProtocol:"clientCustom". That way you can make as many requests as you need to and only call processResponse() when you have all the data.

    Comment


      #3
      Thanks for the response.
      I was not aware of the "clientCustom" option and may give it a try.
      But as you said, the best experience would be the display the fields, as the data arrives, especially if I need to show lots of entries.

      But how do i tell the listgrid to update a specific field of an entry, when i recieve the data for it?
      That's the point where I'm stuck with my attempt I posted above. If my asynchronous requests arrive to late, the corresponding fields in my grid just remain empty.

      Comment


        #4
        See DataSource.updateCaches. Or, consider directly modifying the ListGridRecords and calling ListGrid.markForRedraw().

        Comment


          #5
          I could get it working using 'updateCaches' with operation type set to 'DSOperationType.UPDATE' . The data is now displayed right after I get the response for my asynchronous request.

          There is still one problem though:
          I have a similiar ListGrid with asynchronous requests, but this time displayed in a SelectItem.
          Whenever i call updateCaches the SelectItem collapses and you have to click the picker icon again to reopen it. This is quite annoying.

          I found that if i call selectItem.fetchData() instead of updateCaches the displayed data gets updated, but the SelectItem does not collapse.

          I do not see any additional requests beeing made or any other unwanted behaviour.

          Is this a legit way of solving the problem?


          Big thanks for the support and for pointing me in the right direction.

          Comment


            #6
            Calling fetchData() would be fine, but you should find that the most recent patched version of 4.1 does not auto-close the pickList when updateCaches() is called.

            Comment


              #7
              Thanks, we moved to 5.0 and it's working fine using DataSource.updateCaches now.

              Maybe someone has the same or a similar problem and can find this useful.
              So here's my final solution:

              Code:
              final DataSource dataSource = new DataSource(UrlBuilder.getUrl("status.feed")) {
              		
              	@Override
              	protected void transformResponse(DSResponse response, DSRequest request, Object data) {
              			
              		for (final Record record : response.getData()) {
              			String contentString = record.getAttribute("content");
              			if (contentString != null && !contentString.isEmpty()) {
              				JavaScriptObject jsObject = JSOHelper.getAttributeAsJavaScriptObject(record.getJsObj(), "content");
              				String url = JSOHelper.getAttribute(jsObject, "src");
              				
              				new HttpConnection().getXml(url, new XmlValueHandler() {
              
              					@Override
              					public void onXmlValue(String path, String value) {
              						Payload payload = new Payload();
              						payload.fromXml(value);
              						record.setAttribute("name", payload.get("name"));
              						record.setAttribute("directory", payload.get("directory"));
              						update(record);
              					}
              
              					@Override
              					public void onFailure(Response response) {							
              					}
              
              					@Override
              					public void onError(Throwable exception) {							
              					}
              				});
              
              			}
              		}
              	}
              
              	private void update(Record record) {
              		DSResponse dsResponse = new DSResponse();
              		dsResponse.setData(this.copyRecord(record));
              		dsResponse.setOperationType(DSOperationType.UPDATE);
              		this.updateCaches(dsResponse);
              	}
              };
              dataSource.setDataFormat(DSDataFormat.XML);
              dataSource.setXmlNamespaces(...); //set atom namespace
              dataSource.setRecordXPath("/atom:feed/atom:entry");

              Comment

              Working...
              X