Quantcast
Channel: Extensible » Performance
Viewing all articles
Browse latest Browse all 2

Preparing for Ext 4.1 (Part 2)

0
0

In part 1 of this series I listed all of the major blog, video and forum resources related to the Ext JS 4.0 to 4.1 upgrade. In this post I’d like to dive into some of the specific changes I had to make while upgrading Extensible to 4.1, most of which are not explicitly listed anywhere as official breaking changes.  Some of my issues were due to overriding private code, but some were subtle changes in behavior or other things that cannot be considered “API changes” but affect the end result nonetheless.

Beware the Auto Layout

In my EventWindow class (the window used to edit calendar events), the layout simply stopped working as expected under 4.1. The form still rendered, but obviously Ext was not providing the correct layout logic as it had been previously.  It was looking something like this (note the fields overflowing the window body boundaries):

 After some fruitless debugging of resize events and layout logic, I finally realized that the window had no explicit layout set, and had been simply relying on the browser’s default DOM layout logic to size the form’s container (the window is fixed size, so this always worked as expected before). Even though it had been working previously, that was due more to luck than anything. Relying on auto layout is generally a no-no when dealing with Ext containers, and even though I know better, somehow this got missed.  This minor bug had been flying under the radar for quite some time and Ext 4.1′s revamped layout engine finally exposed it.

Ext.define('Extensible.calendar.form.EventWindow', {
    layout: 'fit',
    // etc.
});

Luckily the fix is very simple, just beware that if you’re still relying on auto layout in any containers, 4.1 may bite you!

Model.store -> Model.stores[]

In Ext 4.1 the previous Model.store : Object property is now Model.stores : Array. This is a simple change, and it’s documented in the existing API change notes, but I wanted to call it out because it’s an explicit breaking change that in fact impacted the calendar code.  I’m quite surprised to find this change in Ext 4.1 without a backwards compatibility patch frankly, especially as it seems that it would have been pretty easy to leave the existing property defaulted to the value of this.stores[0], which would always be valid for existing 4.0.x code.  It’s still beta as of this writing, so maybe someone on the core team will reexamine this before the final release…

Here’s an example of the calendar code that had to change. It takes a record generically and determines whether or not it needs to be added to the calendar’s event store.  I probably could have also checked the record’s phantom property instead, but the code worked just fine before:

onEventAdd: function(form, rec) {
    if (!rec.store) {
        this.store.add(rec);
        this.save();
    }
    this.fireEvent('eventadd', this, rec);
}

In fact this is the worst kind of error, because this code doesn’t actually cause a runtime error under 4.1 — it simply evaluates to true on every check, and thus leads to a silent regression (in this case, duplicate record adds). Ugh!

The fix is to either check rec.phantom, or perhaps rec.stores.length === 0.  In my case I still have to support the end user using 4.0.x as well so checking rec.phantom is the simplest fix. Since there is not one single way that this property could be used, it’s up to you to evaluate it if needed and fix it as appropriate to your own code!

Avoid Manual Creation of Component Elements

The exact issue I had was a bit of a random one, and not likely to affect most people specifically. However, the principle behind why this code broke under 4.1 is still very important to understand, as you might have a similar issue with the same root cause.

It is not uncommon in older versions of Ext, especially in custom components, to manually create the component’s underlying element during render if you need to customize the markup.  This particular bit of code, as happens a lot, simply got ported forward from some older code that’s always worked fine through version 4.0.7. Suddenly under 4.1, this component simply stopped rendering altogether, and it boiled down to this code in the constructor:

if (!this.el) {
    this.el = document.createElement('div');
}

If you’ve been keeping up with the rendering pipeline changes in 4.1, you’ll be aware that component markup is now batched in memory during render, and that DOM writes are managed and performed in bulk by the rendering engine. In the past, each component was free to independently create and insert its own markup into the DOM. However, under 4.1, you should no longer do this as it will not play nicely with the new rendering lifecycle.

