简体   繁体   English

根据道具,更新 Vue3 组件中 div 中的 CSS 类不起作用(缺少反应性?)

[英]Updating CSS classes in on div in Vue3 component, depending on props, does not work (Missing reactivity?)

I found a good looking bottom navigation bar for vue on Github ( https://github.com/imanmalekian31/vue-bottom-navigation ).我在 Github ( https://github.com/imanmalekian31/vue-bottom-navigation ) 上找到了一个好看的 vue 底部导航栏。

Sadly it did not work with Vue3, Typescript and the script setup syntax.遗憾的是,它不适用于 Vue3、Typescript 和脚本设置语法。

I rewrote the component and got it to run and show up in my app.我重写了组件并让它运行并显示在我的应用程序中。

The problem is, the buttons are not reactive at the moment.问题是,按钮目前没有反应。 While the methods on click get run, the css classes on the elements do not get updated.当单击运行时,元素上的 css 类不会更新。

My best guess here is, that localOptions and button in我最好的猜测是,localOptions 和 button

<div v-for="(button, index) in localOptions" :key="`grow-button-${index}`"></div>

need to be reactive, which they might not due to the change to script setup.需要反应,他们可能不会由于脚本设置的更改。

Any help on how to fix this code would be greatly appreciated.非常感谢有关如何修复此代码的任何帮助。

Update: I did replace let localOptions: any[] = reactive([]) with let localOptions = reactive(props.options.slice()) and that seems to have done the trick.更新:我确实用let localOptions = reactive(props.options.slice())替换了let localOptions: any[] = reactive([]) ,这似乎已经成功了。 The buttons are working and updating just fine.这些按钮正在工作并且更新得很好。

The problem that appeared now is, that the computed Property doesn't seem to be run every time the component re-renders.现在出现的问题是,计算属性似乎并没有在每次组件重新渲染时运行。 Therefore the buttons are not changing width or color.因此按钮不会改变宽度或颜色。

Component file:组件文件:

<template>
  <div class="gr-btn-container-foreground" :style="cssVariables">
    <div v-for="(button, index) in localOptions" :key="`grow-button-${index}`" :class="[
      'gr-btn-container',
      { 'gr-btn-container-active': button.selected },
    ]" @click="handleButtonClick(button, index)">
      <div :class="['gr-btn-item', { 'gr-btn-item-active': button.selected }]">
        <div :class="['gr-btn-icon', { 'gr-btn-icon-active': button.selected }]">
          <slot name="icon" :props="button">
            <i :class="`${button.icon}`" />
          </slot>
        </div>
        <div class="gr-btn-title">
          <span class="gr-hidden-title">
            <slot name="title" :props="button">{{ button.title }}</slot>
          </span>
          <span :class="[
            'gr-animated-title',
            { 'gr-animated-title-active': button.selected },
          ]">
            <slot name="title" :props="button">{{ button.title }}</slot>
          </span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, reactive } from "vue";
import { useRoute, useRouter } from 'vue-router'

const router = useRouter()
const route = useRoute()
const button = reactive({})


const model = {
  prop: "value",
  event: "update",
}
const props = defineProps({
  value: {
    default: null,
  },
  options: {
    type: Array,
    default: () => [],
  },
  color: {
    type: String,
    default: "#74cbbb",
  },
  replaceRoute: {
    type: Boolean,
    default: false,
  },
})

const emit = defineEmits<{
  (e: 'update', value: string): void
}>()

let prevSelected = null
let currSelected = null
let localOptions: any[] = reactive([])

const cssVariables = computed(() => {

  const activeTitle = (localOptions[currSelected] || {}).title;
  let activeWidth = 95;
  if (activeTitle && activeTitle.length * 15 > 110) {
    activeWidth = 95 + (activeTitle.length * 15 - 110) / 2;
  }

  const mainColor =
    (localOptions[currSelected] || {}).color || props.color;

  const styles = {
    "--color": mainColor,
    "--color-background": mainColor + "30",
    "--active-width": `${activeWidth}px`,
  };

  return styles;
})

localOptions = props.options.slice();

let index = localOptions.findIndex(
  (item) =>
    item.id == props.value ||
    (item.path || {}).name == (route || {}).name
);

if (index > -1) {
  currSelected = index;
  prevSelected = index;

  localOptions[index] = { ...localOptions[index], selected: true }
}


function handleButtonClick(button: any, index: number) {
  console.log("Button: " + button + " Index: " + index);
  currSelected = index;

  if (prevSelected !== null) {
    localOptions[prevSelected].selected = false;
  }

  localOptions[index] = { ...localOptions[index], selected: true }

  prevSelected = currSelected;
  updateValue(button);
}

function updateValue(button) {
  console.log("Update value: " + button.id + " with Path: " + button.path)

  emit("update", button.id);

  if (button.path && Object.keys(button.path).length) {
    router[!props.replaceRoute ? "push" : "replace"](
      button.path
    ).catch(() => { console.log("Error updating path") });
  }
}

</script>

<style scoped>
.gr-btn-container-foreground {
  position: fixed;
  direction: ltr;
  display: flex;
  align-items: center;
  bottom: 0;
  width: 100%;
  z-index: 2147483647;
  height: 64px;
  background: #fff;
  box-shadow: 0 0 5px 0 #eee;
}

.gr-btn-container {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 45px;
  flex-grow: 1;
  transition: all 0.3s;
}

@media (min-width: 576px) {
  .gr-btn-container {
    cursor: pointer;
  }
}

.gr-btn-container-active {
  background-color: var(--color-background);
  border-radius: 100px;
}

.gr-btn-item {
  display: flex;
  position: relative;
  overflow: hidden;
  width: 24px;
  transition: all 0.3s ease;
  color: #0000008a;
}

.gr-btn-item-active {
  width: var(--active-width);
}

.gr-btn-icon {
  font-size: 20px;
  transition: all 0.3s ease;
  margin: 0px !important;
}

.gr-btn-icon-active {
  color: var(--color);
}

.gr-btn-title {
  position: relative;
  color: var(--color);
  padding: 0px 5px;
  margin-left: 10px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.gr-hidden-title {
  visibility: hidden;
}

.gr-animated-title {
  color: var(--color);
  position: absolute;
  left: 5px;
  bottom: -15px;
  transition: bottom 0.4s ease 0.1s;
}

.gr-animated-title-active {
  bottom: 2px;
}
</style>

currSelected is not a reactive variable (ref). currSelected不是反应变量 (ref)。 As far as I quickly see this is the reason why your cssVariables computed does not recalculate - it misses reactive dependence of currSelected据我所知,这就是为什么你的cssVariables计算不重新计算的原因 - 它错过了currSelected的反应依赖

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM