兄弟组件共享状态而不是具有独立的状态。Vue

huangapple go评论105阅读模式
英文:

Sibling components sharing state instead of have individual one. Vue

问题

I wrote a component that uploads files to the server. It accepts external parameters and has internal ones like const a = ref<>();. Then I create multiple instances of it in the parent component and give them different props, but they are bugging and share one state.

I transfer different props to the children, but even if I give them the same props, they're not supposed to share internal state variables like IsActive, but they do.

ParentComponent.vue

<el-form ...>
  <el-tabs>
    <el-tab-pane>
      <el-form-item>
        <file-uploader
                :fileList="itemODS.tech_cond_files"
                @updateFiles="
                  async (files) => {
                    itemODS.tech_cond_files = files;
                    const response = await saveOdsOisId({
                      id: itemODS.id,
                      ois_id: itemODS.ois_id,
                      tech_cond_files: itemODS.tech_cond_files,
                    });
                  }
                "
              ></file-uploader>
      </el-form-item>
      <el-form-item>
        <file-uploader
              :fileList="itemODS.project_files"
              @updateFiles="
                async (files) => {
                  itemODS.project_files = files;
                  const response = await saveOdsOisId({
                    id: itemODS.id,
                    ois_id: itemODS.ois_id,
                    project_files: itemODS.project_files,
                  });
                }
              "
              ></file-uploader>
      </el-form-item>
    </el-tab-pane>
  </el-tabs>
</el-form>

fileUploader.vue

<template>
  <div class="main">
    <div id="content" draggable="false" :class="getContentClass()">
      <input .../>

      <el-table :data="localFileList" style="width: 100%">
        <el-table-column prop="name">
          <template #default="scope">
            <span class="el-link" @click="handleFileClick(scope.row)">{{ scope.row.name }}</span>
          </template>
        <el-table-column .../>
      </el-table>
      <button .../>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from "vue";
// ...

const localFileList = ref<FileEmbed[]>([]);
const fileService = useFileService();
const overlay = ref<HTMLElement>();
const isActove = ref<boolean>(false);
// ...

const props = defineProps<{
  fileList: FileEmbed[];
  keepDeletedFile?: boolean;
}>();

watch(
  () => props.fileList.length,
  () => {
    localFileList.value = props.fileList;
  }
);
const emit = defineEmits<{
  updateFiles: [files: FileEmbed[]];
}>();

async function uploadFile(event: Event) {
  const target = event.target as HTMLInputElement;
  if (target.files != null && target.files.length >= 0) {
    const file = await fileService.uploadFile(target.files[0]);
    localFileList.value.push(file);
    emit("updateFiles", localFileList.value);
  }
}

function handleDelete(index: number) {
  // ...
  emit("updateFiles", localFileList.value);
}
</script>

link

英文:

I wrote component that upload file to the server. It accepts external parameters and has internal one like const a = ref&lt;&gt;();. Then I create multiple instances of it in parent, and give them different props, but they are bugging and share one state.

兄弟组件共享状态而不是具有独立的状态。Vue

I transfer to children different props, but even if I'll give them same props, they're not supposed to share internal state variables like IsActive, but they do.

ParentComponent.vue

&lt;el-form ...&gt;
&lt;el-tabs&gt;
&lt;el-tab-pane&gt;
&lt;el-form-item&gt;
&lt;file-uploader
:fileList=&quot;itemODS.tech_cond_files&quot;
@updateFiles=&quot;
async (files) =&gt; {
itemODS.tech_cond_files = files;
const response = await saveOdsOisId({
id: itemODS.id,
ois_id: itemODS.ois_id,
tech_cond_files: itemODS.tech_cond_files,
});
}
&quot;
&gt;&lt;/file-uploader&gt;
&lt;/el-form-item&gt;
&lt;el-form-item&gt;
&lt;file-uploader
:fileList=&quot;itemODS.project_files&quot;
@updateFiles=&quot;
async (files) =&gt; {
itemODS.project_files = files;
const response = await saveOdsOisId({
id: itemODS.id,
ois_id: itemODS.ois_id,
project_files: itemODS.project_files,
});
}
&quot;
&gt;&lt;/file-uploader&gt;
&lt;/el-form-item&gt;
&lt;/el-tab-pane&gt;
&lt;/el-tabs&gt;
&lt;/el-form&gt;

fileUploader.vue

&lt;template&gt;
&lt;div class=&quot;main&quot;&gt;
&lt;div id=&quot;content&quot; draggable=&quot;false&quot; :class=&quot;getContentClass()&quot;&gt;
&lt;input &lt;...&gt;/&gt;
&lt;el-table :data=&quot;localFileList&quot; style=&quot;width: 100%&quot;&gt;
&lt;el-table-column prop=&quot;name&quot;&gt;
&lt;template #default=&quot;scope&quot;&gt;
&lt;span class=&quot;el-link&quot; @click=&quot;handleFileClick(scope.row)&quot;&gt;{{
scope.row.name
}}&lt;/span&gt;
&lt;/template&gt;
&lt;el-table-column .../&gt;
&lt;/el-table&gt;
&lt;button .../&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;script lang=&quot;ts&quot; setup&gt;
import { onMounted, ref, watch } from &quot;vue&quot;;
&lt;...&gt;
const localFileList = ref&lt;FileEmbed[]&gt;([]);
const fileService = useFileService();
const overlay = ref&lt;HTMLElement&gt;();
const isActove = ref&lt;boolean&gt;(false);
&lt;...&gt;
const props = defineProps&lt;{
fileList: FileEmbed[];
keepDeletedFile?: boolean;
}&gt;();
watch(
() =&gt; props.fileList.length,
() =&gt; {
localFileList.value = props.fileList;
}
);
const emit = defineEmits&lt;{
updateFiles: [files: FileEmbed[]];
}&gt;();
async function uploadFile(event: Event) {
const target = event.target as HTMLInputElement;
if (target.files != null &amp;&amp; target.files.length &gt;= 0) {
const file = await fileService.uploadFile(target.files[0]);
localFileList.value.push(file);
emit(&quot;updateFiles&quot;, localFileList.value);
}
}
function handleDelete(index: number) {
&lt;...&gt;
emit(&quot;updateFiles&quot;, localFileList.value);
}
&lt;/script&gt;

