Vue.js - Typeahead Directive - vue.js

Vue.js Directive for Typeahead
I am trying to write a Vue Directive for typeahead.js functionality. I was able to instantiate typeahead on the input form control and also assign it options and dataset. Event handling too was not a problem.
The Issue :
Now, I also want to control the typeahead input element from the component. For example, I would like to control these methods $(el).typeahead('destroy') or $(el).typeahead('open'), etc. from the component.
How can i call these methods from the Component? Is it possible ?

Ok, I just figured it out 2 minutes after typing the question.
I add a ref to the input tag.
<!-- bindings is an object which has the "options" and "dataset" -->
<input ref="ttInput" v-typeahead="bindings" />
Then, I have added a method in the component which executes typeahead methods from the component:
...
methods: {
methodHook (action) {
const el = this.$refs.ttInput
$(el).typeahead(action)
}
}
...
So, now, in the html template, i can have a button/buttons:
<button type="button" #click="methodHook('open')" >Open</button>
<button type="button" #click="methodHook('close')">Close</button>
Works like a charm.
Thanks

Related

Vue seems to be calling all custom directives more often than expected. Why, can this be avoided?

I'm only a few months into vue coming from an angularjs background.
I built my first custom directive and it's acting a little odd to me.
Vue.directive('silly',{
componentUpdated: function (el, binding, vnode) {
console.log("it was called");
}
});
and I place it on my form like this:
<form id="opt-cpmt-form" method="post" class="mri-grid mri-p-none">
<label for="one">name<input id="one" type="text" v-model="local.name" v-silly class="form-control"></label><br/>
<label for="two">phone<input v-isnumeric id="two" type="text" v-model="local.phone" class="form-control "></label><br/>
<label for="two">zip<input id="three" type="text" v-model="local.zip" class="form-control" ></label><br/>
</form>
It kinda works...the part that I didn't understand is that my v-silly directive is called when any of the other fields are updated too. It seems to be related to the model but I only want my directive called when the field changes.
Any ideas??
It's an expected behaviour as the component updates whenever a piece of its data object is updated. To not trigger the logic too many times, you can create an event listener when the directive is bound to its parent and then run the logic when a desired event happens.
Vue.directive('silly', {
bind(el) {
this.updateCallback = function(event) {
// Your logic
};
el.addEventListener('input', this.updateCallback);
},
unbind(el) {
el.removeEventListener('input', this.updateCallback);
}
});
In case you plan to listen to the changes of v-model directive, bear in mind that it uses different events based on what element it's bound to. You can read more about that topic in v-model documentation.
v-model internally uses different properties and emits different events for different input elements:
text and textarea elements use value property and input event;
checkbox and radiobutton inputs use checked property and change event;
select fields use value as a prop and change as an event.
Also, from my experience when it comes to the form validation; I've done it using the directives and regretted it afterwards. I found it best to create reusable functions and create the custom form validation for every form. See custom form validation for more.

Watching a dynamically rendered field in Laravel Nova Vue component

