2

I am trying to embed a RaphaelJS paper into qooxdoo widget. RaphaelJS is a SVG drawing library for JavaScript, and it needs to bind to a HTML <div> prior to any drawing. For that, I call new qx.ui.embed.Html("<div id='raphael'></div>") and add it to my widget. After that, I should initialize Raphael by passing the div ID to it.

Problem is that <div id='raphael'> is not committed to the DOM model (i.e., no real DOM element is created) right after qx.ui.embed.Html() constructor call. The DOM element creation is indeed deferred until the widget is painted to the screen. I've managed to catch an appear event for the widget, and, after that, element's existence is guaranteed, and I can initialize Raphael library and do some drawing.

This approach assumes that I have to run all my application logic from within that appear event handler, which is probably not what I want. Is there any other way to get a widget in its ready-for-drawing state in the main application flow?

Dimitri
  • 301
  • 2
  • 13

2 Answers2

1

What you could do is create your own widget RaphaelWidget.js:

qx.Class.define("myApp.RaphaelWidget",
{
  extend : qx.ui.core.Widget,

  construct : function()
  {
    this.base(arguments);

    this.addListener("appear", this._onAppear, this);
  },

  members :
  {
    /**
     * Overwritten from qx.ui.core.Widget.
     */
    _createContentElement : function()
    {
      return new qx.html.Element("div", {
        overflowX: "hidden",
        overflowY: "hidden",
        border: "1px solid #aaa"  // just for debugging
      }, {"id": "canvas-raphael"});
    },

    _onAppear : function()
    {
      var paper = new Raphael(document.getElementById('canvas-raphael'), 250, 250);
      var circle = paper.circle(100, 100, 80);
    }
  }
});

And then do for example in your Application.js:

var raphael = new myApp.RaphaelWidget();
raphael.setWidth(250);
raphael.setHeight(250);
this.getRoot().add(raphael);

Now you can develop your Raphael specific code in this new widget class.

  • Richard, thanks for your suggestion. The code is much more concise than if we were using qx.ui.embed.Html. Unfortunately, it suffers from the same issue: you can't use this widget from the main application flow, right after its creation. Imagine we've got some `foo` method in RaphaelWidget that does some drawing. If you call `raphael.foo()` just after `this.getRoot().add(raphael)`, this wouldn't work, because `canvas-raphael` DOM element wouldn't yet exist at that moment. But if you do `raphael.addListener("appear", function() { this.foo(); })` - this will work. – Dimitri Jun 20 '13 at 06:17
  • I just wanted to know if there is a way to initialize the widget so that it is usable from the main flow, right after creation, and not from some event handler. – Dimitri Jun 20 '13 at 06:19
0

Forcing rendering/DOM manipulation actions is called "flushing" in qooxdoo. E.g. the qx.html.Element from Richard's solution has a .flush() method. You might want to try this, or search the API documentation for the term 'flush'.

That being said flushing is a last resort, and shouldn't be used excessively as this would severely degrade performance. You shouldn't shy away from asynchronous programming when you are doing JavaScript. Even your "main" method is a callback, called from the qooxdoo runtime at some point in time.

There are several qooxdoo contributions that integrate third-party libraries the likes of Rafael. For a more idiomatic solution of doing this see e.g. QxDyGraphs (part. the __addCanvas method), a contrib that integrates the Dygraphs JS library.

ThomasH
  • 22,276
  • 13
  • 61
  • 62