Acentuação no Sort do DataGrid

Continuando o caso do Sort no DataGrid, que no post anterior em http://blog.dclick.com.br/2007/06/02/alterando-o-sort-default-do-datagrid-de-case-sensitive-para-case-insensitive/
onde foi relatado como se utilizar a propriedade caseInsensitive, chegamos agora no problema de tratamento de ordenação de palavras acentuadas com palavras não acentuadas. O sort executado, não consegue identificar, a partir do locale de países de língua portuguesa, que palavras acentuadas devem ser tratadas da mesma maneira que palavras sem acentuação para o caso de ordenação. Ou seja, árvore é igual a arvore quando mostramos em ordem num grid.

A solução para este caso, reside no fato da classe SortField ser responsável pela definição de qual função de comparação será usada em objetos do tipo string, para identificar a ordem. Assim com a simples extensão desta classe (ExtendedSortField), conseguimos indicar qual função de ordenação de strings deve ser usada internamente para o resultado esperado.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package sort
{
import mx.collections.SortField;
import mx.utils.ObjectUtil;
import mx.core.mx_internal;

use namespace mx_internal;

public class ExtendedSortField extends SortField
{

override mx_internal function initCompare(obj:Object):void
{
var isStringField:Boolean = false;

// if the compare function is not already set then we can set it
if (!usingCustomCompareFunction)
{
if (!numeric)
{
if (caseInsensitive)
isStringField = true;
else
{
// we need to introspect the data a little bit
var value:Object;
if (name)
{
try
{
value = obj[name];
}
catch (error:Error)
{
}
}
//this needs to be an == null check because !value will return true
//where value == 0 or value == false
if (value == null)
{
value = obj;
}

var typ:String = typeof(value);
switch (typ)
{
case "string":
isStringField = true;
break;
case "object":
if (!(value is Date))
{
isStringField = true;
var test:String;
try
{
test = value.toString();
}
catch (error2:Error) {}
if (!test || test == "[object Object]")
{
isStringField = false;
}
}
break;
}
}  // else
}
} // if

if (isStringField)
{
compareFunction = stringCompare;
}

super.initCompare(obj);
}

/**
* Pull the strings from the objects and call the implementation.
*/
private function stringCompare(a:Object, b:Object):int
{
var fa:String;
try
{
fa = name == null ? String(a) : String(a[name]);
}
catch (error:Error)
{
}

var fb:String;
try
{
fb = name == null ? String(b) : String(b[name]);
}
catch (error:Error)
{
}

var sa:String = "";
if (fa != null)
{
sa = String(mx.utils.ObjectUtil.copy(fa));
sa = removeSpecialChars(sa);
}

var sb:String = "";
if (fb != null)
{
sb = String(mx.utils.ObjectUtil.copy(fb));
sb = removeSpecialChars(sb);
}

return mx.utils.ObjectUtil.stringCompare(sa, sb, caseInsensitive);
}

/**
*
* @param value String de entrada que será modificada de acorde com a tabela ASCII extendida
* @return String alterada
*
*/
public static function removeSpecialChars(value:String):String
{
var patternTable:Object = new Object();
patternTable["A"] = /À|Á|Â|Ã|Ä|Å/g;
patternTable["E"] = /È|É|Ê|Ë/g;
patternTable["I"] = /Ì|Í|Î|Ï/g;
patternTable["O"] = /Ò|Ó|Ô|Õ|Ö/g;
patternTable["U"] = /Ù|Ú|Û|Ü/g;
patternTable["a"] = /à|á|â|ã|ä/g;
patternTable["e"] = /è|é|ê|ë/g;
patternTable["i"] = /ì|í|î|ï/g;
patternTable["o"] = /ò|ó|ô|õ|ö/g;
patternTable["u"] = /ù|ú|û|ü/g;
patternTable["C"] = /Ç/g;
patternTable["c"] = /ç/g;
patternTable["N"] = /Ñ/g;
patternTable["n"] = /ñ/g;

var result:String = value;
for (var letter:String in patternTable)
{
result = result.replace(patternTable[letter], letter);
}
return result;
}
}
}

Mas para que isso funcione, devemos antes preparar o sort do dataProvider do DataGrid, adicionando neste, conforme o usuário interage com a coluna em questão, os fields do tipo de nossa classe extendida (ExtendedSortField).

Estendendo também a classe DataGrid, adicionamos no seu construtor um listener para o evento DataGridEvent.HEADER_RELEASE.

1
2
3
4
5
6
7
8
9
10
11
public function ExtendedDataGrid()
{
// Como o listener foi criado com a mesma prioridade do listener da classe mãe,
// este deve ser adicionado ANTES da adição do mesmo da classe mãe,
// para que esta implementação seja executada da mesma forma, ANTES.
addEventListener(DataGridEvent.HEADER_RELEASE,
headerReleaseHandler,
false, EventPriority.DEFAULT_HANDLER, true);

super();
}

No handler desse evento, preparamos o dataProvider com o sort adequado.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
private function headerReleaseHandler(event:DataGridEvent):void
{
if (!event.isDefaultPrevented())
{
prepareGridColumnSort(this, dataProvider as ICollectionView,

event.dataField, !sortCaseSensitive);
}
}

/**
*
* @param grid Instância do DataGrid que será preparado para utilização do   caseInsensitive e Numeric
* @param gridDataProvider DataProvider do DataGrid, que será observado para criação da classe Sort
* @return DataProvider modificado, contendo a classe Sort e SortField definida para cada DataGridColumn do DataGrid
*
*/
private function prepareGridColumnSort(grid:DataGrid,    gridDataProvider:ICollectionView, dataField:String, caseInsensitive:Boolean=true):void
{
var sort:Sort;
var field:SortField;
var column:DataGridColumn;
var bFieldFound:Boolean = false;

if (gridDataProvider && dataField)
{
sort = gridDataProvider.sort;

if (sort == null)
{
sort = new Sort();
}

for each (field in sort.fields)
{
if (field.name == dataField)
{
field.caseInsensitive = caseInsensitive;
bFieldFound = true;
break;
}
}

if (!bFieldFound)
{
for each (column in grid.columns)
{
if (dataField == column.dataField)
{
field = new ExtendedSortField();
field.name = column.dataField;
field.descending = column.sortDescending;
field.caseInsensitive = caseInsensitive;
if (column.sortCompareFunction != null)
{
field.compareFunction = column.sortCompareFunction;
}
field.numeric = isDataFieldNumber(gridDataProvider, dataField);
sort.fields = [field];
break;
}
}
}

gridDataProvider.sort = sort;
}
}

/**
*
* @param gridDataProvider Instância do DataGrid no qual será testado o campo do tipo Number
* @param dataField Nome do campo que será testado
* @return Resultado boolean indicando se é Number ou não
*
*/
private function isDataFieldNumber(gridDataProvider:ICollectionView, dataField:String):Boolean
{
if (gridDataProvider && gridDataProvider.length  > 0 && dataField)
{
var obj:Object = gridDataProvider[0];
if (obj.hasOwnProperty(dataField) && ((obj[dataField] is Number) || (obj[dataField] is uint) || (obj[dataField] is int)))
{
return true;
}
}
return false;
}

Após estes passos, toda vez que o usuário clicar no cabeçalho de uma coluna, este evento será capturado primeiro pela nossa função handler que irá preparar o sort do dataProvider do DataGrid com nossa classe ExtendedSortField.

Esta por sua vez, sabe como comparar valores string acentuadas, retirando os caracteres especiais antes de efetuar a comparação.


Um comentário

  1. Gregui Shigunov em 30.jan.08 às 2:08 pm

    Show de bola!!!

Deixe Seu Comentário