Pass properties from parent component to all trans

2019-07-28 23:23发布

I would like to pass some properties from a parent to all of his children when those are transcluded (content distribution syntax). In this case, the parent doesen't know (as far as I know) his children, so I don't know how to proceed.

More specificly, I want a way to write this :

<my-parent prop1="foo" prop2="bar">
    <my-children></my-children> <!-- Must know content of prop1 and prop2 -->
    <my-children></my-children> <!-- Must know content of prop1 and prop2 -->
</my-parent>

Instead of having to write this :

<my-parent prop1="foo" prop2="bar">
    <my-children prop1="foo" prop2="bar"></my-children>
    <my-children prop1="foo" prop2="bar"></my-children>
</my-parent>

Is it possible ? Thanks.

3条回答
一纸荒年 Trace。
2楼-- · 2019-07-28 23:43

At this point (I'm not a vue expert) I just could think in this solution.

Assign every component's props is boring I agree, so why not doing it programmatically?

// Create a global mixin
Vue.mixin({
  mounted() { // each component will execute this function after mounted
    if (!this.$children) {
      return;
    }

    for (const child of this.$children) { // iterate each child component
      if (child.$options._propKeys) {
        for (const propKey of child.$options._propKeys) { // iterate each child's props
          // if current component has a property named equal to child prop key
          if (Object.prototype.hasOwnProperty.call(this, propKey)) {
            // update child prop value
            this.$set(child, propKey, this[propKey]);

            // create a watch to update value again every time that parent property changes
            this.$watch(propKey, (newValue) => {
              this.$set(child, propKey, newValue);
            });
          }
        }
      }
    }
  },
});

This works but you will get an ugly vue warn message:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.

I'm not sure if this is a good solution but it works, so if you decide to use just keep in mind Global-Mixin recomendations:

Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components.

Please see a full example at https://github.com/aldoromo88/PropsConvention

Hope it helps

查看更多
萌系小妹纸
3楼-- · 2019-07-28 23:51

Here is my solution, that's probably not a great deal, but that's the cleanest solution for what I want to do right now. The principle is to create computed properties that will use own component prop if they exist, or get $parent values otherwise. The real prop would then be accessible in this._prop.

Vue.component('my-children', {
    props: ["prop1", "prop2"],
    template: "<div>{{_prop1}} - {{_prop2}}</div>",
    computed: {
        _prop1: function() {
            return this.prop1 || this.$parent.prop1;
        },
        _prop2: function() {
            return this.prop2 || this.$parent.prop2;
        }
    }
});

Here is a mixin generator that does that in a more elegant way, and with, possibly, multiple levels :

function passDown(...passDownProperties) {
    const computed = {};
    passDownProperties.forEach((prop) => {
        computed["_" + prop] = function() {
            return this[prop] || this.$parent[prop] || this.$parent["_" + prop];
        };
    });
    return { computed };
}

Vue.component('my-children', {
    props: ["prop1", "prop2"],
    template: "<div>{{_prop1}} - {{_prop2}}</div>",
    mixins: [passDown("prop1", "prop2")]
});
查看更多
三岁会撩人
4楼-- · 2019-07-28 23:59

Props allow data flow only one level. If you want to perpetuate data, you can use an event bus instead.

Instantiate an event bus with an empty Vue instance in your main file.

var bus = new Vue();

Then in your parent, emit the event with data to be passed

 bus.$emit('myEvent', dataToBePassed);

Listen for myEventanywhere you want to pick up the data. In your case, it is done in your child components

bus.$on('myEvent', function(data) {
     .....
});
查看更多
登录 后发表回答