link

答案1

得分: 1

I think your main problem is that you use document.getElementById() on non-unique ids - having several components means having several elements with the same id, and document.getElementById() will always give you the first, no matter from which component you call it.

To access HTML elements in Vue, use template refs instead of document.getElementById(), just assign a ref attribute to the element:

<input ref="uploader" .../>

and then in the script, declare a ref with the name passed to the ref attribute:

const uploader = ref()

Vue will then bind the HTML element to the variable.

See [playground with refs](https://element-plus.run/#eyJzcmMvQXBwLnZ1ZSI6IjxzY3JpcHQgc2V0dXAgbGFuZz1cInRzXCI+XG5pbXBvcnQgeyBvbk1vdW50ZWQsIHJlZiwgdmVyc2lvbiBhcyB2dWVWZXJzaW9uIH0gZnJvbSAndnVlJ1xuaW1wb3J0IHsgdmVyc2lvbiBhcyBlcFZlcnNpb24gfSBmcm9tICdlbGVtZW50LXBsdXMnXG5pbXBvcnQgRmlsZVVwbG9hZGVyIGZyb20gJy4vRmlsZVVwbG9hZGVyLnZ1ZSc7XG5pbXBvcnQgTXlQYWdlIGZyb20gJy4vTXlQYWdlLnZ1ZSc7XG5pbXBvcnQgeyBFbGVtZW50UGx1cyB9IGZyb20gJ0BlbGVtZW50LXBsdXMvaWNvbnMtdnVlJ1xuXG48L3NjcmlwdD5cblxuXG48dGVtcGxhdGU+XG4gIDxNeVBhZ2UgLz5cbjwvdGVtcGxhdGU+XG4iLCJpbXBvcnQtbWFwLmpzb24iOiJ7XG4gIFwiaW1wb3J0c1wiOiB7fVxufSIsInRzY29uZmlnLmpzb24iOiJ7XG4gIFwiY29tcGlsZXJPcHRpb25zXCI6IHtcbiAgICBcImFsbG93SnNcIjogdHJ1ZSxcbiAgICBcImNoZWNrSnNcIjogdHJ1ZSxcbiAgICBcImpzeFwiOiBcInByZXNlcnZlXCIsXG4gICAgXCJ0YXJnZXRcIjogXCJFU05leHRcIixcbiAgICBcIm1vZHVsZVwiOiBcIkVTTmV4dFwiLFxuICAgIFwibW9kdWxlUmVzb2x1dGlvblwiOiBcIkJ1bmRsZXJcIixcbiAgICBcImFsbG93SW1wb3J0aW5nVHNFeHRlbnNpb25zXCI6IHRydWUsXG4gICAgXCJ0eXBlc1wiOiBbXCJlbGVtZW50LXBsdXMvZ2xvYmFsLmQudHNcIl1cbiAgfSxcbiAgXCJ2dWVDb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwidGFyZ2V0XCI6IDMuM1xuICB9XG59XG4iLCJzcmMvRmlsZVVwbG9hZGVyLnZ1ZSI6Ijx0ZW1wbGF0ZT5cbiAgPGRpdiBjbGFzcz1cIm1haW5cIj5cbiAgICA8ZGl2IHJlZj1cImNvbnRlbnRcIiBkcmFnZ2FibGU9XCJmYWxzZVwiIDpjbGFzcz1cImdldENvbnRlbnRDbGFzcygpXCI+XG4gICAgICA8aW5wdXRcbiAgICAgICAgcmVmPVwidXBsb2FkZXJcIlxuICAgICAgICBkcm

英文:

I think your main problem is that you use document.getElementById() on non-unique ids - having several components means having several elements with the same id, and document.getElementById() will always give you the first, no matter from which component you call it.

To access HTML elements in Vue, use template refs instead of document.getElementById(), just assign a ref attribute to the element:

&lt;input ref=&quot;uploader&quot; .../&gt;

and then in the script, declare a ref with the name passed to the ref attribute:

const uploader = ref()

Vue will then bind the HTML element to the variable.

See playground with refs


However, better yet, don't use direct DOM manipulation at all. Using template refs is necessary when you need to access element functionality (like getting the context from a canvas element). In almost all other cases, it goes against the design principles of Vue, which offers better mechanisms to achieve your goal.

For example, you use a ref on the file input element to trigger click events, which is fine, but all other elements are used to set listeners with element.addEventListener(), which you can do better by using the same Vue syntax as you do with @click in the &lt;el-button&gt;.

See playground without addEventListener


So replacing the getElementById() statements with template refs, or even better, replacing the addEventListener() calls with Vue event listener statements should fix the issue.

huangapple
  • 本文由 发表于 2023年8月9日 14:47:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76865233.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定