A Periodic Zone Refresh Mixin for Tapestry

Tapestry uses a concept of Zones for Ajax based updates. Zones are components which are used to identify/mark a portion of a component/page for Ajax updates. Usually zones are connected to client events of other components e.g. click of EventLink or ActionLink component, change event of Select component or submit event of Form component. But what if we just want a zone which refreshes itself with its own contents.This is what we are going to do.

The JavaScript part is very simple. We just have to use PeriodicalExecuter to periodically update the zone. There is just one catch. As the zone gets updated each time, the script that we call will be called each time. That also means, if we instantiate a PeriodicalExecutor in the script, the same will be done each time the zone is loaded. This will cause recursive instantiation of timers. In order to avoid that we need to keep track of the timers.

if (!Tapestry.ZoneRefresh)
{
   Tapestry.ZoneRefresh = {};
}

Tapestry.Initializer.zoneRefresh = function(params)
{
   //  Ensure a valid period. Not required as PeriodicalUpdater already takes care of it
   // but will will skip unnecessary steps
   if(params.period <= 0)
   {
      return;   
   }
         
   // If the timer is already present, don't create a new one
   if (Tapestry.ZoneRefresh[params.id])
   {
      // Timer already in use
      return;
   }

   // Set zone
   var element = $(params.id);
   $T(element).zoneId = params.id;

   // Function to be called for each refresh
   var keepUpdatingZone = function(e)
   {
      try 
      {
         var zoneManager = Tapestry.findZoneManager(element);
         zoneManager.updateFromURL(params.URL);
      }
      catch(e)
      {
         e.stop();
         Tapestry.error(Tapestry.Messages.invocationException, {
            fname : "Tapestry.Initializer.zoneRefresh",
            params : params,
            exception : e
            });
      }
   };

   // Create and store the executor
   Tapestry.ZoneRefresh[params.id] = new PeriodicalExecuter(keepUpdatingZone, params.period);
};

// Before unload clear all the timers
Event.observe(window, "beforeunload", function()
{
   if (Tapestry.ZoneRefresh)
   {
      for ( var propertyName in Tapestry.ZoneRefresh)
      {
         var property = Tapestry.ZoneRefresh[propertyName];
         property.stop();
      }
   }
});

We have used a Tapestry.ZoneRefresh variable to store the timers for each zone. Before instantiating a new timer, we check with this variable if the timer has already been instantiated. The function takes a json-object which has three parameters, the zone id, time period after which the zone will be refreshed and the URL that should be used to refresh the zone.

Now the Tapestry’s job is to call the script with the proper parameters and then handle the event which is called on each refresh.

@Import(library = "zone-refresh.js")
public class ZoneRefresh
{
   /**
    *  Period is seconds 
    */
   @Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
   private int period;
   
   /**
    * Context passed to the event
    */
   @Parameter
   private Object[] context;
   
   @InjectContainer
   private Zone zone;

   @Inject
   private JavaScriptSupport javaScriptSupport;
   
   @Inject
   private ComponentResources resources;
   
   public ZoneRefresh()
   {
   }
   
   //For testing purpose
   ZoneRefresh(Object [] context, ComponentResources resources, 
      JavaScriptSupport javaScriptSupport, Zone zone)
   {
      this.context = context;
      this.resources = resources;
      this.javaScriptSupport = javaScriptSupport;
      this.zone = zone;
   }

   @AfterRender
   void addJavaScript()
   {
      JSONObject params = new JSONObject();
      
      params.put("period", period);
      params.put("id", zone.getClientId());
      params.put("URL", createEventLink());
      
      javaScriptSupport.addInitializerCall(InitializationPriority.LATE, 
       "zoneRefresh", params);
   }

   private Object createEventLink()
   {
      Link link = resources.createEventLink("zoneRefresh", context);
      return link.toAbsoluteURI();
   }
   
   Object onZoneRefresh()
   {
      CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
      resources.triggerEvent(TawusAddonsEventConstants.REFRESH, context, callback);
      
      if(callback.getResult() != null){
         return callback.getResult();
      }
      
      return zone;
   }

}

The mixin assumes that it has been added to a zone. It passes the zone id along with the zone refresh period and an event link to the script. The event handler triggers a “refresh” event. If the event handler in the container returns a value, then that value is returned otherwise the zone is returned by default.

Usage

<div t:type='zone' t:mixins='tawus/zonerefresh' t:period='10'>Anything inside <div>
About these ads

Tagged: , ,

5 thoughts on “A Periodic Zone Refresh Mixin for Tapestry

  1. crgen November 4, 2011 at 9:29 PM Reply

    Thanks for this, it seems to work nicely. I take it TawusAddonsEventConstants.REFRESH is name of the event that gets called on the page containing the zone: onRefresh()

    If I would like to stop refreshing if some condition is met on the server side in onRefresh() method, how should one modify the code to accomplish this?

    • tawus November 4, 2011 at 9:38 PM Reply

      This mixin is already part of tapestry-core. So you don’t have to implement it. Regarding stoping the refreshing, please file a JIRA at https://issues.apache.org/jira/browse/TAP5 and I will look into that as soon as possible.

  2. sommeralex September 6, 2012 at 1:04 AM Reply

    please show the example which is as you say already part of the core and working with tapestry

  3. Bogdan July 17, 2013 at 7:00 AM Reply

    Hi Taha,
    Looks like the wonderful ZoneRefresh component has an issue with the context parameter being read again on refresh event. This will render the initial set value useless. Was wondering if there a plan to correct this issue?

    Cheers.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 91 other followers

%d bloggers like this: