Question
How does one modify a prop
of a slotted Element at runtime?
Do I use the wrong lifecycle methods?
Are changes to slottedElement.type.props
necessary as well?
Example
A.vue
<template>
<B key="tag-outer">
<B key="tag-inner" />
</B>
</template>
<script>
import B from "./B.vue";
export default {
name: "A",
components: {
B,
},
};
</script>
B.vue
<template>
<p>{{ mode }}</p>
<slot />
</template>
<script>
export default {
name: "B",
props: {
mode: {
type: String,
default: "outer",
},
key: String,
},
computed: {
hasSlot() {
return !!this.$slots.default;
},
},
beforeMount() {
this.modifySlottedElements();
},
beforeUpdate() {
this.modifySlottedElements();
},
methods: {
modifySlottedElements() {
if (this.hasSlot) {
this.$slots.default().forEach((slottedElement) => {
if (slottedElement.type.name === "B") {
// prevent concurrency issues
const copiedSlottedElement = JSON.parse(JSON.stringify(slottedElement));
console.log("before");
console.log(copiedSlottedElement);
slottedElement.props.mode = "inner";
console.log("modified");
console.log(slottedElement);
}
});
}
},
},
};
</script>
Output
before
type = Object {name: "B", props: Object, computed: Object, methods: Object, __file: "C:/dev/git/csx-vue/src/demo/test/B.vue", ...}
props = Object {key: "tag-inner"}
...
modified
type = Object {name: "B", props: Object, computed: Object, beforeMount: Function, beforeUpdate: Function, ...}
props = Object {key: "tag-inner", mode: "inner"}
...
Rendered
<p>outer</p>
<p>outer</p>
Thank you
Workaround
Using an inverted logic (checking the parent)
<template>
<p>{{ modeProxy }}</p>
<slot />
</template>
<script>
export default {
name: "B",
props: {
mode: {
type: String,
default: "outer",
},
key: String,
},
data() {
return {
modeProxy: this.mode,
};
},
beforeMount() {
this.markSubMenu();
},
beforeUpdate() {
this.markSubMenu();
},
methods: {
markSubMenu() {
if (this.$parent.$.type.name === "B") {
this.modeProxy = "inner";
}
},
},
};
</script>
CodePudding user response:
Easiest way to solve your problem is to use inject/provide
- Your menu component will use
inject
to get it's level from the parent (if there is one) - and
provide
to provide the information about the level for it's child components
<template>
<div>
Level: {{ menuLevel }} / {{ menuLevel2 }}
<slot></slot>
</div>
</template>
<script>
import { provide, inject } from 'vue';
export default {
name: 'NestedMenu',
setup() {
// using Composition API
let menuLevel = inject('menuLevel', 1 /* this is default value */);
provide('menuLevel', menuLevel 1);
return { menuLevel };
},
// Same functionality as above but using Options API
inject: {
menuLevel2: { default: 1 },
},
provide() {
return {
menuLevel2: this.menuLevel2 1,
};
},
};
</script>