gwt datagrid Bootstrap Select cell (need to execute javascript after cell has been added/rendered) - select

I inherited a project that uses GWT to create a web application. It uses a GWT wrapper for Bootstrap3 to do the styling of elements. This is working nicely for the most part. Now I hit a road block trying to add a Bootstrap3 Select to a GWT DataGrid table that currently uses a GWT SelectionCell. The problem is that a SelectionCell is not 'Bootstrap styled' and therefore doesn't match the style of the rest of web application.
Unfortunately, I cannot simply add the Select to a GWT DataGrid as the Select does not implement the GWT Cell interface nor does it extend a class that can be added to a Column that is then added to the DataGrid. Sub classing Select and implementing Cell fixes this problem. However, I cannot get the Select to render properly as it requires a JavaScript function to be executed after the Select has been attached to the DOM which it never is being wrapped by a Column in a DataGrid.
Instead, it is rendered into a SafeHtmlBuilder by the render function of Cell.
// GWTBootstrap3::Select function to render the Select
public void render() {
if (isAttached())
command(getElement(), SelectCommand.RENDER);
}
protected native void command(Element e, String command) /*-{
$wnd.jQuery(e).selectpicker(command);
}-*/;
Since the Select is never attached to the DOM I need to call the selectpicker function manually. For his, I created my own native function which I need to call after the SafeHtmlBuilder has been added to the DataGrid. If my Select subclass accepts CLICK events through Cell's onBrowserEvent and calling the native function after clicking into the cell renders the Select properly.
What I cannot figure out is when to call the selectpicker function programatically to render the Select automatically after a row has been added by an RPC call. I tried to register different handlers to no avail. The handlers are called and my native function wrapping the selectpicker function is called as well but it seems like the HTML select has not yet been added to the table. Calling the above JavaScript snipped from the JavaScript console of a browser also works. So it should work find but I need to find the right place to call :(
Handlers in DataGrid that I am using:
addLoadingStateChangeHandler
addRowCountChangeHandler
I also tried to call it at different places after the calls to add to DataGrid the data have been made. Again, the HTML select doesn't seem to have been added to the table. ie. when I call the JavaScript snipped right after a call to myListDataProviderObject.getList()addAll(my data).

Most likely you need to add a Scheduler call before calling your native method to make sure that the browser finished rendering the UI (e.g. your DataGrid) before this call is executed.

Related

Using GWTQuery to call Bootstrap JS collapse method

I want to replicate this jQuery call in gQuery to open all closed Accordion panels at once:
$('.panel-collapse:not(".in")').collapse('show');
I have tested this on my app and it works, however I am unable to successfully implement the same logic using gQuery to call the JavaScript collapse method:
#Override
public void onClick(ClickEvent event) {
GQuery.$(".panel-collapse").not(".in").trigger("collapse", "show");
}
What is the correct way to call JavaScript methods from gQuery?
Further Info - I have successfully tested this gQuery code in my onClick method to add a test class to the affected divs - so I am certain the selector part of the above query works:
GQuery.$(".panel-collapse").not(".in, .MENU").addClass("test");
Why you can't use collapse with GQuery
IIRC collapse() is not a jQuery API method. Maybe you're using Bootstrap JS, or some jQuery plugin that provides this functionality, but it's not one of the jQuery API methods and thus it's not provided by GQuery.
Why trigger wouldn't work either
GQuery, or GwtQuery, is not a wrapper for jQuery, but a full Java implementation of the jQuery API.
What this means is that, when you do something like this:
GQuery.$(".panel-collapse").not(".in").slideToggle();
You're not invoking jQuery's $(), not(), or slideToggle(); you are using a Java library to achieve the same result.
That's the reason why trying something like trigger("slideToggle") won't work: because a) slideToggle() is not an event, but a function; and b) GQuery doesn't make use of jQuery's JS functions.
Solution with GQuery
You can achieve the same "accordion" effect using the slideUp(), slideDown() and slideToggle() functions methods. To open all the collapsed elements, just calling slideDown() on them should work:
GQuery.$(".panel-collapse").not(".in").slideDown();
For full accordion effect, combine those with the toggleClass() and removeClass() methods to mark which elements are open / closed so you know which ones to toggle when clicked.
Solution with native collapse()
Now, if you don't mind the advice... GQuery is great, but the animations are far from being as smooth as in native jQuery. I guess the same happens with Bootstrap.
If you can (and I can't see a reason why you couldn't), just use JSNI to make a native call to collapse() like this:
private native void collapseAll() /*-{
$wnd.$('.panel-collapse:not(".in")').collapse('show');
}-*/;
This requires that you load jQuery (or Bootstrap) in your page, but since you said that invoking collapse() in plain JS worked, I guess that's your case.

Is there a way to know when the DOM of a partial view is ready after it is inserted into the DOM of your web page?

I'm writing an application using Knockout. I have subviews I'm inserting via jQuery's append() function. I'm using the text plugin for RequireJS to dynamically retrieve the HTML, then I'm using append() to attach it to an element in my web page:
$("#parentElement").append(theHTML);
After that, I need to bind "theHTML" to my ViewModel:
ko.applyBindings(myViewModel, $("#subViewElement")[0]);
It seems like the jQuery onDomReady() function is only used in the initial loading of the web page. Is there a way to make sure the DOM in "theHTML" is ready before calling "applyBindings" on it?

Reloading an iframe in GWT