The solution in my case was to simply delete that block of code and allow Ext to do the Element creation for me as part of its normal rendering process.  This logic was simply a leftover piece of code that was not needed, but did no harm (until 4.1).  If you really do need to customize markup, the appropriate place to look now would be the new beforeRender() method, or perhaps even better, customizing your component’s renderTpl (the preferred way to define custom markup under 4.x).

Fields Are Now Tables

I’m not exactly sure why this change was made now, though I assume it was mostly about squeezing every little extra bit of layout performance out of the browser. Tables allow you to line things up “for free” compared to adding overhead to calculate and resize divs to align labels, for example.  Fair enough.  However, there are a couple of things to watch out for here.  First, let’s see how the markup changed (note that the markup is abbreviated to focus mainly on the structure):

<div class="x-field" id="textfield-1018">
    <label class="x-form-item-label">My Field:</label>
    <div class="x-form-item-body"><input class="x-form-field" type="text" name="myfield" size="20" /></div>
</div>

<table class="x-field" id="textfield-1018" cellspacing="0" cellpadding="0">
    <tbody>
        <tr id="textfield-1018-inputRow">
            <td id="textfield-1018-labelCell" valign="top"><label class="x-form-item-label">My Field:</label></td>
            <td class="x-form-item-body" colspan="2"><input class="x-form-field" type="text" name="myfield" size="20" /></td>
        </tr>
    </tbody>
</table>

The first thing to note is that if you have written any custom CSS rules that rely on the previous structure (e.g. div.x-form-item-body) you may have to adjust them accordingly.

A much less common issue, but the one that affected me, relates to positioning custom elements inside a form field. This is commonly done with combo boxes, for example to add a custom icon for each item. This is exactly the case for my calendar picker custom combo, which displays a simple color icon to visually differentiate each calendar in the list. To achieve this, the containing element (previously the x-form-item-body DIV, now a TD, as highlighted above) must be position: relative (which it is) so that inner elements can be absolutely positioned.

In testing, only on Firefox, I was getting the result on the left after upgrading to Ext 4.1:

After some experimenting and Googling, I found that there’s an annoying bug wherein Firefox ignores positioning on table elements. Awesome! The only fix I could get to work was to make the offending element display: block, which feels like solving it with a sledgehammer, but I couldn’t get anything else to work reliably. At least I restricted my fix to Firefox and only to my widget by defining my override like so, to hopefully avoid creating unexpected issues downstream:

.x-gecko .ext-calendar-picker {
    display: block;
}

If you’re doing any kind of positioning inside of fields, this is definitely something to watch out for!

Double-Check Your Overrides

It’s very common to override existing Ext classes to patch issues or add functionality.  Because of that, this is really a general rule of thumb anytime you’re considering upgrading your Ext version — even typical minor versions can fix or alter something that might break or even remove the need for one of your overrides.

In the case of Extensible, this happened with an override to Ext.data.Reader required by the calendar components. Inside the extractData() method I have had to make the following fix for a while now, per the included comments:

// Assuming that the idProperty is intended to use the id mapping, if
// available, getId() should read from the mapped values not the raw values.
// Using the non-mapped id causes updates later to silently fail since
// the updated data is replaced by id.
//id = me.getId(node);
id = me.getId(values);

This is a pretty important fix — as the comments state, without it updates to data simply fail silently. Under 4.1, some internals of the Reader class were refactored, and some code was added to this function, without which the class no longer worked as expected. This was not an API change at all, yet because I’ve overridden private code, I now have to be extra careful to watch out for such internal changes!

To mitigate this you can often use tricks like createSequence or createInterceptor to work around offending methods AOP-stlye. Unfortunately though, sometimes the only solution is to override the entire method, in which case the only fix is to update the override accordingly (as in this case).

Is There More?

These have been a few of the various types of fixes I had to make in Extensible for Ext 4.1, but it’s not all of them. In the next part of this series, we’ll look at a couple of really tricky issues that required even more creative fixes! We’ll also discuss strategies for branching your fixes in order to support multiple versions of Ext simultaneously, a key requirement for anyone building custom components used by others.

See also: Part 1 | Part 3


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images