Friday, May 24, 2013

ZK Tips

Figured I'd list some of the tips here as I work with ZK. A lot of things aren't immediately apparent even using the online docs.

NotifyChange from your Base ViewModels


In annoying thing in ZK is that if your ViewModel extends another ViewModel and your base ViewModel decides that it needs to delegate a call to its subclass, the subclass @NotifyChange({"props}") will not automatically be triggered. As an example take:
public abstract class BaseVM {

 public void doSomething() {
  //do Stuff
  //delegate to implementing class
  doSomeStuff();
 }
 public abstract void doSomeStuff();
}

public ChildVM extends BaseVM {
 private String someProperty;

 @NotifyChange("someProperty")
 public void doSomeStuff() {
  someProperty = "blah";
 }

 public String getSomeProperty() {
  return someProperty;
    }
}
It would be nice if when "doSomeStuff" is called that "someProperty" would be able to be notified so it could display properly in your zul. Matze2 on the forums helped provide the work around http://forum.zkoss.org/question/86637/notifychange-in-subclass-methods-called-from-parent-will-not-fire/ Solution is to use the "Binder" object. Illustrated as so...
public abstract class BaseVM {
 
 protected Binder binder;

 @Init
 public final void init(@ContextParam(ContextType.BINDER) Binder _binder) {
  binder = _binder;
 }

 public void doSomething() {
  //do Stuff
  //delegate to implementing class
  doSomeStuff();
  binder.notifyChange(this, "someProperty");
 }
 public abstract void doSomeStuff();
}

public ChildVM extends BaseVM {
 private String someProperty;

 @Init(superclass=true)
 public void init() {
 }

 @NotifyChange("someProperty")
 public void doSomeStuff() {
  someProperty = "blah";
 }

 public String getSomeProperty() {
  return someProperty;
    }
}
Don't forget the @Init(superclass=true) in your subclass (I think you can add it to the Class annotation if you aren't providing an init method in your subclass.) The above is clunky but it works.

Use Constants for your GlobalCommands


Most of the documentation and examples demonstrate declaring GlobalCommmand annotations like:
@GlobalCommand
public void someMethod() { ... }
and calling it from some other ViewModel like so:
BindUtils.postGlobalCommand(null, null, "someMethod", null);
The problem with this is it's extremely fragile. If you decide to refactor your method name, you end up then having to do a search and replace for everywhere you've called your Global command with "someMethod." Possibly more annoying is you do not get any IDE help when you want to post to this GlobalCommand from another ViewModel... you need to recall the exact name of the method and if you can't recall the name you have to remember where that GlobalCommand was defined which could take time in a large project. Instead, I recommend always getting in the habit of using a GlobalCommandValues class that declares the GlobalCommand as String constant and then using it in your annotation and of course in your postGlobalCommand calls. This seems obvious, but I wasn't even aware it was an option.
//GlobalCommandValues class
public static final String SOME_METHOD = "someMethod";

//ViewModel
@GlobalCommand(GlobalCommandValues.SOME_METHOD)
public void someMethodDoesntHaveToMatchYourConstant() { ... }

//Usage
BindUtils.postGlobalCommand(null, null, GlobalCommandValues.SOME_METHOD, null);

No comments :

Post a Comment

Blogger Syntax Highliter