In Laravel Nova, action modals are rendered in Vue by retrieving a list of fields to display through a dynamic component. I have replaced the action modal with own custom component, but am struggling to achieve the effect I want without also extending the entire set of components for rendering form fields.
I have my CustomResourceIndex.vue, containing a conditionally loaded (via v-if) ActionModal.vue, in which the form fields are rendered like so:
<div class="action" v-for="field in action.fields" :key="field.attribute">
<component
:is="'form-' + field.component"
:resource-name="resourceName"
:field="field"
/>
</div>
where the actual form field component is chosen based on the field.component value.
Those form fields (which I ideally do not want to have to extend and edit) are rendered like so:
<template>
<default-field :field="field" :errors="errors">
<template slot="field">
<input
class="w-full form-control form-input form-input-bordered"
:id="field.attribute"
:dusk="field.attribute"
v-model="value"
v-bind="extraAttributes"
:disabled="isReadonly"
/>
</template>
</default-field>
</template>
I would like to watch the value of specific fields and run methods when they change. Unfortunately due to a lack of ref attribute on the input elements or access to the value that the form element is bound to, I'm not sure how I can accomplish that from within ActionModal.vue.
I am hoping that because I have access to the ids still, there is some potential way for me to emulate this behavior.
Many resources I've found on my own have told me that anything with an ID is accessible via this.$refs but that does not seem to be true. I can only see elements that have an explicitly declared ref attribute in this.$refs, so I am not sure if I've misunderstood something there.
I would recommend looking into VueJS watch property.
You can listen to function calls, value changes etc.
watch: {
'field.component': function(newVal, oldVal) {
console.log('value changed from ' + oldVal + ' to ' + newVal);
},
},
Are those components triggering events? Try looking into the events tab of the Vue DevTools to see if some events are triggered from the default-field component when you update the value.
My guess is that you could write something like:
<div class="action" v-for="field in action.fields" :key="field.attribute">
<component
:is="'form-' + field.component"
:resource-name="resourceName"
:field="field"
#input="doSomething($event)"
/>
</div>
The $event value being the new value of the field.
Hit me on the comments if you have more info on the behavior of the default form fields (Are their complete code accessible somewhere?).

Convert from jQuery to Vue?

I would like to transform the following code which is in javascript:
$('a').click(function() {
$(this).find('i').toggleClass('fa-heartbeat');
});
in vue.js.
function name: like
javascript test: https://jsfiddle.net/jsk590ep/
In Vue, you typically don't select and manipulate DOM elements directly, you rather bind data to parts of the markup within your Vue components.
That said: You don't even need a function for that.
Simply
add a data element that indicates which state the icon is in (see https://v2.vuejs.org/v2/guide/#Declarative-Rendering)
change its value in the #click handler of the surrounding a, see https://v2.vuejs.org/v2/guide/events.html#Listening-to-Events
conditionally bind the fa classes based on the state to the icon, see https://v2.vuejs.org/v2/guide/class-and-style.html
<a href="#" #click="liked = !liked">
<i :class="['fa', liked ? 'fa-heartbeat' : 'fa-plus-circle']"></i>
</a>
When looking at the vue docs, note that #click in the example is a shortcut for v-on:click and :class for v-bind:class.
Working example here: https://codesandbox.io/s/stack-overflow-q-57403395-ul62e?module=/src/App.vue

this.$refs.name.select() function not found in vue

I am using Vue refs in my application so that in click a button some text will be select and copy. So I am using like
//In template
<input type="text" ref="url" value="my url"/>
<button #click="copylink">Copy Link</button>
//in methods
copylink() {
this.$refs.url.select()
}
that is undefined.
But Using document.querySelector('input').select() I can select that.
My question is it possible all document methods using $refs. How do you do this?
If you do console.log(this.$refs.url) it will give you back an array type result. All you have to do is: this.$refs.url[0].select() and it will work.
Let me know.

Access v-for model used within Vue 2 custom directive

Question
How can I update the model data used within a Vue 2 custom directive?
Setup:
<ul>
<li v-for="item in items">
<select my-directive="item">...</select>
</li>
</ul>
So let's say I have a directive with a hook like this:
Vue.directive('chosenjs', {
inserted: function (el, binding, vnode) {
// Here, I'm setting up a callback with the jQuery Chosen library, but this could be any callback.
jQuery(el).chosen().change(function(event, change) {
// CODE HERE...
});
}
});
In the CODE HERE... section, if binding.value is a pointer (array/object), then this is straight-forward. For example, for an array, I'd do e.g. binding.value.push(someValue), and Vue's observable will handle it. But if the value is a primitive, then what can be done?
If the directive is not used within a v-for loop, you can use the vnode to modify the data in the component. It works great as I show here.
But if it is in a v-for, it seems there's no way. Even with access to the binding.expression, there's no way to get at the v-for item.
Background
I'm trying to work with Vue and the ChosenJS jQuery library. I got most of the way there with this answer, but there is a bug when the directive is used within a v-for loop.