How can I add a vue component in kendo grid custom editor? - vue.js

I have a column of kendo grid like:
<kendo-grid-column
:field="'qrcode'"
:title="'qrcode'"
:width="200"
:editor="qrcodeEditor"
></kendo-grid-column>
According to Setting Custom Editors, I can rewrite the editor to textarea, checkbox, or dropdown list, like
textareaEditor: function(container, options) {
$('<textarea data-bind="value: ' +
options.field +
'" cols="20" rows="4"></textarea>'
).appendTo(container);
},
My problem is what if the editor is a vue component, like <qrcode-capture />?

I have found the solution:
methods:{
qrcodeEditor: function(container, options) {
// An <input> element is required for binding data
let input = $(`<input data-bind="value:${options.field}" class="k-textbox width-50"/>`);
input.appendTo(container);
// use Vue.extend to make a component constructor, and new a component
let qrcodeCapture = new (Vue.extend(QrcodeCapture))();
qrcodeCapture.$on("decode", decodedString => {
input.val(decodedString).trigger("change");
// Trigger "change" element to tell kendo that you have change the data
});
qrcodeCapture.$mount();
container.append(qrcodeCapture.$el);
},
}

Related

In Vue, how to get the content of a textarea?

I want to keep the value of a variable identical with the content of a textarea.
I don't want to use v-bind or v-model, because I have already bound the textarea with another value.
This is a notebook app, and the textarea is used to display the content of a note, so it has been bound using v-bind with a note object, like
<textarea cols="30" rows="3" v-bind:value="note"></textarea>
Now, I want to add the "edit note" functionality. So when the content of the textarea changes, I want to store its value into a variable, and when the "submit" button is clicked, I pass the value of the variable, which contains the new content of the note, to backend to update the note.
My question is, how to store the textarea's content into the variable after each time the content changes?
I think I cannot use v-model because this way the note will be changed right after the content of the textarea is modified (though not sent to backend), but this is not what I want. What I want is the note to be changed only after the "submit" button is clicked. Thus, I cannot use v-model
Should I use v-on:change? If so, how to get the content of the textarea?
Like,
<textarea v-on:change="updateTheVariable(I need to get the content of the textarea here)"> ... </textarea>
methods: {
updateTheVariable(content of the textarea) {
this.variable = content of the textarea
}
}
Thanks
I'm assuming this thing only shows up when you click some kind of edit button which is why you don't want to alter note so try something like this instead
<button type="button" v-if="!editMode" #click="editNote">Edit</button>
<form v-if="editMode" #submit="handleSubmit">
<fieldset :disabled="saving">
<textarea v-model="editingNote"></textarea>
<button type="submit">Edit</button>
</fieldset>
</form>
export default {
data: () => ({
note: 'whatever', // maybe it's a prop, maybe assigned later, doesn't matter
editMode: false,
editingNote: null, // this will be used to bind the edited value
saving: false
}),
methods: {
editNote () {
this.editingNote = this.note
this.editMode = true
this.saving = false
},
async handleSubmit () {
this.saving = true // disables form inputs and buttons
await axios.post('/notes/update', { note: this.editingNote}) // just an example
this.note = this.editingNote // or maybe use data from the response ¯\_(ツ)_/¯
// or if it's a prop, this.$emit('updated', this.editingNote)
this.editMode = false
}
}
}
As #Phil indicated in a deleted post, the right way to do it is
<textarea #input="updateTheVariable($event.target.value)"></textarea>
.
.
.
methods:{
updateTheVariable(value){
this.variable = value
}
}

Display a list in leaflet popup

