setting model values from a vuejs 2.0 directive - vuejs2

I'm using a Vue 2.0 directive to apply a Twitter Typeahead to the input. I've been able to get it to apply the value back to the Vue model in a way that feels like a bit of a hack, by looping through the context, finding the parent object and calling my method setAutocmpleteValue()
HTML setup for Vue Custom Directive
<input class="typeahead" type="text" placeholder="Search" name='typeahead'
v-typeahead="getFieldValues(part.fieldName)"
v-model="part.value" v-bind:value="part.value"
v-on:change="updateTest()" #input="onValidChange()" />
Custom Directive
Vue.directive('typeahead', {
inserted: function(el_, binding,v) {
// this feels like a hack, is there a better way?
v.context._self.parent.setAutocmpleteValue('test');
}
})
Within a directive $emit is not available to pass an event, is there a better way to call setAutocmpleteValue()?

In Vue 2, directives are intended for DOM manipulation.
Note that in Vue 2.0, the primary form of code reuse and abstraction
is components - however there may be cases where you just need some
low-level DOM access on plain elements, and this is where custom
directives would still be useful.
They are not intended for setting data values and attempts to do so will rely on hacks like you are using.
Not only that, but this is a very fragile approach. The directive in the question is dependent on a method in the parent of the current context. What if the input element was two levels down or more?
You're much better off implementing a component.
However, given you are already hacking, $emit is available from the context.
v.context.$emit("some-event", someData)
The context is the Vue or Vue component containing the element to which you have attached the directive, so you would want to listen to it from the parent of that component.

Related

Custom vue directive (like v-model) for using getter/setter without computed property [duplicate]

This question already has answers here:
Pass data to a directive?
(2 answers)
Closed 1 year ago.
I am creating a component system that will let other developers write vue applications with the components to quickly get up and running with simple forms.
My components are very general, and expect plain values to be bound to their value property, and will emit plain values with their input events.
The data we want the components to control is complex. I've read that you cannot use classes as vue data, because vue expects plain javascript objects for data. So each piece of data is an object like
{
_value: ...
setVal(): ...
getVal(): ...
}
And we are binding like
<my-component
:value="dataObject.getVal()"
#input="dataObject.setVal($event)"
/>
I thought it might be possible to write a vue directive to shorthand those properties, like how v-model is a shorthand for :value and #input normally, so that I could write
<my-component v-my-directive="dataObject" />
and it automatically binds the right thing to :value and the right thing to #input, but it doesn't seem to be that simple. I've found tutorials that mention twoway for Vue v1 directives, but that was removed in Vue v2, which recommended using a wrapper component as a replacement. But we have a large library of components, writing and maintaining a wrapper for each one seems out of line for just creating a shorthand like what already exists. Is there any other way?
vue2 only allows v-model directive to be bound to :value/#input. So you need to use both v-bind and #on directives for 2 way binding.
vue3 combines these using a convention that allows for multiple v-model bindings
You create a prop called 'foo' and emit and event called 'update:foo' with the value you want to assign to it.
The parent component uses v-model:foo to bind to it.

Is overriding a global Vue component safe?

Let's say there is a global component BIcon.vue available everywhere.
And another component, but regular not global, called BIconFake.vue.
We can override BIcon.vue by BIconFake.vue like that:
<template>
<div>
<b-icon icon="plus"><!-- <- Here is it BIconFake component! -->
</div>
</template>
<script>
import BIcon from '~/components/BIconFake'
export default {
components: {
BIcon // <- BIconFake component inside!
}
}
</script>
By this way, Vue.js will display BIconFake component instead of regular BIcon component.
I tried to pass props, events or attributes and it works like expected.
Vue.js is awesome... and big. Really, I don't know everything about it, and I don't want to see side effects or unexpected behavior when doing this override.
So, I want to know if it's safe to do that? Does it make a mess in Vue.js instance? What about memory?
we can override component with pure vue.js. Also, I made this example for Buefy, but we can do that with any UI frameworks like Quasar, Vuetify...
Thinking globaly, Is it good to override components of UI frameworks? What about security, scalability and maintenability?
In fact, I searched a way to build a plugins or addons system to my Nuxt.js app, like wordpress plugins.
Is it a good architecture to start building my app by overriding vue component? Is there another way to build app addons for vue, by using npm or webpack?
If you are going to wrap existing components like that then you should keep in mind the Liskov substitution principle. <b-icon-fake> can likely be used in place of <b-icon> provided that it:
accepts the same props
emits the same events
exposes the same public methods (if it is used with a ref)
behaves in the same way
Most of those points probably do not apply for a simple <b-icon> component.
Also keep in mind the template of your wrapped component now includes an extra <div> around it. This can interfere with styling and things like that.
You can eliminate the additional memory overhead by using a functional component instead, but you will need to write the render function manually to preserve the behavior of the wrapped component. But honestly I wouldn't worry too much about memory usage unless you have determined it to be an issue (after profiling your app).
In terms of whether it is "good" to do this or not, I can't say. There are advantages and disadvantages. In my opinion, wrapping components is fine as long as you are the only consumer of the wrapper component and doing so doesn't affect any existing usage of the wrapped component outside of your code.

