单击元素时切换元素的class(或更改样式),Vuejs

Toggle class(or change style) of element when element in clicked, Vuejs

我获取数据的方式有点复杂。我有存储数据的“tweets”数组,每条推文都是一张卡片,单击卡片时我成功更改了样式(markTweet 函数),但每条推文也有回复,显示与推文相同(每个回复都有自己的卡片)。我从服务器获取数据的方式:

let replies = []
for(const tweet of tweets) {
    let reply = await SQL('SELECT * FROM tweet_replies WHERE tweet_replies.conversation_id = ?', tweet.tweet_id)
    replies.push(reply)
}
    
const data = {
    tweets: tweets,
    page: parseInt(currentPage),
    numberOfPages: arr,
    replies
}

然后我在 vue 中有组件。您可以看到回复作为 tweetReplies 存储在每条推文的 tweets 数组中。 在 markReply 函数中,我成功地将 id 添加到数组中。

<template>
  <div class="container-full">
    <div class="tweets-container">
      <div
        v-for="(tweet, i) in tweets"
        :key="tweet.id"
      >
        <div
          class="tweet-card"
          :class="{ selected: tweet.isSelected }"
          @click="markTweet(tweet.tweet_id, i)"
        >
          <div class="text">
            <p
              v-html="tweet.tweet_text"
            >
              {{ tweet.tweet_text }}
            </p>
          </div>
        </div>
        <div class="replies">
          <div
            v-for="(reply, index) in tweet.tweetReplies"
            :key="reply.tweet_id"
            @click="markReply(reply.tweet_id, index)"
          >
            <div class="tweet-card tweet-reply">
              <div class="text">
                <p>
                  {{ reply.tweet_text }}
                </p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
import { getUserToken } from '@/auth/auth'
import moment from 'moment'
import { BFormTextarea, BButton, BFormSelect } from 'bootstrap-vue'

export default {
  components: { BFormTextarea, BButton, BFormSelect },
  data() {
    return {
      tweets: [],
      tweetActionIds: [],
      categories: [],
    }
  },
  beforeMount() {
    this.getTweets()
  },
  methods: {
    getTweets() {
      this.tweets = []
      const API_URL = `${this.$server}/api/twitter/tweets`

      const params = {
        token: getUserToken(),
        page: this.$route.query.page,
        newCurrentPage: newCurrent,
      }
      axios.post(API_URL, null, { params }).then(res => {
        this.currentPage = res.data.page
        this.numberOfPages = res.data.numberOfPages

        if (res.data) {
          res.data.tweets.forEach(tweet => {
            const tweetData = {
              id: tweet.id,
              tweet_id: tweet.tweet_id,
              tweet_text: htmlText,
              tweet_text_en: htmlTextEn,
              twitter_name: tweet.twitter_name,
              twitter_username: tweet.twitter_username,
              added_at: moment(String(tweet.added_at)).format(
                'MM/DD/YYYY hh:mm',
              ),
              URL: tweet.URL,
              isSelected: false,
              tweetReplies: [],
            }
            this.tweets.push(tweetData)
          })
          res.data.replies.forEach(reply => {
            reply.forEach(r => {
              this.tweets.forEach(tweet => {
                if (tweet.tweet_id === r.conversation_id) {
                  tweet.tweetReplies.push(r)
                }
              })
            })
          })
        }
      })
    },
    markTweet(tweetId, i) {
      const idIndex = this.tweetActionIds.indexOf(tweetId)
      this.tweets[i].isSelected = !this.tweets[i].isSelected
      if (this.tweetActionIds.includes(tweetId)) {
        this.tweetActionIds.splice(idIndex, 1)
      } else {
        this.tweetActionIds.push(tweetId)
      }
    },
    markReply(replyId) {
      const idIndex = this.tweetActionIds.indexOf(replyId)
      if (this.tweetActionIds.includes(replyId)) {
        this.tweetActionIds.splice(idIndex, 1)
      } else {
        this.tweetActionIds.push(replyId)
      }
    },
  },
}
</script>

我尝试在数据中添加 replySelected 然后当在 markReply 中触发点击时我将 replySelected 更改为 true,但是随后选择了推文的每个回复,这不是我想要。

如果我理解正确的话,请尝试像下面的片段:

