PDA

Просмотр полной версии : Проблема с ComboBox в DataGrid


Skabeika
03.08.2009, 19:36
Как можно реализовать редактирование поля в DataGrid с помощью ComboBox? Причем данные для этого поля должны браться из другой таблицы.

Вот код msxml:

<mx:DataGrid id = "booksAdmin"
width = "100%"
height = "100%"
editable = "{editableBookCh.selected}"
dataProvider = "{bookAdminArray}"
creationComplete = "booksAdmin.addEventListener(DataGridEvent.ITEM_EDIT_END, editBooksAdminCellEnd)">

...

<mx:DataGridColumn id = "izdatBooksAdminCol"
labelFunction="izdat_labelFunc"
dataField = "izdat"
editorDataField="id"
headerText = "Издательство">
<mx:itemEditor>
<mx:Component>
<mx:ComboBox editable="false"
dataProvider="{outerDocument.bookAdminIzdatArray}"
labelField="name"

/>
</mx:Component>
</mx:itemEditor>
</mx:DataGridColumn>
...

В приведенном выше примере в массиве bookAdminIzdatArray хранятся данные из родительской таблицы, этот массив заполняется динамически с помощью HttpService. Во время редактирования в ComboBox появляются записи родительской таблицы, но я никак не могу сделать так, чтобы в DataGrid отображался не первичный ключ родительской таблицы, а элементы массива bookAdminIzdatArray. Кроме того, после редактирования пропадает даже этот первичный ключ и не отображается ничего.

wvxvw
03.08.2009, 19:47
Ну так покажите, что вы в editBooksAdminCellEnd делаете - это ж как бы самое главное :)

Skabeika
03.08.2009, 19:49
editBooksAdminCellEnd работает с текстовыми полями нормально (я его не сам придумал а списал с какого-то примера, не помню уже какого)


private function editBooksAdminCellEnd(e:DataGridEvent):void {
var rowIndex:int = e.rowIndex;
var columnIndex:Number = e.columnIndex;

var vo:* = bookAdminArray[rowIndex];

var col:DataGridColumn = booksAdmin.columns[columnIndex];
var newvalue:String = booksAdmin.itemEditorInstance[col.editorDataField];

if (newvalue == "") {
booksAdmin.itemEditorInstance[col.editorDataField] = vo.name;
return;
}

if (newvalue == vo.name) {
return;
}

CursorManager.setBusyCursor();

var parameters:* = { "method": "Update", "id": vo.id, "new": newvalue }

gateway = new HTTPService();
gateway.url = BOOK_URL;
gateway.method = "POST";
gateway.useProxy = false;

gateway.request = parameters;

gateway.addEventListener(ResultEvent.RESULT, resultUpdateBooksHandler);
gateway.addEventListener(FaultEvent.FAULT, faultUpdateBooksHandler);

gateway.send();
}


Кстати сейчас только я подумал что в параметрах сервису нужно передавать еще и колонку, но это поправимо. А вот как быть с ComboBox, понятия не имею

Почти все похожие примеры в google используют собственные компоненты, не существует ли метода попроще? И можно ли такое сделать с помощью компонентов?

wvxvw
03.08.2009, 19:56
Пример почти гарантировано отсюда ;) 3-й или второй...
http://livedocs.adobe.com/flex/3/html/help.html?content=celleditor_8.html
Только разница в том, что у вас редактирование происходит асинхронно - показывайте, что в resultUpdateBooksHandler() находится.
var parameters:* = { "method": "Update", "id": vo.id, "new": newvalue };
parameters - должны быть типа Object, а не *.
vo технически может быть типа *, но лучше не допускать такию ситуацию.

Skabeika
03.08.2009, 19:58
Пример почти гарантировано отсюда ;) 3-й или второй...
http://livedocs.adobe.com/flex/3/html/help.html?content=celleditor_8.html
Только разница в том, что у вас редактирование происходит асинхронно - показывайте, что в resultUpdateBooksHandler() находится.

Собственно вот это:)


private function resultUpdateBooksHandler(e:ResultEvent):void {
CursorManager.removeBusyCursor();
}

wvxvw
03.08.2009, 20:03
Ну так а вам в ней как раз и нужно обновить данные... а вы в ней просто ничего не делаете... :)

Skabeika
03.08.2009, 20:07
А все остальное правильно? Просто я ни разу с Flex не работал еще. Что-то мне подсказывает что нужно обработать событие change в ComboBox и как-то установить свойство selected (и то и другое у ComboBox). Но я с ними сколько не игрался так и не добился успеха

Добавлено через 10 минут
Не подскажите как можно обновить данные?

wvxvw
03.08.2009, 20:24
Мммм... itemEditor сам уже подписан на "change", так что вы неявно на него подписались. Просто вы никогда itemEditorInstance не кастуете к ComboBox, поэтому вам это может быть не заметно.
т.е. если ваша функция editBooksAdminCellEnd вообще вызывается, то вы подписались на "change" и эта часть сработала нормально. Другое дело, что создавать каждый раз по экземпляру HTTPService - это как-то многовато, лучше было бы создать один экземпляр и использовать его с любым итем-эдитором.
Ну, и как бы вы самое главное не сделали - когда приходит результат от сервиса - вам надо эти изменения отразить в дата-провайдере, а вы этого никогда не делаете.