I want to display an unorderd list or table in a leaflet popup.
The number of items and their content are different and depend on the type of element which was clicked.
So ideally the popup content should be created on the click event.
I tried to build the list inside the bindPopup function, but it's not working.
L.marker([mapElement.y * -1, mapElement.x], {
uniqueID: mapElement.elementID,
mapIconWidth: mapElement.width,
icon: new mapIcon({
iconUrl: icon.mapIcon.imageData,
iconSize: [elementSize, elementSize]
})
})
.addTo(markers)
.bindPopup(mapElement.element.nbr + ' ' + mapElement.element.name + "<br/<ul> <li v-for='state in mapElement.element.states'>{{ state.definition.stateTypeTitle }}</li> </ul>");
That's the output:
Any ideas would be great!
Thanks!
Edited code (get this error message: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.):
LoadMarker(mapID) {
console.log("Load Markers");
map.removeLayer(markers);
markers.clearLayers();
fetch(this.$apiUrl + "/mapelements/" + mapID)
.then(response => response.json())
.then(received => {
let mapElements = received;
let mapZoom = map.getZoom();
mapElements.forEach(function(mapElement) {
let elementSize = (mapElement.width / 8) * mapZoom;
let popup = new Vue({
template:
mapElement.element.nbr +
" " +
mapElement.element.name +
"<br/<ul> <li v-for='state in mapElement.element.states'>{{ state.definition.stateTypeTitle }}</li> </ul>",
data: {
mapElement
}
}).$mount().$el;
let icon = mapIconSchemas.find(
schema => schema.mapIconSchemaID == mapElement.mapIconSchemaID
);
if (icon != null) {
L.marker([mapElement.y * -1, mapElement.x], {
uniqueID: mapElement.elementID,
mapIconWidth: mapElement.width,
icon: new mapIcon({
iconUrl: icon.mapIcon.imageData,
iconSize: [elementSize, elementSize]
})
})
.addTo(markers)
.bindPopup(popup);
}
});
});
map.addLayer(markers);
},
You can not use Vue templating syntax in the HTML String for the popup. But as can be seen from the docs the .bindPopup method can also accept HTML element. So your way to go would be like this:
first create the popup element:
let popup = new Vue({
template: mapElement.element.nbr + ' ' + mapElement.element.name + "<br/<ul> <li v-for='state in mapElement.element.states'>{{ state.definition.stateTypeTitle }}</li> </ul>",
data: {
mapElement
}
}).$mount().$el
and then use it in the .bindPopup method:
/*...*/
.bindPopup(popup)
There is a solution, if you want to use the vue templating engine to fill the popup content.
I explained it for this question.
You create a component with the content you want to display in the popup, but you hide it :
<my-popup-content v-show=False ref='foo'><my-popup-content>
Then you can access the generated html of that component in your code like this :
const template = this.$refs.foo.$el.innerHTML
and use it to fill your popup.
The big advantage of that method is that you can generate the popup content with all the vue functionalities (v-if, v-bind, whatever) and you don't need messy string concatenations anymore.
import Component from './Component.vue'
import router from './router'
import store from './store'
bindPopup(() => new Vue({
// router,
// store,
render: h => h(Component)
}).$mount().$el)
perfect

aurelia-validation with custom element

