Utilities
@ActiveProvide / @ActiveInject
These typescript property decorators provide reactive versions of Vue's Provide and Inject features.
@ActiveProviderequires a key to identify the value being passed down. Use dot notation, and prefix withjdsto namespace all JDS injections.@ActiveProviderequires a default value (set as the property's value) to ensure reactivity.@ActiveInjectshould not need a default value. Assert the type using:!instead.
@Provide or a provides array in a single component's configuration. Use one or the other. In general, the JDS will use ActiveProvide.<script lang="ts">
@Component({})
export class Provider1 extends Vue {
@ActiveProvide('jds.test.provide1') var1 = '';
@Prop() toInject!: string;
@Watch('toInject', {immediate: true})
syncProvide() {
this.var1 = this.toInject;
}
}
@Component({})
export class Injected1 extends Vue {
@ActiveInject({from: 'jds.test.provide1', default: 'def'}) val1!: string;
@ActiveInject({from: 'jds.test.provide2', default: ''}) val2!: string;
}
</script><div class="jds-mb-4">
Inject:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo1"/>
</div>
<provider1 :to-inject="injectDemo1">
<injected1/>
</provider1>Multiple Nesting
Inject2:
<div class="jds-mb-4">
Inject1:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo2a"/>
<br> Inject2:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo2b"/>
</div>
<provider1 :to-inject="injectDemo2a">
<provider2 :to-inject="injectDemo2b">
<injected1/>
</provider2>
</provider1>Multiple @ActiveProvide
This test makes sure that components don't intermingle with other instances.
Inject2:
Inject1:
Inject2:
<div class="jds-mb-4">
Inject1:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo3a"/>
<br> Inject2:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo3b"/>
<br><br> Inject1:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo3c"/>
<br> Inject2:
<input class="jds-border jds-border-grey-400 jds-p-1" type="text" v-model="injectDemo3d"/>
</div>
<provider-multi :to-inject1="injectDemo3a" :to-inject2="injectDemo3b">
<injected1/>
</provider-multi>
<provider-multi :to-inject1="injectDemo3c" :to-inject2="injectDemo3d">
<injected1/>
</provider-multi>Accessing from Functional Components
We don't recommend using injects, may it be Vue's standard inject or ActiveInject, with functional components.
Internally, ActiveProvide works by passing around a computed function as a getter for the provided value, instead of the raw value. This means you can reach into an ActiveProvide value through regular code (e.g. inside a functional component) by using injects regularly, and then executing the function it returns. See InjectFunctional.vue below.
Due to how Vue works (see Vue issue #5837), you cannot write a functional component with injects if it is a direct child of the component with provide. This has to do with template compilation and contexts — the actual invocation of the functional component (i.e. when you typed the tag name of the functional component) cannot be in the <template> tag of the component that provide.
Therefore, slots don't solve this problem, as even if you put the functional component invocation inside the slot, the actual location of where you wrote the functional component tag name is still in the parent.
This won't work:
<template>
<div>
<inject-functional />
</div>
</template>
<script lang="ts">
@Component({
components: {InjectFunctional}
})
export default class ParentComponent extends Vue {
@ActiveProvide('jds.test.functional') foo = 'bar';
}
</script><script type="text/jsx">
// InjectFunctional.vue
export default {
functional: true,
inject: {
val: {
from: "jds.test.provide1",
default () {
return () => 'default'
}
}
},
render(h, context) {
return <div>
Value1: { context.injections.val() }
</div>
},
}
</script>One solution is to write a "passthrough" wrapper component that contains nothing but the functional component. This way, the functional component is called in the context of the wrapper, not the parent, so the inject/provide works.