package com.asayama.gwt.angular.client; import java.util.logging.Level; import java.util.logging.Logger; import com.asayama.gwt.jsni.client.Closure; import com.asayama.gwt.jsni.client.JSClosure; import com.google.gwt.core.client.GWT; /** * Provides an interface for AngularJS's controller objects. While AngularJS * does not require the users to declare their controllers to be controllers, * GWT Angular clarifies the roles of the controllers by requiring that the * controller implementations to support at least one method, * {@link #onControllerLoad()}. * <p> * User defined controllers supporting this interface must be registered with * the user's module in order for it to be visible to the framework. For * example, * </p> * <pre> * public class MyModule extends AbstractModule implements EntryPoint { * public void onModuleLoad() { * Angular.module(this); * controller(MyController.class); * } * } * * class MyController implements Controller { * String name; * public void onControllerLoad() { * name = "World!"; * } * public String getName() { * return name; * } * } * </pre> * * @author kyoken74 */ public interface Controller { /** * This method is called when the user supplied controller is loaded by the * framework. The method is similar to the JavaScript constructor for * controllers, which is called by the AngularJS framework, though the * binding of properties and functions to the {@code $scope} is handled by * GWT Angular, rather than requiring the user to write the binding by hand. * <p> * This reduces the need for custom implementation of * {@link #onControllerLoad()}, and hence an no-op implementation of this * method is provided by {@link AbstractController}. * </p> */ void onControllerLoad(); } /** * Provides a bridge between GWT and AngularJS during module initialization. * <p> * During module initialization, controllers are registered to the module, * presenting the constructor of the controller. AngularJS expects the * constructor to be provided as a JavaScript function, but during that * construction time, we want to perform some initialization of the object in * the GWT layer. This class enables us to perform the GWT tasks to be defined * as part of that constructor. * </p><p> * DefaultControllerContructor assumes that the first item in the dependency * is always "$scope", which was supplied by {@link AbstractModule}, and it * automatically binds the controller's properties and functions to the scope * according to the logic provided by {@link ControllerScopeBinderFactory}. * </p> * * @author kyoken74 * @param <C> Concrete implementation type of the Controller to be constructed. */ class DefaultControllerConstructor<C extends Controller> extends Closure { private static final String CLASS = DefaultControllerConstructor.class.getName(); private static final Logger LOG = Logger.getLogger(CLASS); protected final String name; protected final Class<C> klass; DefaultControllerConstructor(String name, Class<C> klass) { this.name = name; this.klass = klass; } @Override public void exec(Object... args) { String m = "entering"; try { m = "creating controller " + name; ControllerCreator creator = GWT.create(ControllerCreator.class); Controller controller = creator.create(klass); m = "creating scopeBinder for " + name; ControllerScopeBinderFactory scopeBinderFactory = GWT.create(ControllerScopeBinderFactory.class); JSClosure scopeBinder = scopeBinderFactory.create(controller); m = "creating binder for " + name; ControllerBinderFactory binderFactory = GWT.create(ControllerBinderFactory.class); JSClosure binder = binderFactory.create(controller); m = "shifing args"; Object[] shiftedArgs = new Object[args.length - 1]; for (int i = 0; i < shiftedArgs.length; i++) { shiftedArgs[i] = args[i + 1]; } m = "binding args"; scopeBinder.apply(args); m = "injecting shiftedArgs"; if (binder != null) { binder.apply(shiftedArgs); } LOG.finest(m = name + ".onControllerLoad()"); controller.onControllerLoad(); } catch (Exception e) { LOG.log(Level.WARNING, "Exception while " + m, e); } } }