1

I'm using palette components on a page and I want the available elements in two of them to change depending on what is selected in the first.

What is the best way to achieve this? Which events are thrown by the palette component, that I could listen to, adapt the palette's model and perform a zone update? I thought it would work the same way as for select components doing something like this:

void onValueChanged() {
    // do something
}

Unfortunately that doesn't work for palettes.

I'm using Tapestry 5.4-beta-6, but I guess things haven't changed that much since earlier versions.

martin
  • 2,150
  • 1
  • 34
  • 48
  • 1
    Just a few notes; 5.4-beta-17 is now available (as a preview, a vote is forthcoming) and I can't honestly say what's changed there since beta-6. There's a lot of power on the client side here, with the Palette component producing willChange and didChange events (including veto power to the listener). – Howard M. Lewis Ship Sep 02 '14 at 19:02
  • Thanks, I didn't know that. Sounds very promising. I will check it out. – martin Sep 03 '14 at 09:05
  • How can I catch these event? I tried the following, but none of them gets called: @OnEvent(value="willChange", component="myPalette"), @OnEvent(value="t5:palette:willChange", component="myPalette"). Am I missing something? – martin Sep 08 '14 at 09:52
  • 1
    No... the willChange and didChange are clientside events. @OnEvent is for serverside events. – lance-java Sep 12 '14 at 12:54

2 Answers2

0

I'd probably do this with a mixin.

public class PaletteChange {
   @Parameter
   private String zone;

   @InjectContainer
   private Palette palette;

   public void afterRender() {
      Link eventLink = componentResources.createEventLink("change");
      JSONObject args = new JSONOBject(
          "id", pallete.getClientId(), 
          "url", eventLink, 
          "zone", zone
      );
      javascriptSupport.addScript("palleteChange(%s)", args);
   }

   Object onChange(@RequestParameter("value") String value) {
      CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
      resources.triggerEvent("change", new String[] { value }, callback);
      return callback.getResult();
   }
}

Javascript

function palleteChange(spec) {
   var field = $('#' + spec.id + '/select[1]');
   field.on('change', function() {
      var zoneManager = Tapestry.findZoneManagerForZone(spec.zone);
      var params = { value: field.val() };
      zoneManager.updateFromURL(spec.url, params);
   });
}

Then use the mixin in your code

<t:palette t:id="myPalette" t:mixins="paletteChange" zone="myZone" ... />
<t:zone t:id="myZone">
    ...
</t:zone>

Page

@Inject
private Zone myZone;

Block onChangeFromMyPalette(String value) {
   doStuff(value);
   return myZone.getBody();
}

See here for a similar mixin.

lance-java
  • 25,497
  • 4
  • 59
  • 101
0

I finally used the didChange element together with a similar mixin like the Observe mixin. I put a demo on Github for anyone, who is interested.

Just a few notes:

  • I used 5.4 beta 6, it already has the necessary client side events.
  • I couldn't make it work using a Tapestry javascript module, so I still use javascriptSupport.addInitializerCall.
  • The remaining problem is, that updating the second palette with a zone update will reset any changes the user has made in this palette, since they are only kept on the client side (in a hidden field). I will still need to look into that, but it is not part of the original question.
martin
  • 2,150
  • 1
  • 34
  • 48
  • Your Index page is initializing variables in their declaration which your should never do (eg `private List availableSubCategories = new ArrayList()`). This will cause variables to bleed from one request to another. Use a render event instead to initialize (eg setupRender() or onPrepare()). – lance-java Sep 23 '14 at 16:14