从另一个组件中更改一个组件中的属性值

Posted

技术标签:

【中文标题】从另一个组件中更改一个组件中的属性值【英文标题】:Change a property's value in one component from within another component 【发布时间】:2019-02-05 19:40:54 【问题描述】:

我正在努力学习 Vue.js 作品,阅读大量文档和教程,并参加了一些复数课程。我有一个非常基本的网站 UI 正在运行。这是 App.vue(我将其用作母版页)。

(为了让阅读更轻松、更快捷,请查看以下评论​​:This is the part you should pay attention to)...

<template>
    <div id="app">
        <div>
            <div>
                <CommandBar />
            </div>
            <div>
                <Navigation />
            </div>
        </div>
        <div id="lowerContent">

            <!-- This is the part you should pay attention to -->

            <template v-if="showLeftContent">
                <div id="leftPane">
                    <div id="leftContent">
                        <router-view name="LeftSideBar"></router-view>
                    </div>
                </div>
            </template>

            <!-- // This is the part you should pay attention to -->

            <div id="mainPane">
                <div id="mainContent">
                    <router-view name="MainContent"></router-view>
                </div>
            </div>
        </div>
    </div>
</template>

然后在同一个 App.vue 文件中,这是脚本部分

<script lang="ts">
    import  Component, Vue  from 'vue-property-decorator';

    import CommandBar from './components/CommandBar.vue';
    import Navigation from './components/Navigation.vue';


    @Component(
        components: 
            CommandBar,
            Navigation,
        
    )
    export default class App extends Vue 
        data() 
            return 
                showLeftContent: true // <--- This is the part you should pay attention to
            
        
    
</script>

好的,我的想法是,我想在某些页面上显示左侧边栏,但在其他页面上我不想。这就是为什么那个 div 被包裹在 &lt;template v-if="showLeftContent"&gt; 中的原因。

然后使用命名为&lt;router-view&gt;'s,我可以控制将哪些组件加载到`router\index.ts\ 文件中。路线如下所示:

    
        path: '/home',
        name: 'Home',
        components: 
            default: Home,
            MainContent: Home,  // load the Home compliment the main content
            LeftSideBar: UserSearch  // load the UserSearch component in the left side bar area
        
    ,

到目前为止一切顺利!但这是踢球者。有些页面没有左侧栏,在这些页面上,我想将 showLeftContenttrue 更改为 false。那是我想不通的部分。

假设我们有一个看起来像这样的“Notes”组件。

<template>
    <div class="notes">
        Notes
    </div>
</template>

<script lang="ts">
    import  Component, Prop, Vue  from 'vue-property-decorator';

    @Component
    export default class Notes extends Vue 
        data() 
            return 
                showLeftContent: false  // DOES NOT WORK
            
        
    
</script>

显然,我在这里没有正确处理showLeftContent。我理解,data 中的属性似乎仅适用于该组件。我只是没有找到任何关于如何在 App 组件中设置数据属性,然后在通过router-view 加载子组件时在子组件中更改它的任何信息。

谢谢!

编辑:

我将 Notes 组件的脚本部分更改为:

<script lang="ts">
    import  Component, Prop, Vue  from 'vue-property-decorator';

    @Component
    export default class Notes extends Vue 
        data() 
            return 
                showLeftContent: false  // DOES NOT WORK
            
        
    
</script>

到:

<script lang="ts">
    import  Component, Prop, Vue  from 'vue-property-decorator';

    @Component
    export default class Notes extends Vue 
        mounted() 
            this.$root.$data.showLeftContent = false;
        
    
</script>

虽然这不会导致任何编译或运行时错误,但它也没有达到预期的效果。在Notes 上,左侧栏仍然显示。

编辑 2:

如果我在 Notes 组件的脚本部分添加警报:

export default class Notes extends Vue 
    mounted() 
        alert(this.$root.$data.showLeftContent);
        //this.$root.$data.showLeftContent = false;
    

在我单击导航中的“备注”之前,警报不会弹出。但是,该值是“未定义”。

编辑 3:

在语法上苦苦挣扎(记住这是 TypeScript,我不太了解!!)

编辑 4:

慢慢来!

export default class App extends Vue 
    data() 
        return 
            showLeftContent: true
        
    

    leftContent(value: boolean) 
        alert('clicked');
        this.$root.$emit('left-content', value);
    

这不会导致任何错误,但它也不起作用。该事件永远不会被触发。我将尝试将其放入 Navigation 组件中,看看是否可行。

【问题讨论】:

