#55 webui

andy Fri 17 Mar 2006

I think I have a good idea of what the "webui" layer is going to look like (which will sit above the raw "servlet" API). As we discussed, this API will be geared toward the programmer, and bucks any performance considerations at this point. So what we end of with it is a pretty familiar widget-style API:

abstract class Widget
{
  virtual Void get(WebContext cx)
  {
    if (form_submit) // target one widget.get
    else children.each |Widget w| { w.get(cx) }
  }

  virtual Void post(WebContext cx)
  {
    if (form_submit) // target one widget.post
    else children.each |Widget w| { w.post(cx) }
  }

  Void add(Str name, Widget child)
  {
    children[name] = child
  }

  Void startForm(WebContext cx, Bool post := true)
  { 
    out("<form method='post/get' action='/test.a.b'>")
  }

  Void endForm(WebContext cx)
  {
    out("</form>")
  }

  Widget[Str] children := Widget[:]
}

The tree for a widget is built in its constructor, so gets built before each request. We'll use the child names as the path to route requests back to individual widgets (not sure how we encode that in the form/action yet). The convenience startForm method will handle writing the form though.

Right now I have all the context in single parameter that gets passed around. I also thought about setting request/response/etc as fields on each widget (like Rails does) so you didn't have to pass the cx around - not sure its that big a deal though.

andy Fri 17 Mar 2006

Also, a Chrome class will be responsible for writing the containing markup for a widget - the default Chrome class will be pretty minimal:

class Chrome
{
  virtual Void get(Widget widget, WebContext cx)
  {
    out("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"")
    out(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">")
    out("<html>")
    out("<head>")
    out("  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />")
    out("  <title>Untitled</title>")
    out("</head>")
    out("<body>")
    widget.get(cx)
    out("</body>")
    out("</html>")
  }

  virtual Void post(Widget widget, WebContext cx)
  {
    widget.post(cx)
  }
}

There are some higher level abstractions like how we map URL's to widgets, and how Chrome is registered that I don't really understand yet - the URL mapping may tie into a core naming solution we do in Fan (per my discission with Brian the other day).

brian Fri 17 Mar 2006

I don't think I really like the WebContext name - whatever we do should map to the gateway/servlet API. For example if a Servlet.get() takes a WebReq and WebRes, then that pattern should map directly to webui: WebUiReq and WebUiRes.

Isn't Widget just a composable Servlet designed to be wrapped with Chrome?

andy Fri 17 Mar 2006

Yep, pretty much.

I'd like to avoid dual params, becuase that adds two things to every method you have to pass them to - I'd like to see something cleaner.

brian Fri 17 Mar 2006

Then doesn't it extend Weblet (or whatever we call it).

I like the idea of one class you pass around, but I don't like the term WebContext.

andy Sat 18 Mar 2006

Potentially unless it doesn't make sense. Though I think I have come around to the Rails method, where WebReq and WebResp are just fields on the widget itself - maybe with a custom getter that fetches the root widget's instances. Its just annoying to always have to pass that stuff around.

brian Sun 19 Mar 2006

If that's the case then I say we keep things WebReq and WebRes at the gateway level, and you subclass them as WebUiReq and WebUiRes.

Login or Signup to reply.