Proxi
<proxi>
is a tiny proxy component. Whatever you add to it gets proxied to a target component!
🙋 Why?
♻️ Uses Vue's Template API Doesn't re-invent component communication!💪 Provide/Inject on Steroids! Familiar concepts, but super powered!💥 Reactive All injected data is reactive (unlike provide/inject)!🐥 Tiny 755 B Gzipped!
🚀 Install
npm i vue-proxi
🚦 3-step Setup
👩 Parent component
1. - Import and register
import Proxi from 'vue-proxi'
- Insert anywhere in your template:
<proxi :proxi-key="key" [... attr / :prop / @listener]>
key
is used to communicate with the Child. Use a unique string value or aSymbol
👶 Child component
2. - Import the Proxi Inject mixin:
import { ProxiInject } from 'vue-proxi'
- Register the mixin:
export default {
// ...,
mixins: [
ProxiInject({
from: key, // from Step 1
props: [
// Becomes available on VM eg. `this.propName`
'propName'
// ...
]
})
]
}
✅ Done!
3. - The injected data is all available in
this.$$
-
this.$$.class
: Class -
this.$$.props
: Props (Automatically bound to VM) -
this.$$.attrs
: Attributes- eg.
v-bind="$$.attrs"
orv-bind="{ ...$attrs, ...$$.attrs }"
- eg.
-
this.$$.listeners
: Event listeners (Automatically bound to VM)- eg.
v-on="$$.listeners"
orv-on="{ ...$listeners, ...$$.listeners }"
- eg.
-
👨🏫 Demos
Some quick demos to get you started!
Inheriting props?
When you declare a prop, it filters it out from the attributes list (
$$.attrs
) to be available directly on the view model (this.propName
) and the props list ($$.props
).
<proxi
:proxi-key="key"
:child-disabled="isDisabled"
:child-label="label"
/> |
<label>
{{ label }}
<input
type="checkbox"
:disabled="childDisabled"
>
</label> export default {
mixins: [
ProxiInject({
from: key,
props: [
'childDisabled',
'childLabel'
]
})
],
computed: {
label() {
return this.childLabel + ':';
}
}
}; |
Inheriting the class?
Both the static class and computed class are consolidated into
$$.class
for you to easily attach to any element.
<proxi
:proxi-key="key"
class="static-class"
:class="['child-class', {
disabled: isDisabled
}]"
/> |
<div :class="$$.class">
Child
</div> export default {
mixins: [
ProxiInject({ from: key })
],
}; |
Inheriting attrs?
- Looking to inherit a specific attribute? Just pick it out from
$$.attrs
- Looking to inherit all attributes? Throw
$$.attrs
intov-bind
<proxi
:proxi-key="key"
:disabled="true"
/> |
<div
:disabled="$$.attrs.disabled"
v-bind="$$.attrs"
>
Child
</div> export default {
mixins: [
ProxiInject({ from: key })
],
}; |
Inheriting listeners?
All event listeners are in
$$.listeners
to throw right into v-on
!
<proxi
:proxi-key="key"
@click="handleClick"
@custom-event="handleCustomEvent"
/> |
<button v-on="$$.listeners">
Child
</button> export default {
mixins: [
ProxiInject({ from: key })
],
mounted() {
// Listeners are automatically bound to VM
this.$emit('custom-event', 'Mounted!');
}
}; |
Advanced
This demo shows how a parent-child pair, RadioGroup and Radio, communicate using Proxi. Note how the two components only come together at usage.
Usage
<template>
<div>
<radio-group v-model="selected">
<radio
label="Apples"
value="apples"
/>
<radio
label="Oranges"
value="oranges"
/>
<radio
label="Bananas"
value="bananas"
/>
</radio-group>
<div>
Selected: {{ selected }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
selected: []
}
}
}
</script>
Parent: RadioGroup.vue
<template>
<div>
<proxi
:proxi-key="key"
:checked-items="value"
@update="$emit('input', $event)"
>
<slot />
</proxi>
</div>
</template>
<script>
import Proxi from 'vue-proxi'
export default {
components: {
Proxi
},
props: ['value'],
data() {
return {
// Same idea as provide/inject
// Use a Symbol for security
key: 'radios'
}
}
}
</script>
Child: Radio.vue
<template>
<label>
<input
type="checkbox"
:checked="isChecked"
@click="onClick"
>
{{ label }}
</label>
</template>
<script>
import { ProxiInject } from 'vue-proxi'
export default {
mixins: [
ProxiInject({
// Same key as parent
from: 'radios',
// Declare props that can be injected in
// Only array supported for now
props: ['checkedItems']
})
],
props: {
label: String,
value: null
},
computed: {
isChecked() {
return this.checkedItems.includes(this.value)
}
},
methods: {
onClick() {
if (this.isChecked) {
this.$emit('update', this.checkedItems.filter(i => i !== this.value))
} else {
this.$emit('update', [...this.checkedItems, this.value])
}
}
}
}
</script>
👪 Related
-
vue-subslot -
💍 Pick 'n choose what you want from a slot passed into your Vue component -
vue-pseudo-window -
🖼 Declaratively interface window/document in your Vue template -
vue-vnode-syringe -
🧬 Mutate your vNodes with vNode Syringe💉