добавить класс в конкретный родительский div при щелчке по компоненту, созданному с помощью v-for

Я новичок в vuejs, вот что я хочу сделать: у меня есть список компонентов, каждый в div. Теперь, если я что-то сделаю с компонентом (то есть щелкну по нему). Я хочу добавить класс к родительскому div. Это то, что я делал до сих пор, код упрощен, просто чтобы показать, что я хочу сделать с простым случаем.

мой app.vue:

<div class="toggle-box" v-for="(name, index) in names" :class="classActive" :key="index">
    <app-comp :myName="name" :myIndex="index" @someEvent="doSomething"></app-counter>
</div>
data() {
    classActive: '',
    names: ['alpha', 'beta', 'gamma']
},
methods: {
    doSomething() {
        this.classActive === '' ? this.classActive = 'is-active': this.classActive='';
    }
}

компонент:

<div>
    <button @click="toggle">{{ myName }} - {{ myIndex }}</button>
</div>

props: ['myName', 'myIndex'],
methods: {
    toggle() {
        this.$emit('someEvent', index);
    }
}

что он делает: он создает 3 блока с классом «toggle-box» с кнопкой в ​​нем с меткой «имя - индекс». Когда я нажимаю кнопку, она генерирует событие «someEvent» с прикрепленным индексом. Родитель прослушивает это событие и переключает класс is-active в div с помощью класса toggle-box. Дело в том, что прямо сейчас, когда я нажимаю кнопку, он добавляет класс ко всем трем div. Вероятно, потому что нет разницы между тремя div для vuejs. Я знаю, что могу добавить индекс к событию и вызвать его с помощью $ event в родительском элементе, но как это использовать? Или, может быть, есть лучший способ добиться того, чего я хочу?

Спасибо за помощь.


person yangsunny    schedule 03.05.2019    source источник
comment
Не уверен, почему вы делаете classActive свойством приложения, а не компонентом.   -  person raina77ow    schedule 03.05.2019
comment
что ты имеешь в виду? Я знаю, что это может быть не лучший способ, не стесняйтесь его менять :-)   -  person yangsunny    schedule 03.05.2019


Ответы (1)


Есть несколько разных способов подойти к этому, но я думаю, что отправной точкой является размышление о том, как вы хотите представить это как данные, а не о том, как это отображается в пользовательском интерфейсе. Итак, модель перед просмотром.

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

Для аргументации предположим, что массив активных элементов является подходящей моделью для того, чего вы пытаетесь достичь. Возможно, это не так, но это простой пример.

So:

data() {
    return {
        activeNames: [],
        names: ['alpha', 'beta', 'gamma']
    }
},

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

Для метода toggle я был бы более склонен выдавать name, чем index, но вам лучше понять, какой из них лучше представляет данные. В моем примере это будет name:

methods: {
    toggle() {
        this.$emit('someEvent', this.myName);
    }
}

Затем в родительском компоненте мы добавим / удалим name из массива при срабатывании события. Для этого могут быть лучше другие структуры данных, я вернусь к этому в конце.

methods: {
    doSomething(name) {
        if (this.activeNames.includes(name)) {
            this.activeNames = this.activeNames.filter(item => item !== name);
        } else {
            this.activeNames.push(name);
        }
    }
}

Теперь у нас есть массив, содержащий активные имена, которые мы можем использовать для получения класса для этих div-оберток.

<div
    class="toggle-box"
    v-for="(name, index) in names"
    :class="{'is-active': activeNames.includes(name)}"
    :key="index"
>

Выполнено.

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

Вместо массива мы можем использовать объект с логическими значениями:

data() {
    return {
        names: ['alpha', 'beta', 'gamma'],
        activeNames: {
            alpha: false,
            beta: false,
            gamma: false
        }
    }
}

Во многих отношениях это более простая структура для работы с этим конкретным примером, но в конечном итоге мы дублируем имена в качестве ключей свойств. Если мы не заполним его таким образом, мы можем столкнуться с проблемами реактивности (хотя их можно решить с помощью $set).

Другой альтернативой является использование объектов для представления имен в первую очередь:

data() {
    return {
        names: [
            {name: 'alpha', active: false},
            {name: 'beta', active: false},
            {name: 'gamma', active: false}
        ]
    }
}

Я не могу судить, имеет ли такая структура данных смысл для вашего варианта использования.


Обновление:

Основываясь на том, что вы сказали в комментариях, я был бы склонен создать еще один компонент для представления переключателя. Каждый из них может хранить свое собственное active состояние, а не пытаться удерживать их все в родительском компоненте. Ваш v-for затем создаст экземпляры этого нового компонента напрямую. В зависимости от обстоятельств этот новый компонент может быть объединен с вашим исходным компонентом.

Здесь есть множество других соображений, из-за которых действительно сложно дать окончательный ответ. Если активное состояние необходимо знать за пределами компонента переключателя, то это совсем другой сценарий, чем если бы это было просто внутреннее состояние. Если одновременно может быть открыт только один переключатель (например, аккордеон), это также сложно, поскольку внутреннего состояния недостаточно.

person skirtle    schedule 03.05.2019
comment
прежде всего. Спасибо за ответ. В моем проекте компонент намного сложнее. скажем, поле, которое показывает некоторую информацию. при нажатии на компонент окно открывается и закрывается при повторном щелчке. Я просто хочу добавить стиль к div переключателя, чтобы показать, что поле в настоящее время открыто. Как я уже сказал, я новичок в vuejs, поэтому я не знаю, есть ли лучший способ сделать это, моя идея заключалась в добавлении к нему класса is-active. Однако сложно предварительно заполнить объект, поскольку количество переключателей является динамическим. this.activeNames = this.activeNames.filter (item = ›item! == name); это большая помощь. - person yangsunny; 03.05.2019
comment
Я обновил свой ответ, но суть проблемы по-прежнему заключается в правильном хранении данных. После того, как вы исправите форму и расположение данных, все остальное будет легко. Основываясь на вашем комментарии, похоже, что перемещение границ компонентов может быть лучшим вариантом, чем сложные структуры данных. - person skirtle; 03.05.2019