I am currently working on a GWT project where I am displaying an HTML file within an iframe in my application. This HTML file is actually being written to as it is getting displayed, and I am hoping to be able to reload the frame so that the changes made to the HTML file are reflected on screen. I am able to do this two different ways that both work when running in development mode, however neither seem to work when the project is deployed.
The first method I tried was setting the frame's URL to itself:
frame.setUrl(frame.getUrl());
The second method I tried using JSNI:
public native void refresh() /*-{
if($doc.getElementById('__reportFrame') != null) {
$doc.getElementById('__reportFrame').src =
$doc.getElementById('__reportFrame').src;
}
}-*/;
When deployed, the frame gets displayed in a Window, and when the file is finished being written to, a call to either of these refresh methods is made, and the frame refreshes to contain the finished HTML file. When I am deployed, the call to refresh does not reload the contents of the frame, however if I bring up the frame's context menu (in Firefox), then go into 'This Frame', and click Reload, it successfully reloads the frame to contain the finished HTML file. I have tested this on multiple versions of Firefox without any luck.
Does anyone have any suggestions? Why would the behavior be different from one mode to the other?
Thanks.
wow, google is really fast with his search^^
You can use some JSNI to do this. Create a method such as
protected native void reloadIFrame(Element iframeEl) /-{
iframeEl.contentWindow.location.reload(true); }-/;
Then call it with your iFrame element
so your question you posted twice was already answerd here
http://groups.google.com/group/google-web-toolkit/browse_thread/thread/64aa7712890652d3
We had a requirement where one GWT application(parent) had another GWT application(child) loaded in an iframe. The child application had to refresh the iframe after it performs certain DB operations. We used JSNI to accomplish the same.
private native void refreshChild(String url)/*-{
$wnd.location.href=url;
}-*/
In case, if the child frame needs to be redirected to another webpage, the url can be modified accordingly.
We did try to use the reload() method, but it did not help.
The above piece of code, of course needs to be written in the child application.

Unable to call getValues() on an Ext Js FormPanel on initialization of a container Panel

I have an Ext Js panel that I am adding to my main TabPanel. The panel I am adding contains a FormPanel as one of it's items and inside the FormPanel I have a Name field. What I want to do is change the name of the Tab based on the name in the form field.
The problem is that if I call the FormPanel's getForm().getValues() inside of the panel's initComponent, I get the following javascript error:
Uncaught TypeError: Cannot read property 'dom' of undefined
If I do this outside of initComponent (e.g. on a button press) everything works fine. After doing some testing, I think the issue is that the FormPanel isn't actually rendered yet (and thus the dom doesn't exist), getValues() fails. However, I can't seem to figure out a way to get my FormPanel's values from the Panel on load.
I tried to listen for events. I tried:
this.detailForm.on('afterrender', function () { alert('test'); });
but doing this showed that AfterRender is called prior to the form actually being rendered (it's not visible on the screen). Changing the alert to my custom function handler produces the previous dom exception. I attempted to use the activate and enable events instead of afterrender, but even though the API says that FormPanel fires those events, the alert('test') never gets called.
I can't seem to find any way for my panel to get the inner FormPanel's values upon loading my panel. Does anyone have any ideas?
Using getFieldValues() in place of getValues() will collect values by calling each field instance's getValue() method instead of by reading from the DOM. This should allow you to get your values regardless of the form's rendered state.
I've got the same problems on one of my projects, I managed to fix it using the afterlayout event.
I'd give setting .deferredRender:false a try.
Ext.TabPanel.deferredRender
Probably best to roll out of your afterlayout changes, then test with just a straight deferredRender:false config item.
I believe the problem is caused because the inactive tabs are not rendered until they become active. In your scenario, you cannot get the values, because they don't exist until the tab is activated/shown.
Setting deferredRender:false will render the items within all tabs. There could be a performance hit by setting deferredRender:false, so testing you must do.
Hope this helps.

google wave: how did they make divs clickable

As we are facing GWT performance issues in a mobile app I peeked into Google Wave code since it is developed with GWT.
I thought that all the buttons there are widgets but if you look into generated HTML with firebug you see no onclick attribute set on clickable divs. I wonder how they achieve it having an element that issues click or mousedown events and seemingly neither being a widget nor injected with onclick attribute.
Being able to create such components would surely take me one step further to optimizing performance.
Thanks.
ps: wasnt google going to open source client code too. Have not been able to find it.
You don't have to put an onclick attribute on the HTML to make it have an onclick handler. This is a very simple example:
<div id="mydiv">Regular old div</div>
Then in script:
document.getElementById('mydiv').onclick = function() {
alert('hello!');
}
They wouldn't set the onclick property directly, it would have been set in the GWT code or via another Javascript library.
The GWT documentation shows how to create handlers within a GWT Java app:
public void anonClickHandlerExample() {
Button b = new Button("Click Me");
b.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
// handle the click event
}
});
}
This will generate an HTML element and bind a click handler to it. However, in practice this has the same result as using document.getElementById('element').onclick() on an existing element in your page.
You can hook functions to the onclick event using JavaScript. Here's an example using jQuery:
$(document).ready(function(){
$("#div-id").click(function(){
/* Do something */
});
});
If you're interested in optimizing performance around this, you may need to investigate event delegation, depending on your situation.
A click event is generated for every DOM element within the Body. The event travels from the Body down to the element clicked (unless you are using Internet Explorer), hits the element clicked, and then bubbles back up. The event can be captured either through DOM element attributes, event handlers in the javascript, or attributes at any of the parent levels (the bubbling or capturing event triggers this).
I'd imagine they've just set it in a .js file.
Easily done with say jQuery with $(document).ready() for example.