How to force DOM element creation with qx.ui.embed.Html? - javascript

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?

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.

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.

Related

Testing D3.js drag events with Cypress.js

I have an SVG object which uses d3-zoom for zoom and pan functionality. It works flawlessly but the problem showed up when I started to work on integration tests using Cypress.js.
I tried using standard mouse events on the svg element, to simulate drag behavior:
cy.get('svg')
.trigger('mousedown', { which: 1, force: true })
.trigger('mousemove', { position: 'left' })
.trigger('mouseup', { position: 'left', force: true });
The example above is taken from the Cypress drag and drop recipe, and it produces the following error in the nodrag.js file:
cannot read property document of undefined
Below you can see where the error occurs (view is undefined):
__webpack_exports__["default"] = (function(view) {
var root = view.document.documentElement,
...
I spent a lot of hours trying to trigger the event in another way, but without a success - like trying the snippet above with the svg container.
Please keep in mind that I cannot access any d3.js package from the Cypress test because it's imported as an NPM package in a React application.
Thank you in advance for you help!
I could only arrive at a partial answer before I had to move on, but perhaps this can help you, or someone else, find the ultimate solution.
To remedy the error, a view property must be provided for mousedown. Providing window, like this, allowed the D3 methods to fire properly:
cy.get('svg')
.trigger('mousedown', { which: 1, force: true, view: window }) // <-- here
.trigger('mousemove', { position: 'left', view: window }) // <-- here
.trigger('mouseup', { position: 'left', force: true });
However, no dragging or movement occurred during the test run, and other questions emerged from there. Starting with... Is this the right context to send along with the event? It seemed so, since window seems to be the only context that has the property chain that D3 anticipates:
view.document.documentElement
Or is that an anti-pattern... a code smell?
Running down those subsequent questions led to a few observations that seemed to have significance.
The first concerns how D3 handles mouse and drag events. D3 has numerous event listeners and callbacks that override standard events and their respective handlers.
The second, is that iframes are in play with the Cypress test runner.
Could it be that Cypress's programmatically triggered events are firing properly in the Cypress iframe, but due to D3's aggressive event handling, the translation of those events into the application iframe are getting lost? Especially considering that manually dragging a circle in the testing viewport worked fine.
Which, again, leads back to:
Are the programmatically triggered events not being called in the correct context?
Are those events somehow being swallowed by or colliding with D3's event handlers?
I selected the Zoomable Force Directed Graph as my D3 subject, inside of a simple Ember application, for researching this question. It perfectly reproduced the error mentioned, so it definitely seems to be a D3 + Cypress challenge, and unrelated to the front-end framework.
I hope this effort is helpful.
Continued...
After some further reading – Cypress's Trade-offs, and particularly, their open pull request Support for Native Browser Events – it seems likely that the event handling overrides within D3 are not yet fully reconcilable within Cypress. Simpler implementations, like those detailed in the drag and drop example, do not present the event handling challenges introduced by a 3rd party library like D3. However, this support does appear to be under development within the Cypress team.
Try this:
cy.window().then(win => {
cy.get('svg')
.trigger('mousedown', {
which: 1,
force: true,
view: win,
})
.trigger('mousemove', {
clientX: 300,
clientY: 500,
force: true,
})
.trigger('mouseup', {
force: true,
view: win,
});
});
Referencing Jennifer Shehane's answer in this GitHub issue, the answer to the cannot read property document of undefined part is to plug the window object into view in the trigger options. The issue mentioned in jacefarm's answer, where no movement occurred, seems to be resolved by specifying clientX/clientY rather than using positions relative to the selected element.
I used a little bit different from your code so you can try
cy.get('svg')
.trigger('mousedown', { which: 1 })
.trigger('dragstart', {})
.trigger('drag', {});
FWIW I had this same issue and using this library allowed me to interact with the D3 elements: https://github.com/dmtrKovalenko/cypress-real-events

Binding javascript (d3.js) to shiny

First, I am fairly unfamiliar with javascript and its library d3.js, but I am familiar with R. Creating dashboards using Shiny has been fun and easy (thanks to stackoverflow). Now I want to expand it by connect d3 elements to it.
I'm looking for information sources on how to actually bind javascript to Shiny (R dashboard) and explain what is actually going on.
Background:
I did the tutorial on js and jquery on w3schools and learned (a bit) about d3 using Scott Murray's book (Interactive Data visualization for the web). I hoped this would be enough to make me understand the examples and explanation concerning how to build custom input/output bindings on the Shiny website:
http://shiny.rstudio.com/articles/building-inputs.html
But unfortunately I don't and I can't seem to find any examples which are in minimal working code. Many examples on github are to complex for me to dissect, most probably because of my little experience with javascript. Here is an examples of custom input binding with javascript:
https://github.com/jcheng5/shiny-js-examples/tree/master/input
Here is an example of an input & output binding I try to unfold:
<script src="http://d3js.org/d3.v3.js"></script>
<script type="text/javascript">
(function(){
// Probably not idiomatic javascript.
this.countValue=0;
// BEGIN: FUNCTION
updateView = function(message) {
var svg = d3.select(".d3io").select("svg")
svg.append("text")
.transition()
.attr("x",message[0])
.attr("y",message[1])
.text(countValue)
.each("end",function(){
if(countValue<100) {
countValue+=1;
$(".d3io").trigger("change");
}
})
}
// END: FUNCTION
//BEGIN: OUTPUT BINDING
var d3OutputBinding = new Shiny.OutputBinding();
$.extend(d3OutputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
renderError: function(el,error) {
console.log("Foe");
},
renderValue: function(el,data) {
updateView(data);
console.log("Friend");
}
});
Shiny.outputBindings.register(d3OutputBinding);
//END: OUTPUT BINDING
//BEGIN: INPUT BINDING
var d3InputBinding = new Shiny.InputBinding();
$.extend(d3InputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
getValue: function(el) {
return countValue;
},
subscribe: function(el, callback) {
$(el).on("change.d3InputBinding", function(e) {
callback();
});
}
});
Shiny.inputBindings.register(d3InputBinding);
//END: OUTPUT BINDING
})()
</script>
Where "d3io" is a div element in the ui, updateView() is a function. Here is the ui:
#UI
library(shiny)
d3IO <- function(inputoutputID) {
div(id=inputoutputID,class=inputoutputID,tag("svg","")) #; eerst zat ; erbij, maar werkt blijkbaar ook zonder
}
# Define UI for shiny d3 chatter application
shinyUI(pageWithSidebar(
# Application title
headerPanel("D3 Javascript chatter",
"Demo of how to create D3 I/O and cumulative data transfer"),
sidebarPanel(
tags$p("This widget is a demonstration of how to wire shiny direct to javascript, without any input elements."),
tags$p("Each time a transition ends, the client asks the server for another packet of information, and adds it
to the existing set"),
tags$p("I can't claim this is likely to be idiomatic javascript, because I'm a novice, but it allows d3 apps
to do progressive rendering. In real use, a more complex request/response protocol will probably be
required. -AlexBBrown")
),
mainPanel(
includeHTML("d3widget.js"),
d3IO("d3io") #Creates div element that d3 selects
)
))
Here is the server file:
# SERVER
library(shiny)
# Define server logic required to respond to d3 requests
shinyServer(function(input, output) {
# Generate a plot of the requested variable against mpg and only
# include outliers if requested
output$d3io <- reactive(function() {
if (is.null(input$d3io)) {
0;
} else {
list(rnorm(1)*400+200,rnorm(1)*400+200);
}
})
})
Specific questions:
1) The server.r seems to get input called "d3io" (input$d3io) since this is not defined in ui.r, I reasoned it must come from the javascript file. Which element does it actually refer to?
2) I have trouble understanding the custom binding part:
var d3OutputBinding = new Shiny.OutputBinding();
$.extend(d3OutputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
renderError: function(el,error) {
console.log("Foe");
},
renderValue: function(el,data) {
updateView(data);
console.log("Friend");
}
});
Shiny.outputBindings.register(d3OutputBinding);
My understanding is:
Create a new shiny outputbinding, first find the class .d3io (div element), if error then write to console "Foe" (is this special code?), if not error then renderValue using the function updateView using data (Where does it receive this value from?) and write to console "Friend". Finally register output.
Hope you guys can help! I'm creating a document with the steps on "The necessary steps to learn how to implement javascript into shiny when you don't know any javascript", I would love that!:)
Cheers,
Long
Hi Sweetbabyjesus (so fun to say). You had two questions:
1) The server.r seems to get input called "d3io" (input$d3io) since this is not defined in ui.r, I reasoned it must come from the javascript file. Which element does it actually refer to?
That phrase input$d3io has the following components:
input is a parameter passed into the function - it's a list that
stores the current values of all the widgets in the app.
$ is the member selector.
d3io refers to the content of the div element with that id
('d3IO("d3io")') in the mainPanel of the UI.
2) I have trouble understanding the custom binding part:
var d3OutputBinding = new Shiny.OutputBinding();
That's right, this creates an instance of Shiny.OutputBinding and assigns it to the variable d3OutputBinding.
$.extend(d3OutputBinding, {
find: function(scope) {
return $(scope).find(".d3io");
},
renderError: function(el,error) {
console.log("Foe");
},
renderValue: function(el,data) {
updateView(data);
console.log("Friend");
}
});
This code extends the behaviour of d3OutputBinding with three functions called find, renderError and renderValue. Those three functions are required for a Shiny.OutputBinding.
find is the key because it returns a list of elements that should be passed into the two render functions via their el parameter. Notice it's returning elements whose css class is "d3io" - that's the same div mentioned earlier.
Note that extend() is a function of jQuery javascript library, and the $ in this context is an alias for the jQuery object.
Shiny.outputBindings.register(d3OutputBinding);
Lets Shiny know that this newly configured object should be put to use now.
Cheers, Nick
I'm going to take a step back and assume you want the amazing results D3 is capable of, but aren't necessarily tied to D3. Essentially, I'll be answering this question:
What are the necessary steps to learn how to implement JavaScript into Shiny when you don't know any JavaScript?
While D3 is amazingly powerful, it's also notoriously difficult to master - even for many folks who are quite comfortable with JavaScript. While I love D3 and use it almost every day, I'd recommend against it in this case. Instead, there's library called Plotly, which uses D3 in the background, but is built specifically for the scientific community and data scientists, so it's very friendly to the R community.
They have a thorough tutorial for connecting to Shiny and even have a ggplot2 converter if you're already familiar with that syntax, as many in the R world are. Unless your needs are very unusual, Plotly will likely serve your needs just as well as writing directly in D3, with a much more friendly learning curve.
Are you familiar with the rCharts package? It can work pretty well with Shiny and most of the output options are based on D3 variants. Two examples.
Very busy with work, I haven't had the chance to post it. Note that this is a workaround using the customMessageHandler (and I'm not using custom input/output binding). Here goes:
Objective: Send data from data frame to create a D3JS tree using customMessageHandler.
Path: I've managed to send data in data.frame format to a d3js tree. After you click the actionbutton, it changes the data in the data frame to JSON format, then sends it to the js file which creates the tree. The data of the tree is hard-coded on "server.r".
Where's the code? On my github!
https://github.com/SweetBabyJesus/shiny-d3js-simple-binding
Original: I created a tree algorithm based on CHAID to create insights from large datasets. People can upload their csv to the dashboard which then spits out the d3js tree:) The code is somewhat lengthy, therefore I cut it down for you and created a minimal code example.
Hope you like it.
Cheers,
Long

