Предотвращение дублирования, созданного Mithril, при сортировке элементов с помощью SortableJS

Я делаю инструмент для создания панели инструментов. Панель инструментов состоит из строк, в каждой строке есть поля. Поэтому я пытаюсь составить список списков элементов. Элементы могут перемещаться между списками. Списки можно сортировать между собой. Все сохраняется в одном объекте, который можно легко экспортировать в формате JSON. Прохладно.

_________          ______________
| A  B  |   move   | A  B  C  C |  
|-------|   C up   |------------|   
| C     |   ===>   |            |  
|_______|          |____________|

Если я перемещаю блок C в первую строку и вызываю перерисовку, блок C дублирует себя. Один C - это оригинальный ящик из второго ряда, а второй C ящик создан мифрилом из модели.

Дело в том, что я хочу просто переместить поле между строками, а не копировать его.

Я пробовал играть с onbeforeupdate и m.redraw(), но это не решение, потому что мне нужен рабочий цикл перерисовки.

class Grid {

    private boxMap = new Map<TDashboardBox, IEditor>();

    constructor(
        public readonly dashboard: DashboardDAO,
        private readonly editorFactory: IBoxFactory
    ) {
        for (const row of dashboard.dashboard.config) {
            for (const box of row) {
                this.boxMap.set(box, editorFactory.create(box.title, box))
            }
        }
    }

    public boxView(box: TDashboardBox): Vnode {
        return m(this.boxMap.get(box) || null);
    }

    public rowView(row: TDashboardRow): Vnode[] {
        return row.map(b => m('.Box', {}, this.boxView(b)));
    }

    public view(): Vnode {
        return m('.Grid', {},
            this.dashboard.dashboard.config.map(r => m('.Row',  {}, this.rowView(r)))
        );
    }

    public oncreate(vnode: VnodeDOM): void {
        Sortable.create(<HTMLElement>vnode.dom, {
            group: "rows",
            animation: 150,
            onEnd: (e: SortableEvent) => {
                this.dashboard.swapRows(e.oldIndex, e.newIndex);
            },
        });

        const rows = vnode.dom.getElementsByClassName('Row');
        for (let i = 0; i < rows.length; i++)
        {
            const row = <HTMLElement>rows[i];
            Sortable.create(row, {
                group: "boxes",
                animation: 150,
                onEnd: (e: SortableEvent) => {
                    const children = e.from.parentElement.childNodes;
                    // Search row indexes of "from row" and "to row"
                    let fromIndex, targetIndex;
                    for (let j = 0; j < children.length; j++) {
                        if (children[j] === e.from) {
                            fromIndex = j;
                        }
                        if (children[j] === e.to) {
                            targetIndex = j;
                        }
                        if (fromIndex !== undefined && targetIndex !== undefined) {
                            break;
                        }
                    }
                    this.dashboard.moveBox(fromIndex, e.oldDraggableIndex, targetIndex, e.newDraggableIndex);

                    m.redraw();
                },
            });
        }
    }
}

ИЗМЕНИТЬ 1

Скопированное поле может быть удалено в обратном вызове onEnd(). Это решает проблему копирования.

Если я поменяю местами блоки в одной строке, они поменяются местами в модели. Но на экране после перерисовки они меняются местами обратно.

ИЗМЕНИТЬ 2

Я нашел решение какое-то решение. Это почти там.

// End of onEnd() event
// if boxes moved, then...
m.render(vnode.dom, m(this)); // redraw Grid scratch

Я просто выбрасываю весь HTML-код Grid и позволяю мифрилу восстановить его.

Но сейчас в Grid просто HTML без событий, без цикла перерисовки.


person Vladimír Mlázovský    schedule 15.09.2020    source источник


Ответы (1)


Хорошо, я понял это. Это не лучшее решение в своем классе, но оно работает хорошо.

Я создаю функцию обратного вызова, передаваемую компоненту Grid из его родительского компонента. После перемещения ящиков эта функция обратного вызова просто уничтожает сетку и создает новую.

родительский компонент

public recreateGrid(): void {
    this.grid = new Grid(
        this.editor.currentDashboard,
        this.factory,
        () => this.recreateGrid()
    )
}
...

public view(): Vnode { // example of view method 
     return m('.Layout.selected-dashboard', {}, [
         ... // some other components
         m(this.grid),
     ]);
}

Конструктор сетки

constructor(
    public readonly dashboard: DashboardDAO,      // model
    private readonly editorFactory: IBoxFactory,  // boxes factory
    private readonly onBoxMoved: ()=>void,        // callback       <--
    ) { ... }
person Vladimír Mlázovský    schedule 17.09.2020