你确定 Notes 组件已经挂载了吗?可能会添加一个console.log 以仔细检查它。 @Sphinx,见编辑 2 如果值为'undefined',可能是App不是根实例造成的。通过console.log(this.$root.$el)仔细检查。 大声笑,我也不知道打字稿(对于编辑 3)。添加了 typescript 标签。 this.$root.$emit('left-content', value); 应该在想要隐藏/显示侧面板的组件上调用。而你没有定义this.$on 【参考方案1】:

正如@lukebearden 回答中所说,您可以使用发出事件在router-link 点击时将真/假传递给App 主组件。

假设您的 Navigation 组件如下所示,您可以执行以下操作:

#Navigation.vue
<template>
  <div>
      <router-link to="/home" @click.native="leftContent(true)">Home</router-link> - 
      <router-link to="/notes" @click.native="leftContent(false)">Notes</router-link>
  </div>
</template>

<script>
export default 
  methods: 
    leftContent(value) 
      this.$emit('left-content', value)
    
  

</script>

在您的主要App 中,您在Navigation 上收听发射:

<template>
  <div id="app">
    <div>
      <Navigation @left-content="leftContent" />
    </div>
    <div id="lowerContent">
      <template v-if="showLeftContent">
         //...
      </template>

      <div id="mainPane">
         //...
      </div>
    </div>
  </div>
</template>

<script>
  //...
  data() 
    return 
      showLeftContent: true
    
  ,
  methods: 
    leftContent(value) 
      this.showLeftContent = value
    
  
;
</script>

【讨论】:

@CaseyCrookston 我不太了解 ts,但我认为您不需要此块 methods: ... 来初始化方法。 @CaseyCrookston 关于您的编辑 4,发出 'left-content' 事件的不是 App,而是 Navigation(你不需要$root)。 App在导航组件上监听事件。 @sovalina,你能告诉我那会是什么样子吗?伙计,我解决了一个问题,另一个问题突然出现了!!我真的很感谢你的帮助!! @CaseyCrookston 我做了一个codesandbox here 来测试路由器链接(但它不在打字稿中) @CaseyCrookston 如果/notes 是一个路由,您也可以将LeftSideBar 从它的路由组件中排除:codesandbox.io/s/6lp16pwjpr【参考方案2】:

父子组件关系中的一个基本方法是从子组件发出事件,然后在父组件中侦听和处理该事件。

但是,我不确定这种方法在使用路由器视图时是否有效。这个人通过观察 $route 属性的变化解决了这个问题。 https://forum.vuejs.org/t/emitting-events-from-vue-router/10136/6

您可能还想研究使用 vue 实例或使用 vuex 创建简单的事件总线。

【讨论】:

this.$root.$emit/$on 将是一种选择。【参考方案3】:

如果您想访问根实例的数据属性(或道具、选项等),您可以使用this.$root.$data。 (查看Vue Guide: Handling Edge)

对于您的代码,您可以在其他组件的 hook=mounted 中将 this.$root.$data.showLeftContent 更改为 true/false,然后当 Vue 为这些组件创建实例时,它将显示/隐藏左侧面板相关。

下面是一个演示

Vue.config.productionTip = false
Vue.component('child', 
  template: `<div :style="'background-color':color" style="padding: 10px">
                Reach to root: <button @click="changeRootData()">Click me!</button>
                <hr>
                <slot></slot>
             </div>`,
  props: ['color'],
  methods: 
    changeRootData() 
      this.$root.$data.testValue += ' :) '
    
  
)

new Vue(
  el: '#app',
  data() 
    return 
      testValue: 'Puss In Boots'
    
  
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <h2>testValue</h2>
  <child color="red"><child color="gray"><child color="green"></child></child></child>

</div>

【讨论】:

在根 App 组件中,我应该使用道具而不是数据吗?意思是,showLeftContent 应该是一个道具而不是 data() 对象的一部分吗? @CaseyCrookston,Vue 在单向数据流下工作。一个组件不应更改其道具的值。所以我认为使用一个 prop 代替 data 属性不是一个好主意。 好的,谢谢。这就说得通了。所以...我仍在努力了解如何更改数据属性“showLeftContent” 如果做一个警报,像这样:alert(this.$root.$data.showLeftContent);它返回“未定义”

以上是关于从另一个组件中更改一个组件中的属性值的主要内容,如果未能解决你的问题,请参考以下文章

React - 从另一个子组件更改子组件中的状态

我想从另一个反应原生的一个类中更改一个静态数组

无法从另一个组件/ Angular 4中的auth.service访问错误消息

如何在单击时从另一个兄弟组件中增加一个值?

prop 更改时新的 vue 组件实例

从另一个组件调用一个组件中的函数