Vue component 'mount' on v-bind change

mount on a component does not fire for v-bind changes to the component in the root.
I have a dynamic component where I often change the value of the data referenced in v-bind:is and v-bind attributes, switching the current component and its data. This works great to show different "screens" of my application.
I have some logic that happens on mount for many of the components. However, this only gets called when the v-bind:is value is changed, but no when only the v-bind attribute is. That sort of makes sense to me, though I do need to capture either.
I'm new to Vue and there is a lot of documentation. I'm so far unable to determine if there's a built-in functionality for a callback to v-bind:is and v-bind. Can anyone help me out here?

Does $destroy function removes the Vue Custom component from cache

I construct deep nested tree of parent and children Vue custom components using my top level component dynamically and then I am updating the data from which all tree is constructed. Which has an effect of rendering the entire tree (its a form with various custom components). I refresh/rebuild the whole form after fetching the data (which is what vue do for reactive data) that itself tell me how to regenerate the view (its like a JSON Schema from which I render the entire view).
This is related to my other issue here.
I am observing a very weird behavior in my Vue Application. When I destroy all my children components and rebuild the data to force rendering the form, it appears that even after I have called $destroy on every child component...Vue is not entirely removing them from cache?
Does vue remove the component from cache if a $destroy is called ?
Because I do not see multiple components of the same type in the Vue component list in the Chrome Vue DevTool extension panel. But I see that the same custom event is handled twice by the same component. Same function that handle the events is getting called twice even though there is only one component visible in Vue DevTools of this type.
This only happens after I render the form. When the page is loaded for the first time every thing works. Then after I reset the form by destroying the child component and resetting the data to re-render the form, magically this child component start handling the event twice.. and in 3rd render it handle the events thrice. But I see only one component in google chrome VueJS DevTool extension panel. So my guess is that vue is still keeping the previously destroyed component in cache. I am trying to figure out how should I destroy those components in the cache.
If anyone has observed something similar and found a solution please let me know.
At the moment I am going to dig little bit more on my component keys (this particular component does not have explicit key set by me).
First and foremost, the vue documentation states:
vm.$destroy
In normal use cases you shouldn’t have to call this method yourself.
Prefer controlling the lifecycle of child components in a data-driven
fashion using v-if and v-for.
So instead of destroying and rebuilding the components manually yourself, you should really letting vue handle that via v-if and v-for. If the components aren't updating to your changes, you might be dealing with a reactivity issue.
As you mentioned that this is a deeply nested structure, the reactivity is key to keeping the components up to data with the data.
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'b', 2)
Inside of a component:
this.$set(this.someObject, 'b', 2)
Also, keep in mind that Vue should be doing the heavy lifting in regards to component management, while you should define the parameters by which a component is rendered.

Use predefined vue.js directive with custom directive

Suppose I have a custom directive that fetches an array result from certain location , How could I possibly use v-repeat alongside my directive internally rather than separately calling v-repeat externally.
In Vue, you normally use directives (http://vuejs.org/guide/index.html#Directives) to modify the behavior of an existing DOM element. The behavior you are describing is more suited to a component (http://vuejs.org/guide/index.html#Components).
You would declare a component with a template attribute that either inline or via reference to a <script> style template would have markup that would include usage of v-repeat.