I created an input form with aurelia-validation plugin which worked perfectly.
Now I created a custom element to replace the input type textbox.
I'm trying to validate the new custom element value, by setting the value attribute with the "validate" keyword - but the custom element input value isn't validated.
It seems like the Validation Controller is not binded to the custom element.
The bindings array of the Validation Controller doesn't contains the custom element.
Maybe this is related to the actions that should trigger the validation (blur\focus out), so I added dispatching of blur event, but it still doesn't work. As mentioned - it worked perfectly when it was a regular element.
Here is the relevant code (un-needed code was removed):
custom element template:
<template>
<label>
${title}<input name.bind="fieldName"
title.bind="title" focusout.trigger="focusoutAction()" />
</label>
</template>
custom element relevant viewmodel code:
#bindable onFocusout;
...
bind(bindingContext) {
var input = this.element.getElementsByTagName("input")[0];
input.type = this.customType || "text";
input.placeholder = this.placeHolder || "";
//input.value.bind = bindingContext.registration.firstName & validate;
}
...
focusoutAction() {
var customEvent = new CustomEvent("blur");
this.element.dispatchEvent(customEvent);
this.onFocusout();
}
Relevant container(parent) view code:
<form-input name="firstName" id="firstName" title="First Name" bind-
value="registration.firstName & validate" field-name="firstName" on-
focusout.call="validateInput()" />
And the relevant viewmodel code:
ValidationRules
.ensure(r => r.firstName).displayName('first
name').required().withMessage(`\${$displayName} cannot be blank`)
.satisfiesRule('FirstNameValidation')
.ensure(r => r.lastName).displayName('last
name').required().withMessage(`\${$displayName} cannot be blank`)
.satisfiesRule('LastNameValidation')
validateInput() { this.getValidationError(event.target.name); }
getValidationError(propertyName) {
let error = this.getValidationFirstError(propertyName);
....
}
getValidationFirstError(propertyName)
{
if (this.controllerToValidate.errors !== null &&
this.controllerToValidate.errors.length > 0) //This is 0 !!!!!
}

Twitter typeahead.js not working in Vue component

I'm trying to use Twitter's typeahead.js in a Vue component, but although I have it set up correctly as tested out outside any Vue component, when used within a component, no suggestions appear, and no errors are written to the console. It is simply as if it is not there. This is my typeahead setup code:
var codes = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('code'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: contextPath + "/product/codes"
});
$('.typeahead').typeahead({
hint: true,
highlight: true,
minLength: 3
},
{
name: 'codes',
display: 'code',
source: codes,
templates: {
suggestion: (data)=> {
return '<div><strong>' + data.code + '</strong> - ' + data.name + '</div>';
}
}
});
I use it with this form input:
<form>
<input id="item" ref="ttinput" autocomplete="off" placeholder="Enter code" name="item" type="text" class="typeahead"/>
</form>
As mentioned, if I move this to a div outside Vue.js control, and put the Javascript in a document ready block, it works just fine, a properly formatted set of suggestions appears as soon as 3 characters are input in the field. If, however, I put the Javascript in the mounted() for the component (or alternatively in a watch, I've tried both), no typeahead functionality kicks in (i.e., nothing happens after typing in 3 characters), although the Bloodhound prefetch call is made. For the life of me I can't see what the difference is.
Any suggestions as to where to look would be appreciated.
LATER: I've managed to get it to appear by putting the typeahead initialization code in the updated event (instead of mounted or watch). It must have been some problem with the DOM not being in the right state. I have some formatting issues but at least I can move on now.
The correct place to initialize Twitter Typeahead/Bloodhound is in the mounted() hook since thats when the DOM is completely built. (Ref)
Find below the relevant snippet: (Source: https://digitalfortress.tech/js/using-twitter-typeahead-with-vuejs/)
mounted() {
// configure datasource for the suggestions (i.e. Bloodhound)
this.suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: item => item.id,
remote: {
url: http://example.com/search + '/%QUERY',
wildcard: '%QUERY'
}
});
// get the input element and init typeahead on it
let inputEl = $('.globalSearchInput input');
inputEl.typeahead(
{
minLength: 1,
highlight: true,
},
{
name: 'suggestions',
source: this.suggestions,
limit: 5,
display: item => item.title,
templates: {
suggestion: data => `${data.title}`;
}
}
);
}
You can also find a working example: https://gospelmusic.io/
and a Reference Tutorial to integrate twitter typeahead with your VueJS app.

onfocus event not firing on dojo dijit FilteringSelect

I have a page with 7 dijit FilteringSelect widgets that I want to connect to stores fetched from the server if the user clicks on the widget or the widget otherwise gains focus. I want the event to fire one time.
If I add onfocus="loadDropDown(this)" to my markup, it executes every time the widget gains focus, as you would expect.
I'm trying to use dojo to fire the event one time using on.once(). The function to use dojo event handling is running but the event handler function never gets called when a widget gains focus.
Any pointers?
This is my markup
<select data-dojo-type="dijit.form.FilteringSelect"
type="text" intermediateChanges="false"
data-dojo-props="required:false, pageSize:20, placeholder: '---'"
scrollOnFocus="true" name="CJ1lxA" style="width: 40em;"
id="searchAgency">
</select>
This is to regester the events
function registerDDLoad(){
require(["dojo/on", "dijit", "dojo/ready"], function(on, dijit, ready){
ready(function(){
var dropDown = dijit.byId("searchAgency");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
dropDown = dijit.byId("searchLocation");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
dropDown = dijit.byId("searchCounty");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
dropDown = dijit.byId("searchRep");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
dropDown = dijit.byId("searchSenate");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
dropDown = dijit.byId("searchStatus");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
dropDown = dijit.byId("searchAE");
on.once(dropDown, "onfocus", function() {
loadDropDown(dropDown);
});
});
});
}
registerDDLoad();
The dojo event class, dojo/on expects events to be specified without the 'on':
onFocus = focus
onClick = click
onMouseOut = mouseout
...
I think changing that should fix your problem. I've copied your code into test area on jsFiddle, so you can play around with it.
NB: Since you are re-using the dropdown variable, it will always equal the last filteringSelect (id=searchAE) and never the earlier ones.