const app = Vue.createApp({
  data() {
    return {
      tweets: [{id: 1, tweet_id: 1, isSelected: true, tweet_text: 'aaa', tweetReplies: [{tweet_id: 11, tweet_text: 'bbb'}, {tweet_id: 12, tweet_text: 'ccc'}]}, {id: 2, tweet_id: 2, isSelected: false, tweet_text: 'ddd', tweetReplies: [{tweet_id: 21, tweet_text: 'eee'}, {tweet_id: 22, tweet_text: 'fff'}]}],
      tweetActionIds: [],
    }
  },
  methods: {
    markTweet(tweetId, i) {
      const idIndex = this.tweetActionIds.indexOf(tweetId)
      this.tweets[i].isSelected = !this.tweets[i].isSelected
      if (this.tweetActionIds.includes(tweetId)) {
        this.tweetActionIds.splice(idIndex, 1)
      } else {
        this.tweetActionIds.push(tweetId)
      }
    },
    markReply(replyId) {
      const idIndex = this.tweetActionIds.indexOf(replyId)
      if (this.tweetActionIds.includes(replyId)) {
        this.tweetActionIds.splice(idIndex, 1)
      } else {
        this.tweetActionIds.push(replyId)
      }
    },
    checkReply(r) {
      return this.tweetActionIds.includes(r) ? true : false
    }
  },
})

app.mount('#demo')
.selected {color: red;}
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<div id="demo">
  <div class="container-full">
    <div class="tweets-container">
      <div v-for="(tweet, i) in tweets" :key="tweet.id">
        <div
          class="tweet-card"
          :class="{ selected: tweet.isSelected }"
          @click="markTweet(tweet.tweet_id, i)"
        >
          <div class="text">
            <p v-html="tweet.tweet_text">
              {{ tweet.tweet_text }}
            </p>
          </div>
        </div>
        <div class="replies">
          <div
            v-for="(reply, index) in tweet.tweetReplies"
            :key="reply.tweet_id"
            @click="markReply(reply.tweet_id, index)"
          >
            <div class="tweet-card tweet-reply">
              <div class="text" :class="{selected: checkReply(reply.tweet_id)}">
                <p>{{ reply.tweet_text }}</p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  {{tweetActionIds}}
</div>

您可以通过绕过将 isSelected 添加到每个单独的 Tweet 的额外步骤来构建 Nikola 的答案,只需检查它是否在 tweetActionIds 数组中,然后对以下内容的回复执行相同的操作保持清洁

<div id="demo">
  <div class="container-full">
    <div class="tweets-container">
      <div
        v-for="(tweet, i) in tweets"
        :key="tweet.id"
      >
        <div
          class="tweet-card"
          :class="{ selected: isActive(tweet) }"
          @click="markTweet(tweet.tweet_id, i)"
        >
          <div class="text">
            <p v-html="tweet.tweet_text">
              {{ tweet.tweet_text }}
            </p>
          </div>
        </div>
        <div class="replies">
          <div
            v-for="(reply, index) in tweet.tweetReplies"
            :key="reply.tweet_id"
            @click="markReply(reply.tweet_id, index)"
          >
            <div 
             :class="{ selected: isActive(reply) }"
             class="tweet-card tweet-reply"
            >
              <div class="text">
                <p>{{ reply.tweet_text }}</p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
  {{tweetActionIds}}
</div>
const app = Vue.createApp({
  data() {
    return {
      tweets: []
      tweetActionIds: [],
      categories: [],
    }
  },
  methods: {
    markTweet(tweetId, i) {
      const idIndex = this.tweetActionIds.indexOf(tweetId)
      if (this.tweetActionIds.includes(tweetId)) {
        this.tweetActionIds.splice(idIndex, 1)
      } else {
        this.tweetActionIds.push(tweetId)
      }
    },
    markReply(replyId) {
      const idIndex = this.tweetActionIds.indexOf(replyId)
      if (this.tweetActionIds.includes(replyId)) {
        this.tweetActionIds.splice(idIndex, 1)
      } else {
        this.tweetActionIds.push(replyId)
      }
    },
    isSelected(tweet) {
      return this.tweetActionIds.includes(tweet.tweet_id);
    }
  },
})