VUE 2 сортировка компонентов
Итак страница имеет
<div id="app"> <sort> <line uid="1"> бла бла бла — много html идет в слот </line> <line uid="2"> бла бла бла — много html идет в слот </line> <line uid="3"> бла бла бла — много html идет в слот </line> </sort> </div> В компоненте line кнопочка - наверх (вниз) по кнопочке улетает событие. Родитель (sort) ловит событие и меняет местами компоненты Вся эта колобаха генерится движком сайта. Вытаскивать все данные в Vue и там их сортить совсем не хочется. |
Это противоречит сути vue так-то. Именно отдельно данный и надо слать, не вижу проблем заменить этот мусор на json.
Но если оень хочется, то придётся ручками писать render функцию, ктоторая будет сортировать ноды упавшие в слот к <sort> |
Цитата:
Но локоть сильно близко. И вроде все дети есть ... и можно было бы их местами поменять... Или забить на эту задачу (и сделать смену на сервере в общем то админка) или приклеить туда jQuery и на нем махнуть местами два слоя... |
Кстати ты не можешь называть компонент "line" (если конечно не пошаманишь с конфигом Vue), т.к. такой элемент есть среди стандартных.
|
Цитата:
Поставить один элемент перед другим - одна строка. elem1.before (elem2) // elem2 поставить перед elem1 |
Можно вообще это всё обойти: использовать для sort - display:flex (row\coluum что надо) и проставлять детям order не трогая реальной разметки.)
|
С render функцией это можно сделать примерно так:
<div id="app"> <v-sort> <v-line uid="1">1 бла бла бла — много html идет в слот </v-line> <v-line uid="2">2 бла бла бла — много html идет в слот </v-line> <v-line uid="3">3 бла бла бла — много html идет в слот </v-line> </v-sort> </div> <script src="https://unpkg.com/vue@2"></script> <script> function addListener(options, type, listener) { if (!options || !type || !listener) return; if (!options.listeners) options.listeners = {}; if (!options.listeners[type]) options.listeners[type] = listener; else if (Array.isArray(options.listeners[type])) options.listeners[type].push(listener); else options.listeners[type] = [options.listeners[type], listener]; return options; } Vue.component('v-sort', { data() { return { keyCounter: 0, events: ['up', 'down'], children: [] } }, render(h) { return h('div', this.children.map( ({vNode, key}) => h('div', { key }, [vNode]) )); }, created() { this.children = this.getChildren(); }, methods: { getNewKey() { return this.keyCounter++; }, getChildren() { const vNodes = this.$slots.default?.filter(vNode => vNode.componentOptions); if (!vNodes?.length) return []; return vNodes.map(vNode => { const key = this.getNewKey(); this.events.forEach(event => addListener( vNode.componentOptions, event, (...args) => this[event](key, ...args) )); return { key, vNode }; }); }, findIndexByKey(key) { return this.children.findIndex(child => key === child.key); }, up(key) { const index = this.findIndexByKey(key); if (index < 1) return; this.children.splice(index - 1, 2, this.children[index], this.children[index - 1]); }, down(key) { const index = this.findIndexByKey(key); if (index === -1 || index > this.children.length - 2) return; this.children.splice(index, 2, this.children[index + 1], this.children[index]); } } }) Vue.component('v-line', { template: `<p> <button @click="$emit('up')">up</button> <slot/> <button @click="$emit('down')">down</button> </p>` }); new Vue().$mount('#app') </script> Конечно гораздо лучше было бы если бы не нужно было слушать события из подкомпонентов, а просто рисовать кнопки прямо из компонента sort.) |
Нереально круто!
В общем посмотрел я на твою колобаху и понял, мне проще переписать в стандартной модели ВУ. А вообще нереально круто. |
Aetae,
если я каким-то хитрым способом изменю порядок в this.children, что нужно сделать чтоб новые изменения вступили в силу? |
Вот чем мне не нравятся все фрейворки, так это тем, что простейшие вещи (почесать левое ухо) надо делать хрен знает как (правой ногой через спину)
|
рони, изменения массива this.children сами по себе запускают ререндер, этож vue. Если ты имеешь ввиду изменения в содержимом v-slot, то в задаче этого не стояло, но в принципе можно по beforeUpdate добавить алгоритм сопоставления позиций убирая отсутствующие и добавляя новые в конец, например.
voraa, есть множество способов сделать эту задачу на Vue красиво и аккуратно, просто делать её надо изначально на vue. Выглядеть будет это например так: <div id="app"> <v-line v-for="content in data" @up="up" @down="down">{{content}}</v-line> </div> Без всякого шаманства. Извращения нужны для извращённых случаев, которые идут в разрез с механизмом использования инструмента. Vue - работает с данными, которые элегантно мапит в разметку, а в задаче мы ему скармливаем готовую разметку и просим с ней разобраться. То что он вообще может что-то сделать с этим - это уже показатель. То что ты тут видишь - это advanced техники в основном для узких специфических задач авторов плагинов\библиотек. Так что не надо оскорблять божественный Vue.) P.S. Хотя вот тот же Angular - там да, много ереси. |
Цитата:
pop: ƒ mutator() push: ƒ mutator() reverse: ƒ mutator() shift: ƒ mutator() sort: ƒ mutator() splice: ƒ mutator() unshift: ƒ mutator() что делает mutator я не осилил. мне бы в ручную рендер запустить, я синтаксиса не знаю. могу конечно достичь нужного запуском без аргументов this.children.splice(); но наверняка есть что-то типа this.render(); |
Aetae,
так сработало this.$forceUpdate(); может есть ещё варианты для такого this.children[0] = {}; или в Vue так не делают? |
рони,
Vue.set(this.children, 0, {}); // или this.$set(this.children, 0, {}); В vue 3 с этим вопрос решён(используется Proxy), там можно напрямую, но тут мы говорили о Vue 2.) Собсно к реактивному отлову изменений массива по прямому присвоению до прокси было только два варианта: 1. В vue 2 отказались от этого в пользу splice и Vue.set, описав в документации, зато массив - всё ещё тот же самый массив, работают все методы массива, проверки на массив, сравнение === и.т.д. 2. В mobx сделали объект - обёртку над массивом, реактивно работает присвоение по номеру, но массив уже не массив и по === не сравнить, в функции ожидающие массив с гарантией не передашь и т.д. Требуется явно вызывать .toArray. |
Aetae,
:write: :thanks: |
Цитата:
|
Цитата:
Во всех руководствах этот вопрос стеснительно обходится. Дескать,возьмите фетч или аксиос, и загрузите данные и будет вам щастье. А со страницы как ему данные отправить? Какойнить приличный массивчик. Эдак 100 записей, каждая из которых маленькая картинка и с десяток полей? А никак. Можно упаковать эти данные пярмо в компоненты, но потом их не перетасуешь...... |
ANAGAMA, что значит "никак", лол.
Весь современный веб так и работает. API на сервере отдаёт массив вида [{ img: 'http://...', title: 'text', content: 'text' }, {...}]а фронт это дело рисует. |
Цитата:
<div id="app"> <ul> <li v-for="el in myarr">{{el.name}} - <span v-html="el.content"></span></li> </ul> </div> </body> <script> const myarr = [{name:'one', content:'<strong>one</strong>'}, {name:'two', content:'<strong>two</strong>'}, {name:'three', content:'<strong>three</strong>'}]; const vm = new Vue({ el: "#app", data: { myarr }, }); </script> |
Часовой пояс GMT +3, время: 19:06. |