jQuery, What's Best, Have All the Binds in One Single File For an Entire Site or on a per Page Basis?

I'm in the middle of building a web app with heavy use of jQuery plugins and lots of bindings.
The backend was developed with a template system which only allows (as of now) to place all scripts in that one HTML file. We will use YUI compressor to merge all these into one.
Now, for bindings, how bad is it to have binds in an HTML file (which now is a template for the whole site) for elements that may not be present on a particular page?
Any advice is greatly appreciated
I've been using Paul Irish's markup-based solution pretty extensively on larger sites.
One of the biggest problems with doing this is one of performance - the selector will be evaluated and the DOM searched for each binding not intended for a specific page. At the very least, perhaps set up an object literal to run appropriate ready binding code based on a page identifier, which could be the window.location.href or a substring of. Something like
// avoid global pollution!
(function() {
var pages = {
pageX : {
ready: function() { /* code to run on ready */ },
teardown: function() { /* code to run on teardown */ }
},
pageY : {
ready: function() { /* code to run on ready */ },
teardown: function() { /* code to run on teardown */ }
},
}
// set up ready event handler
$(ready);
// handler function to execute when ready event raised
// Note: Access to pages through closure
function ready() {
var location = window.location.href;
pages[location].ready();
}
})();
Be careful with your selectors if you've got some large pages. For example, if you've got some pages with big, but inert (no bindings) tables, but other pages where tables are small but have controls in them, you probably don't want to do this:
$('td.bindMe').bind('whatever', function() { ... });
(Set aside the live() issue here; sometimes you need to do element-by-element work and that's what I'm talking about.) The problem is that Sizzle will have to look through all the td elements on the page, potentially. Instead, you can put some sort of "marker" container around things like the "active" table with controls, and work it that way:
$('table#withControls').find('td.bindMe').bind(/* ... */);
That way Sizzle only needs to figure out that there's no table called "withControls", and then it's done.
Biggest problem for using all bindings on all pages is that you can get bindings that you did not intended to have, causing troubles...
And of course you will have some performance issues in the page load, but if that is a problem is of course depending on how many bindings you have and how the code looks like.
You might lose some performance on the client side (parsing the file, executing the document-ready handler), but it improves caching on the client (i.e. the file doesn't need to be transferred more than once). That saves server lookups as well. I think this is rather an advantage than a disadvantage as long as you can ensure you're not accidentally modifying objects.
I think the selector engine is fast enough that you, or anyone else, shouldn't notice a difference.
Obviously this is not a "best practice," but if you're binding to ID's and classnames and you won't have any conflicts or unintended bindings then I don't see the harm.

Dojo widget defaults

i'm currently writing a large scale application heavily based on the dojo toolkit. The whole app is working and standing, but one issue i can not find my way out with, is the creation of custom widgets. It would be useful because it would clean up my source code and also i can reuse this 'widgets' in later projects.
For example: i have a main toolbar, which i would like to call using
myapp.toolbar = new myapp.mainToolbar();
instead of using
myapp.toolbar = new new dijit.Toolbar({}, containerID);
var button1 = new dijit.form.Button({
label: 'Delete',
id: 'toolbarbutton1',
showLabel: true,
iconClass: "dijitEditorIcon dijitEditorIcon Delete"
});
myapp.toolbar.addChild(button1);
...
So in short: how do i set up the whole toolbar somewhere else and call it as a simple object? Trying to figure out dojo.declare('myapp.mainToolbar', dijit.Toolbar, {...}) but then i get a bunch of errors like 'startup function not existing' etc...
I'd like to do all this programmatically, so without the template html and css files in a custom widget.
A link to a good tutorial or howto would be nice, although google nor yahoo! will reveal any extra's on this matter for me... :)
There are multiple ways to do this.
It seems like your method of extending Toolbar should work (not sure why it didn't).
You can also declare a class that embeds Toolbar and the buttons, using widgetsInTemplate:
dojo.declare("MyToolbar", [dijit._Widget, dijit._Templated], {
_widgetsInTemplate: true,
templateString: '<div> <div dojoType=dijit.Toolbar>' +
' <button dojoType=dijit.form.Button ...
Note that the top node in MyToolbar can't have a dojoType, so I put Toolbar one level down.
Alternately you can do the same thing by using dijit.Declaration, see http://docs.dojocampus.org/dijit/Declaration.
It works for me when I use declare with the superclass inside of an array:
dojo.declare('myapp.mainToolbar', [ dijit.Toolbar ],
{
....
}
);
var x = new myapp.mainToolbar({ ... });
x.startup();
Which kind of violates the docs. It should take one Function or an array of Functions.

Event handling in Dojo

Taking Jeff Atwood's advice, I decided to use a JavaScript library for the very basic to-do list application I'm writing. I picked the Dojo toolkit, version 1.1.1. At first, all was fine: the drag-and-drop code I wrote worked first time, you can drag tasks on-screen to change their order of precedence, and each drag-and-drop operation calls an event handler that sends an AJAX call to the server to let it know that order has been changed.
Then I went to add in the email tracking functionality. Standard stuff: new incoming emails have a unique ID number attached to their subject line, all subsequent emails about that problem can be tracked by simply leaving that ID number in the subject when you reply. So, we have a list of open tasks, each with their own ID number, and each of those tasks has a time-ordered list of associated emails. I wanted the text of those emails to be available to the user as they were looking at their list of tasks, so I made each task box a Dijit "Tree" control - top level contains the task description, branches contain email dates, and a single "leaf" off of each of those branches contains the email text.
First problem: I wanted the tree view to be fully-collapsed by default. After searching Google quite extensively, I found a number of solutions, all of which seemed to be valid for previous versions of Dojo but not the one I was using. I eventually figured out that the best solution would seem to be to have a event handler called when the Tree control had loaded that simply collapsed each branch/leaf. Unfortunately, even though the Tree control had been instantiated and its "startup" event handler called, the branches and leaves still hadn't loaded (the data was still being loaded via an AJAX call). So, I modified the system so that all email text and Tree structure is added server-side. This means the whole fully-populated Tree control is available when its startup event handler is called.
So, the startup event handler fully collapses the tree. Next, I couldn't find a "proper" way to have nice formatted text for the email leaves. I can put the email text in the leaf just fine, but any HTML gets escaped out and shows up in the web page. Cue more rummaging around Dojo's documentation (tends to be out of date, with code and examples for pre-1.0 versions) and Google. I eventually came up with the solution of getting JavaScript to go and read the SPAN element that's inside each leaf node and un-escape the escaped HTML code in it's innerHTML. I figured I'd put code to do this in with the fully-collapse-the-tree code, in the Tree control's startup event handler.
However... it turns out that the SPAN element isn't actually created until the user clicks on the expando (the little "+" symbol in a tree view you click to expand a node). Okay, fair enough - I'll add the re-formatting code to the onExpand() event handler, or whatever it's called. Which doesn't seem to exist. I've searched to documentation, I've searched Google... I'm quite possibly mis-understanding Dojo's "publish/subscribe" event handling system, but I think that mainly because there doesn't seem to be any comprehensive documentation for it anywhere (like, where do I find out what events I can subscribe to?).
So, in the end, the best solution I can come up with is to add an onClick event handler (not a "Dojo" event, but a plain JavaScript event that Dojo knows nothing about) to the expando node of each Tree branch that re-formats the HTML inside the SPAN element of each leaf. Except... when that is called, the SPAN element still doesn't exist (sometimes - other times it's been cached, just to further confuse you). Therefore, I have the event handler set up a timer that periodically calls a function that checks to see if the relevant SPAN element has turned up yet before then re-formatting it.
// An event handler called whenever a "email title" tree node is expanded.
function formatTreeNode(nodeID) {
if (dijit.byId(nodeID).getChildren().length != 0) {
clearInterval(nodeUpdateIntervalID);
messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML
if (messageBody.indexOf("<b>Message text:</b>") == -1) {
messageBody = messageBody.replace(/>/g, ">");
messageBody = messageBody.replace(/</g, "<");
messageBody = messageBody.replace(/&/g, "&");
dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>";
}
}
}
// An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed.
function setupTree(theTree) {
dijit.byId("tree-"+theTree).rootNode.collapse();
messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren();
for (pl = 0; pl < messageNode.length; pl++) {
messageNode[pl].collapse();
messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');");
}
}
The above has the feel of a truly horrible hack, and I feel sure I must have taken a wrong turn somewhere early on in my thought process. Can someone please tell me:
The correct way to go about putting nicely-formatted text inside a Dojo/Dijit Tree control.
The correct way to handle Dojo events, like where I can figure out what events are available for me to subscribe to.
A better JavaScript library to use (can I do what I want to with JQuery and avoid the all-around-the-houses approach seen above?).
PS: If you're naming a software project, give thought to its name's uniqueness in Google - I'm sure searching for "Dojo" documentation in Google would be easier without all the martial arts results getting in the way.
PPS: Firefox spellchecker knows how to spell "Atwood", correcting me when I put two 'T's instead of one. Is Jeff just that famous now?
I assume that you followed the dijit.Tree and dojo.data in Dojo 1.1 tutorial which directed you to pass the data to the tree control using a data store. That had me banging my head of a brick wall for a while.
Its not really a great approach and the alternative is not really well documented. You need to create a use model instead. I have included an example below of a tree model that I created for displaying the structure of an LDAP directory.
You will find the default implementation of the model in your dojo distribution at ./dijit/_tree/model.js. The comments should help you understand the functions supported by the model.
The IDirectoryService class the code below are stubs for server-side Java POJOs generated by Direct Web Remoting (DWR). I highly recommend DWR if you going to be doing a lot of client-server interaction.
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], {
getRoot : function(onItem) {
IDirectoryService.getRoots( function(roots) {
onItem(roots[0])
});
},
mayHaveChildren : function(item) {
return true;
},
getChildren : function(parentItem, onComplete) {
IDirectoryService.getChildrenImpl(parentItem, onComplete);
},
getIdentity : function(item) {
return item.dn;
},
getLabel : function(item) {
return item.rdn;
}
});
And here is an extract from the my JSP page where I created the model and used it to populate the tree control.
<div
dojoType="LDAPDirectoryTreeModel"
jsid="treeModel"
id="treeModel">
</div>
<div
jsid="tree"
id="tree"
dojoType="dijit.Tree" model="treeModel"
labelAttr="name"
label="${directory.host}:${directory.port}">
</div>

Categories

Resources