ciscoheat / mithril-hx

Haxe externs for Mithril (JS MVC framework)
181 stars 12 forks source link

Question on controller macro #7

Closed andywhite37 closed 9 years ago

andywhite37 commented 9 years ago

Hi again, I had a question on the controller macro here:

https://github.com/ciscoheat/mithril-hx/blob/master/src/mithril/macros/ModuleBuilder.hx#L176-L178

I'm trying out a couple different ways of componentizing views, and one pattern I tried looked like this (some code is left out):

class Button implements Module<Button> {
  public function new () {
    // something here
  }
  public function controller() {
    // do something?
  }
  public function view() {
    m("button", "My Button");
  }
}

class HomePage implements Module<HomePage> {
  @prop var button : Button;

  public function new(button : Button) {
    this.button = M.prop(button);
  }

  public function controller() {
    // Stack overflow issue here, due to macro logic for controller
    button().controller();
  }

  public function view() {
    m("div", button().view());
  }
}

When the HomePage controller is called, and it calls the Button controller, the button.controller() body ends up re-calling the HomePage controller, because of the if check inserted by the macro at the top of the controller methods. This eventually results in a call stack overflow. Is there anything that could be changed with that macro to allow nested controller calls like this?

if(m.__cm != this && Type["typeof"](m.__cm) != ValueType.TObject) return m.__cm.controller();

In this case m.cm is HomePage and is != this (Button), and m.cm is not a TObject, so it re-calls HomePage controller, rather than calling the button controller.

andywhite37 commented 9 years ago

I guess since mithril treats the constructor function of a module as a constructor, maybe in the Haxe world, it makes sense to just use the class constructor to initialize the component, and kind of ignore the controller function?

I noticed you just use the plain View interface in a lot of your examples rather than Module<T> - is that because the controller isn't as necessary in Haxe?

ciscoheat commented 9 years ago

Yes, you nailed the issue. :) Mithril makes a new module.controller() call in m.module which forces this hack. And it's true that the controller isn't that useful in Haxe, I'm using it only when some expensive computations or ajax requests needs to be done, to avoid putting that in the constructor.

A few versions back you could only pass a Module to m.route or m.module, but when that was relaxed I was happy to make the View interface simpler to get rid of the controller method when it's not needed. There is a ControllerView<T> interface if you create your modules from different objects and want the controller object to be passed to the view, like view(?ctrl : T).

However, I still think this should work without stack overflow so I'll take a look and hopefully fix it! Thanks for helping out. :)

andywhite37 commented 9 years ago

Cool, thanks for your reply. I think the View and ControllerView interfaces are going to make a lot of sense for the stuff I'm doing. Thanks again for your work on the project!

ciscoheat commented 9 years ago

Glad you're using it. :) I've fixed the issue now in 0.20.3, tested with your code and the examples and it seems to work.