<?xml version="1.0" encoding="utf-8"?>
<!-- view.ValueTable -->
<mx:Panel
xmlns:mx="http://www.adobe.com/2006/mxml"
title="Skill XML"
horizontalAlign="center"
width="400" height="300"
>
<mx:Metadata>
[Event(name="valueUpdate", type="flash.events.Event")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import flash.events.Event;
import mx.controls.listClasses.IDropInListItemRenderer;
import mx.controls.TextInput;
import mx.events.DataGridEvent;
import mx.events.DataGridEventReason;
import mx.managers.PopUpManager;

private function counter(input:Object, input2:Object):String
{
var i:int;
var ri:int;
super.data.(contains(input) ? (ri = i) : i++);
return (ri + 1).toString();
}

private function editValueBegin(event:DataGridEvent):void
{
event.preventDefault();
dg.createItemEditor(event.columnIndex, event.rowIndex);
(dg.itemEditorInstance as IDropInListItemRenderer).listData =
(dg.editedItemRenderer as IDropInListItemRenderer).listData;
dg.itemEditorInstance.data = dg.editedItemRenderer.data;
(dg.itemEditorInstance as TextInput).setFocus();
}

private function editValueEnd(event:DataGridEvent):void
{
if (event.reason == DataGridEventReason.CANCELLED) return;
var newData:String =
(dg.itemEditorInstance as TextInput).text;
dg.editedItemRenderer.data = dg.itemEditorInstance.data;
if (newData == "")
{
event.preventDefault();
(dg.itemEditorInstance as TextInput).errorString =
"This field is mandatory!";
return;
}
else
{
(dg.itemEditorInstance as TextInput).text = newData;
dispatchEvent(new Event("valueUpdate"));
}
}

]]>
</mx:Script>
<mx:DataGrid
id="dg"
dataProvider="{data}"
width="100%"
height="100%"
editable="true"
itemEditBegin="editValueBegin(event)"
itemEditEnd="editValueEnd(event)"
>
<mx:columns>
<mx:DataGridColumn
headerText="Level"
labelFunction="{counter}"
editable="false"
width="40"
resizable="false"
/>
<mx:DataGridColumn
headerText="Value"
editable="true"
dataField="@val"
>
<mx:itemEditor>
<mx:Component>
<mx:TextInput restrict="0-9"/>
</mx:Component>
</mx:itemEditor>
</mx:DataGridColumn>
</mx:columns>
</mx:DataGrid>

<mx:Button label="Close" click="PopUpManager.removePopUp(this)"/>
</mx:Panel>

Вот пример где-то был. Только тут нет асинхронного изменения данных, оно у меня происходит в этой строчке:
dg.editedItemRenderer.data = dg.itemEditorInstance.data;
А вам прийдется записать, какая позиция редактировалась, когда отправился запрос, и когда ответ на запрос будет получен, заменить эту позицию новыми данными.

Skabeika
03.08.2009, 20:31
Если создать один экземпляр, то скорее всего придется обнулять handler-ы каждый раз (кода ненамного меньше). У меня вылезало какое-то переполнение буфера из-за этого.

Дело в том, что у меня даже не отображается то что надо, без редактирования. Вроде бы задал свойства dataField, editorDataField. Тогда почему не отображается соответствующая запись из списка, а значение первичного ключа. Почем не работает двунаправленное связывание???

wvxvw
03.08.2009, 20:41
Двунаправленое связывание - фича новая, и не везде работает, как раз например в аргументах сервиса - не работает.

Нет, как раз не прийдется обнулять хендлеры. Их вполне можно использовать многократно.

вы видите первичное значение потому, что вы никогда не применяете изменения, вы получаете ответ от сервиса - но в коде никак на это не реагируете, т.е. просто ничего не меняете - вам нужно в resultUpdateBooksHandler записать новое значение туда, где вы его до запроса хотели поменять.

Skabeika
03.08.2009, 20:49
За пример спасибо, сейчас буду изучать его.

Дело в том что я вижу первичные ключи до редактирования. То есть мне надо еще что-то дописывать в resultBooksAdminHandler?


private function resultBooksAdminHandler(e:ResultEvent):void {
booksAdmin.enabled = true;
bookAdminArray.removeAll();

var xmlData:XML = e.result as XML;

for each (var row:XML in xmlData.row) {
var temp:* =
{
"id": row.id,
"name": row.name,
"tom": row.tom,
"author": row.author,
"coauthor": row.coauthor,
"year": row.year,
"izdat": row.izdat,
"mesto": row.mesto,
"cat": row.cat
};
bookAdminArray.addItem(temp);
}

}


То есть автоматически подставлятся не будет текстовое значение из вспомогательного списочного массива вместо значения первичного ключа, который находится в основном массиве?

в приведенном выше методе происходит заполнение источника данных для DataGrid

wvxvw
03.08.2009, 21:01
Нет, автоматически не будет подставлятся, вам это нужно описать в labelFunction например. А еще лучше - непосредственно модифицировать датапровайдер грида, т.как грид повторно использует итем-рендереры, и если у вас появится скроллинг в гриде - комбобоксы начнут показывать неизвесно что, т.как они не будут создаваться по-новой, а просто последний удаленный будет использоваться для отображения нового.