Extjs + D3: render conflict - extjs

I'm digging into something really interesting when combining Extjs and D3.
In general, ExtJS renders the main frame for the whole page and D3 to render a dynamic graph in certain div in that frame.
As the rendering logic in Extjs is quite complicated, the place where I put the D3 render logic seems crucial.
Here I've tried two:
1) put into 'initComponent'
[ExtJS]
Ext.define('EDS.view.selector.Container', {
extend: 'Ext.panel.Panel',
alias : 'widget.selectorcontainer',
initComponent: function(){
renderSelectorOrgView();
}
}
[D3]
function renderSelectorOrgView(divId, divHeight, divWidth) {
var svg = d3.select("#" + divId).append("svg");
....
}
The problem is that it just doesn't work since during "initComponent", the div is not completely generated.
2) put into global Ext.onReady()
This turned out that D3 can only select empty result. I think the reason is similar to 1)
3) put into onRender()
Logically this should work alright. And in fact D3 can get the div and its attr perfectly. However the problem is, the ExtJS render process is totally ruined by this code. The whole layout is corrupted. So am I missing anything important in onRender()?
Ext.define('EDS.view.selector.Container', {
extend: 'Ext.panel.Panel',
alias : 'widget.selectorcontainer',
layout: 'fit',
onRender: function(){
//// Render D3
// Selector
console.log("onRender");
console.log(this.height);
console.log(this.width);
var divId = Ext.getCmp('selector-organization').id;
var divHeight = Ext.get(divId).getHeight();
var divWidth = Ext.get(divId).getWidth();
console.log(divHeight);
console.log(divWidth);
renderSelectorOrgView(divId, divHeight, divWidth);
},
}

Best way to make your d3 code work with extjs is to keep the d3 code in a different html all together. Then in your extjs panel use autoLoad config and give the url of the html.
{
xtype : 'panel',
title : 'd3Graph',
autoLoad : { url : 'd3code.html', scripts : true} // your HTML file containing d3 code.
}
I have tried this and it works for me without any errors.
Another way is to use contentEl config and give the name of the div in the config where your d3 visualization is being rendered.

If it is possible to render D3 object as a string of HTML markup, you can include that string in the Panel template. Another way is to let Ext JS components render themselves as usual, and then inject new markup in boxready event handler. Either way will avoid rendering conflicts.

You must add this.callParent(arguments); in your onRender. Here, you're completely replacing the Panel's own rendering logic, that's why it ends up broken.
You can also see this and this questions where a similar topic is discussed.

Related

Trying to get class/id value on tabbed Panel tab section

For test automation purposes, I need a class/id value on the tab bit (whatever you call it..) in a tabbed pane.
I have tried the following AAA,BBB,CCC and DDD, and none of these appear in the class in the DOM level.
var panel1 = Ext.create('Ext.panel.Panel');
panel1.cls = 'AAA'
panel1.itemCls = 'BBBB'
var panel2 = Ext.create('Ext.panel.Panel');
Ext.create('Ext.tab.Panel', {
cls:'CCC',
itemCls:'DDD',
renderTo: document.body,
items: [panel1,panel2]
}
);
I know its somehow possible. Anyone have an idea??
http://jsfiddle.net/GQULg/9/
OK just figured this out. I need to set a tabConfig object on the panel or container that goes inside the tabbed panel.
var panel1 = Ext.create('Ext.panel.Panel');
panel1.tabConfig = {
cls: 'myTab'
};
The tabConfig object holds the cls (class values) that will populate the tabs on the top.
If you intend to use selenium: other people stated that selenium uses plain JavaScript commands and that best would be to also use Ext.getCmp(), Ext.ComponentQuery()... from selenium.
Why? Because it does not need any special changes to your production code!

How to use Select2 with Backbone Marionette views outside the DOM

I'm trying to integrate the Select2 widget with Backbone Marionette views. My simple setup uses a Marionette.CollectionView to create and handle the select tag and Marionette.ItemViews to render the option tags.
That basically looks like this:
SelectCollectionView = Backbone.Marionette.CollectionView.extend({
itemView : SelectItemView,
tagName : "select",
onRender : function() {
this.$el.select2();
},
onClose : function() {
this.$el.select2("destroy");
}
}
SelectItemView = Backbone.Marionette.ItemView.extend({
tagName : "option",
render : function() {
// create the needed option tags
}
}
As you can see, I have to call the Select2 initialize and destroy methods upon render and close to have the needed additional tags added to the DOM.
This setup works very well, as long as the view handling the select tag (SelectCollectionView) has already been added to the DOM. If that is not the case, Select2's additional tags get lost, as they are not part of SelectCollectionView's $el and thus not added to the DOM.
I wonder how to elegantly solve this? One could add an extra div container and render everything inside it, but that would produce extra code for the script and the DOM. It also makes my view less versatile. I just hope for a better solution I didn't think of.
As you suspect, you are going to need a containing div to surround your template. Fortunately, this is really simple. Instead of what you currently have:
SelectCollectionView = Backbone.Marionette.CollectionView.extend({
tagName : "select"
Get rid of tagName, and in your template (I'm assuming you use Handlebars or Underscore or something like that), define your HTML:
template.html
<select class="whatever-you-want"></select>
Then in your view:
SelectCollectionView.js
SelectCollectionView = Backbone.Marionette.CollectionView.extend({
template: _.template(templateHtml)
Marionette will automatically wrap a div around your <select>, and then all of the extra markup that select2 adds will be safely contained within the view's $el.
It took me some time to figure it out, but I used to solve it this way:
var Select2View = Mn.View.extend({
template: _.template('<select class="form-control"><option></option></select>'),
onRender:function() {
this.$el.find('select').select2();
}
});
The important part here is
this.$el.find('select').select2();
which select the select tag, inside the element. But I don't use collection view in my example. If you need to fetch data you can use:
this.$el.find('select').select2({data: ['1','2','3','4']});
Also select2 provide very good API for manipulating select items during runtime.

Sencha Touch 2 - Text Overlapping Issue

I'm a new developer in Sencha Touch 2 and I'm trying to create my first application using its provided MVC architecture. I find issues with toolbar/titlebar text overlapping when navigating between pages. Take a look at these screenshots:
Example 1
Example 2
I'm not pretty sure what's happening out there. I am using animateActiveItem and routing method to move across my application.
Users.js controller file, login method
// Ajax code here, not included here
// on ajax success:
this.redirectTo("login");
Routes.js controller file
routeLoginPage: function() {
console.log("routeLoginPage");
Ext.Viewport.animateActiveItem({ xtype: "loginpage" }, { type: "slide", direction: "left" });
},
Has anybody really faced a problem like this? I have no idea what to do right now as I was trying to resolve this issue for 2 days+.
EDIT
Basically I need to move across the pages defined as views. I define each view in different file containing properties: extend, requires, alias, config and methods defined by me. Every config property has titlebar attached as its first item.
When I'm trying to change page, I load another view by controller command which changes address hash. Routes controller then fires an animateActiveItem method which loads another View (defined previously as xtype by alias property).
I was using Miami Coder's Tutorial (miamicoder.com/2012/how-to-create-a-sencha-touch-2-app-part-1/) to learn Sencha Touch basics.
I think you mean title bar and not toolbar...
Use navigation view to navigate between views instead of Ext.Viewport.animateActiveItem
It is a better method. For using navigation view use this guide in sencha docs...
Sencha has a steep learning curve so be ready for frustrations like this...
Navigation View Guide
You can add your required views in one panel class and enable the required view using
mainclass.setActiveItem(0)
or else use the navigation view
{
xtype: 'navigationview',
id: 'navView',
navigationBar: {
hidden: true
}
}
The above code will hide the title bar generated by navigation view... Now you need to define your own titlebar like so
{
xtype: 'titlebar',
title: 'title',
items: [
{
xtype: 'button',
text: 'back',
listeners: [
{
fn: function(button){
Ext.getCmp('navView').pop();//this will pop the view and show previous view
},event: 'tap'
}
]
}
]
}
Hope it helps...

setting 'minWidth' default config for button in ext js 4

i'm currently trying to get my head around the theming capabilities of ext js 4 and am having some trouble with the styling of buttons.
setting button dimensions isn't quite possible using the scss variables provided, but there are some javascript config options for this. as i don't want to have to apply these settings to every button instance, i was looking for a way of setting 'config defaults' for the button class.
the following approach worked out well when setting a default height:
Ext.button.Button.prototype.height = 15;
unfortunately, this seems not to be possible for the 'minWidth' config:
Ext.button.Button.prototype.minWidth = 1; //nope, sorry
is there a way of setting a default value for 'minWidth'?
short answer
Ext.override(Ext.panel.Panel, {
minButtonWidth: 1
});
Ext.override(Ext.button.Button, {
minWidth: 1
});
longer answer
setting Ext.button.Button.prototype.minWidth = 1; is correct (better use the override mentioned in the short answer), but ignored by ExtJs internal handling of setting the default minWidth of 75 of Ext.panel.Panel (see bridgeToolbars() function). So if you would use a button outside of a Panel Toolbar, your initial try would work.
Also, there is a hardcoded minWidth of 75 of buttons in the makeButtons() function of Ext.window.MessageBox.
In my opinion you should open a bugreport #sencha, because there should be a themable, more global setting for this.
update: this was tested with ExtJS 4.1
If you want to change it for every single button with in the framework. Use the override system.
app/overrides/Button.js
Ext.define('App.overrides.Button', {}, function() {
Ext.override(Ext.button.Button, {
minWidth: 20
});
});
app.js
Ext.onReady(function() {
/*
* Load Overrides
*/
Ext.require([
'App.overrides.Button'
]);
});
var app = Ext.Application({.....
I'm not sure how sensible this is however as things like tabs extend from button and it might have some adverse effects. But thats how it's done any how.

EXTJS ReRendering of the Grid with Datastore

I have a grid made with ExtJS, and I render it to a div that I define, when a refresh is requested I simply "empty" the div using JQuery and then re-render the grid creating new objects each time:
var grid = new xg.GridPanel({
store: store,
columns: [
...
renderTo: 'db-grid'
And then to empty the div I use this:
$("#db-grid").empty();
This works well for like 3 or 4 refreshes, and then it seems to load really slowly, I imagine this is because it re-creates all these objects again, the only thing that changes each time is the "store." I acquire that through an AJAX request, is there a way to refresh this without creating a new grid each time?
I'm pretty new to ExtJS, and I'm making a bit of a transition here from JQuery, which is why I'm using the "empty()" function from JQuery, if you know of a ExtJS alternative, I would be glad to use that instead,
Thanks!
Take a look at Store's load(Object options) and reload(Object options) methods at http://dev.sencha.com/deploy/dev/docs/.
An example of this functionality can be seen in Ext.PagingToolbar. This class contains a button that refreshes a Grid and its Store, without destroying the Grid each time:
// private
doLoad : function(start){
var o = {}, pn = this.getParams();
o[pn.start] = start;
o[pn.limit] = this.pageSize;
if(this.fireEvent('beforechange', this, o) !== false){
this.store.load({params:o}); // here is the call you're interested in
}
},
doLoad() is then simply called from the method doRefresh():
doRefresh : function(){
this.doLoad(this.cursor);
},
I included this code for the sake of a complete example, but really all you need to worry about is the load() or reload() methods. Make sure to read the API to see a full list of arguments that can be passed into the methods.

Resources