#1128 Serving fantom generated JavaScript directly from pod files

dfreire Sun 27 Jun 2010

I have an HTML file to test a web application composed of several Fantom generated JavaScript files. Everything runs fine.

Now I'm trying to do the same thing, but instead of using the HTML file, I want to dynamically load the generated JavaScript files, which are contained in the pods.

The closest example I have encountered is the js demo, but I don't completely understand it, and it seems overcomplicated to my purposes (I don't want to compile any Fantom code on the fly, I just want to use already generated JavaScript). So I've tried my luck simplifying things to something like this:

using util
using web
using webmod
using wisp

class Demo : AbstractMain
{
  @Opt { help = "http port" }
  Int port := 8080

  override Int run()
  {
    wisp := WispService
    {
      it.port = this.port
      it.root = DemoMod()
    }
    return runServices([wisp])
  }
}

const class DemoMod : WebMod
{
  override Void onGet()
  {
    res.headers["Content-Type"] = "text/html; charset=utf-8"
    res.out.docType
    res.out.html
    res.out.head
      res.out.title.w("Demo").titleEnd
      res.out.includeJs(`/pod/sys/sys.js`)
      res.out.includeJs(`/pod/web/web.js`)
      res.out.includeJs(`/pod/gfx/gfx.js`)
      res.out.includeJs(`/pod/dom/dom.js`)
      res.out.includeJs(`/pod/helloJsStep2/helloJsStep2.js`)
      res.out.includeJs(`/pod/helloJsStep2Demo/helloJsStep2Demo.js`)
    res.out.headEnd
    res.out.body
    res.out.script.w(getJs()).scriptEnd
    res.out.bodyEnd
    res.out.htmlEnd
  }

  Str getJs()
  {
    Str js := "var demo = fan.helloJsStep2Demo.JsLibWrapperDemo.make();\n"
    js += "demo.run();\n"
    return js
  }

}

Now, this code doesn't run in the browser, because "fan is not defined" (according to firebug).

It is clear that the res.out.includeJs(...) lines are not fetching the js files from the pods. But if that is so, how come this works without problem in the js demo?

Is there any simple way to do what I need?

Thanks!

Yuri Strot Mon 28 Jun 2010

The problem is you construct index page for any request to server. When browser loading content of js file it hook the same DemoMod.onGet method and get index content instead of js file.

js demo handle this situation correctly:

override Void onGet()
{
  //check uri first
  name := req.modRel.path.first
  if (name == null)
    onIndex // load index page for `http://yoursite.com/`
  else if (name == "pod")
    onPodFile //load file from pod for `http://yoursite.com/pod/...`
}

** This method fetching resources from the pods
Void onPodFile()
{
  // serve up pod resources
  File file := ("fan://" + req.uri[1..-1]).toUri.get
  if (!file.exists) { res.sendErr(404); return }
  FileWeblet(file).onService
}

Void onIndex()
{
  ...
}

dfreire Mon 28 Jun 2010

ystrot, thanks for the reply, but as far as I understand, that serves one static file of one pod, but it does not serve several static contents of several pods, as it would be done in an html file.

(Having a single index page for any request to server is not a problem for me at the moment.)

andy Mon 28 Jun 2010

dfreire - you actually have two problems. The first, as ystrot noted, you need to modify your DemoMod to serve up the pod js files. Once that is fixed you will see the second, which is you're trying to execute js before you have loaded the dependencies. If you plan to call any Fantom APIs in your webpage - you must do so in your onLoad callback to guareente you have everything you need.

dfreire Mon 28 Jun 2010

andy, thanks but I don't get it yet...

Using the FileWeblet, I can only serve the js files of one pod, right? What if (as it is my case) I want to serve js files spread from different pods?

What if (as it will be my case in the future) I also want to link to external js files?

To clarify: my approach was not to call any Fantom code in the web page, but instead, call the JavaScript generated by the relevant Fantom code.

(In the solution you are proposing, does it make sense to have an index.html file inside the main pod, and this index.html would make the appropriate references?)

Thanks for your patience :-)

andy Mon 28 Jun 2010

Sounds like maybe you're confused about the role of WebMod - I'll try to catch up with you on IRC to explain it.

rfeldman Mon 28 Jun 2010

I am confused about this as well; after the IRC clarification, could I trouble you to post a summary of some sort on here?

andy Mon 28 Jun 2010

@rfeldman - Depends on what you are confused about. I am guessing for both you, that it was the role WebMod plays. In this case, the Wisp server has just a single WebMod - so its responsible for every HTTP request coming in. So as ystrot pointed out, you need to follow the same design as js/demo and allow it to service requests for the external JavaScript scripts. dreire's example returns the index HTML for every request to his webserver. I believe the js/demo already does exactly what you want.

dfreire Mon 28 Jun 2010

Thanks to ystrot and andy for correctly identifying the problem (which was of course, the onGet method always retrieving the same content, regardless of the call - silly me).

So an updated - and fully working - version looks like this:

using util
using web
using webmod
using wisp

class Demo : AbstractMain
{
  @Opt { help = "http port" }
  Int port := 8080

  override Int run()
  {
    wisp := WispService
    {
      it.port = this.port
      it.root = DemoMod()
    }
    return runServices([wisp])
  }
}

const class DemoMod : WebMod
{

  override Void onGet()
  {
    name := req.modRel.path.first
    if (name == null)
      onIndex // http://localhost:8080
    else if (name == "pod")
      onPodFile // http://localhost:8080/pod/helloJsStep3Demo/res/index.html
  }

  Void onIndex()
  {
    res.headers["Content-Type"] = "text/html; charset=utf-8"
    res.out.docType
    res.out.html
    res.out.head
      res.out.title.w("Demo").titleEnd
      res.out.includeJs(`/pod/sys/sys.js`)
      res.out.includeJs(`/pod/web/web.js`)
      res.out.includeJs(`/pod/gfx/gfx.js`)
      res.out.includeJs(`/pod/dom/dom.js`)
      res.out.includeJs(`/pod/helloJsStep3/helloJsStep3.js`)
      res.out.includeJs(`/pod/helloJsStep3Demo/helloJsStep3Demo.js`)
    res.out.headEnd
    res.out.body
    res.out.script.w(getJs()).scriptEnd
    res.out.bodyEnd
    res.out.htmlEnd
  }

  Str getJs()
  {
    Str js := "var demo = fan.helloJsStep3Demo.JsLibWrapperDemo.make();\n"
    js += "demo.run();\n"
    return js
  }

  Void onPodFile()
  {
    File file := ("fan://" + req.uri[1..-1]).toUri.get
    if (!file.exists) { res.sendErr(404); return }
    FileWeblet(file).onService
  }

}

This code exemplifies two possible solutions for my original problem:

  • either dynamically construct the html page, with the onIndex()
  • or publish an html page located inside the pod, with the onPodFile()

I have added this as the Step3 of my ongoing step-by-step Fantom examples.

Thanks again :-)

Login or Signup to reply.