Vue.js crud javascript? - javascript

I need to load data to a hands-on table,
When I use:
case: if used directly into data, its work good, but I need to load data when is created from Axios, using Axios. This doesn't work.
data: function() {
return {
info:[],
hotSettings: {
data: [['a','b','c'],['ra','rb','rc']],
}
}
}
case: if use in my variable info, it doesn't work either.
data: function() {
return {
info:[['a','b','c'],['ra','rb','rc']],
hotSettings: {
data: this.info,
}
}
}
case: using hook created. This doesn't work.
<template>
<div>
<hot-table ref="hotTableComponent" :settings="hotSettings"></hot-table>
</div>
</template>
<script>
import { HotTable } from '#handsontable/vue';
import Handsontable from 'handsontable';
export default {
created: function (){
this.newData()
},
data: function() {
return {
info:[],
hotSettings: {
data: this.info,
colHeaders: ['ID','Name',' pain'],
rowHeaders: true,
minRows: 2,
minCols: 3,
}
}
},
methods: {
newData() {
//dont work 1rs,
this.info = ['a','b','c'],['ra','rb','rc']];
// don't work, change 2dn
// let urlsecciones = 'seccion/show';
// axios.get(urlsecciones).then(response => {
// this.info = response.data;
// console.log(response.data) // run good
// });
}
},
components: {
HotTable
}
}
</script>

You canĀ“t reference data properties between them, instead you can use a computed property to handle what you want:
new Vue({
el: "#app",
created: function (){
this.newData()
},
data() {
return {
info: [],
}
},
computed:{
hotSettings(){
return {
data: this.info,
colHeaders: ['ID','Name',' pain'],
rowHeaders: true,
minRows: 2,
minCols: 3,
}
}
},
methods: {
newData() {
this.info = [
["a", "b", "c"],
["ra", "rb", "rc"]
]
// Handle Axios logic here
}
},
components: {
'hottable': Handsontable.vue.HotTable
}
});
<div id="app">
<HotTable :settings="hotSettings"></HotTable>
</div>
Jsfiddle: https://jsfiddle.net/hansfelix50/069s1x35/

Related

Vue component get and set conversion with imask.js

I am trying to use iMask.js to change 'yyyy-mm-dd' to 'dd/mm/yyyy' with my component however when I am setting the value I think it is taking the value before the iMask has finished. I think using maskee.updateValue() would work but don't know how to access maskee from my component.
I am also not sure if I should be using a directive to do this.
Vue.component("inputx", {
template: `
<div>
<input v-mask="" v-model="comp_date"></input>
</div>
`,
props: {
value: { type: String }
},
computed: {
comp_date: {
get: function () {
return this.value.split("-").reverse().join("/");
},
set: function (val) {
const iso = val.split("/").reverse().join("-");
this.$emit("input", iso);
}
}
},
directives: {
mask: {
bind(el, binding) {
var maskee = IMask(el, {
mask: "00/00/0000",
overwrite: true,
});
}
}
}
});
var app = new Vue({
el: "#app",
data: {
date: "2020-12-30"
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="https://unpkg.com/imask"></script>
<div id="app">
<inputx v-model="date"></inputx>
Date: {{date}}
</div>
The easiest way you can achieve this is by installing the external functionality on the mounted hook of your Vue component, instead of using a directive.
In this way you can store the 'maskee' object on your component's data object to later access it from the setter method.
Inside the setter method you can then call the 'updateValue' method as you hinted. Then, you can extract the processed value just by accessing the '_value' prop of the 'maskee' object.
Here is a working example:
Vue.component("inputx", {
template: `
<div>
<input ref="input" v-model="comp_date"></input>
</div>
`,
data: {
maskee: false,
},
props: {
value: { type: String },
},
computed: {
comp_date: {
get: function () {
return this.value.split("-").reverse().join("/");
},
set: function () {
this.maskee.updateValue()
const iso = this.maskee._value.split("/").reverse().join("-");
this.$emit("input", iso);
}
}
},
mounted(){
console.log('mounted');
const el = this.$refs.input;
this.maskee = IMask(el, {
mask: "00/00/0000",
overwrite: true,
});
console.log('maskee created');
}
});
var app = new Vue({
el: "#app",
data: {
date: "2020-12-30"
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="https://unpkg.com/imask"></script>
<div id="app">
<inputx v-model="date"></inputx>
Date: {{date}}
</div>

extend existing event in custom control ui5

I'd like to extend (not override) the existing UI5 event of MultiComboBox component.
I found that https://github.com/SAP/openui5/blob/master/src/sap.m/src/sap/m/MultiComboBox.js
has MultiComboBox.prototype._handleSelectionLiveChange, how do I actually extend it in my own custom control?
Here is what I've done:
sap.ui.define([
'sap/m/MultiComboBox',
'sap/m/HBox'
], function (MultiComboBox, HBox) {
return MultiComboBox.extend('TokenizedMultiComboBox', {
metadata: {
aggregations: {
_hbox: { type: 'sap.m.HBox', multiple: false }
}
},
init: function () {
MultiComboBox.prototype.init.apply(this, arguments);
this.setAggregation('_hbox', new HBox({
items: []
}));
},
onAfterRendering: function() {
const hbox = this.getAggregation('_hbox');
},
_handleSelectionLiveChange: function() {
// should my code go here or?
},
renderer: function (rm, oControl) {
sap.m.MultiComboBoxRenderer.render(rm, oControl);
rm.write('<div');
rm.writeControlData(oControl);
rm.write('>');
rm.write('<div>');
rm.renderControl(oControl.getAggregation('_hbox'));
rm.write('</div>');
rm.write('</div>');
},
})
})

V-model is not listening to value change for an input (vuejs)

I have an object property which could listen to the user input or could be changed by the view.
With the snipped below :
if I typed something the value of my input is updated and widget.Title.Name is updated.
if I click on the button "External Update", the property widget.Title.Name is updated but not the value in my field above.
Expected result : value of editable text need to be updated at the same time when widget.Title.Name change.
I don't understand why there are not updated, if I inspect my property in vue inspector, all my fields (widget.Title.Name and Value) are correctly updated, but the html is not updated.
Vue.component('editable-text', {
template: '#editable-text-template',
props: {
value: {
type: String,
default: '',
},
contenteditable: {
type: Boolean,
default: true,
},
},
computed: {
listeners() {
return { ...this.$listeners, input: this.onInput };
},
},
mounted() {
this.$refs["editable-text"].innerText = this.value;
},
methods: {
onInput(e) {
this.$emit('input', e.target.innerText);
}
}
});
var vm = new Vue({
el: '#app',
data: {
widget: {
Title: {
Name: ''
}
}
},
async created() {
this.widget.Title.Name = "toto"
},
methods: {
externalChange: function () {
this.widget.Title.Name = "changed title";
},
}
})
button{
height:50px;
width:100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<editable-text v-model="widget.Title.Name"></editable-text>
<template>Name : {{widget.Title.Name}}</template>
<br>
<br>
<button v-on:click="externalChange">External update</button>
</div>
<template id="editable-text-template">
<p ref="editable-text" v-bind:contenteditable="contenteditable"
v-on="listeners">
</p>
</template>
I searched a lot of subject about similar issues but they had reactivity problem, I think I have a specific problem with input. Have you any idea of what's going on ? I tried to add a listener to change event but it was not triggered on widget.Title.Name change.
To anwser to this problem, you need to do 3 differents things.
Add watch property with the same name as your prop (here value)
Add debounce function from Lodash to limit the number of request
Add a function to get back the cursor (caret position) at the good position when the user is typing
For the third point : when you change the value of widget.Title.Name, the component will re-render, and the caret position will be reinitialize to 0, at the beginning of your input. So, you need to re-update it at the last position or you will just write from right to left.
I have updated the snippet above with my final solution.
I hope this will help other people coming here.
Vue.component('editable-text', {
template: '#editable-text-template',
props: {
value: {
type: String,
default: '',
},
contenteditable: {
type: Boolean,
default: true,
},
},
//Added watch value to watch external change <-> enter here by user input or when component or vue change the watched property
watch: {
value: function (newVal, oldVal) { // watch it
// _.debounce is a function provided by lodash to limit how
// often a particularly expensive operation can be run.
// In this case, we want to limit how often we update the dom
// we are waiting for the user finishing typing his text
const debouncedFunction = _.debounce(() => {
this.UpdateDOMValue();
}, 1000); //here your declare your function
debouncedFunction(); //here you call it
//not you can also add a third argument to your debounced function to wait for user to finish typing, but I don't really now how it works and I didn't used it.
}
},
computed: {
listeners() {
return { ...this.$listeners, input: this.onInput };
},
},
mounted() {
this.$refs["editable-text"].innerText = this.value;
},
methods: {
onInput(e) {
this.$emit('input', e.target.innerText);
},
UpdateDOMValue: function () {
// Get caret position
if (window.getSelection().rangeCount == 0) {
//this changed is made by our request and not by the user, we
//don't have to move the cursor
this.$refs["editable-text"].innerText = this.value;
} else {
let selection = window.getSelection();
let index = selection.getRangeAt(0).startOffset;
//with this line all the input will be remplaced, so the cursor of the input will go to the
//beginning... and you will write right to left....
this.$refs["editable-text"].innerText = this.value;
//so we need this line to get back the cursor at the least position
setCaretPosition(this.$refs["editable-text"], index);
}
}
}
});
var vm = new Vue({
el: '#app',
data: {
widget: {
Title: {
Name: ''
}
}
},
async created() {
this.widget.Title.Name = "toto"
},
methods: {
externalChange: function () {
this.widget.Title.Name = "changed title";
},
}
})
/**
* Set caret position in a div (cursor position)
* Tested in contenteditable div
* ##param el : js selector to your element
* ##param caretPos : index : exemple 5
*/
function setCaretPosition(el, caretPos) {
var range = document.createRange();
var sel = window.getSelection();
if (caretPos > el.childNodes[0].length) {
range.setStart(el.childNodes[0], el.childNodes[0].length);
}
else
{
range.setStart(el.childNodes[0], caretPos);
}
range.collapse(true);
sel.removeAllRanges();
}
button{
height:50px;
width:100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<editable-text v-model="widget.Title.Name"></editable-text>
<template>Name : {{widget.Title.Name}}</template>
<br>
<br>
<button v-on:click="externalChange">External update</button>
</div>
<template id="editable-text-template">
<p ref="editable-text" v-bind:contenteditable="contenteditable"
v-on="listeners">
</p>
</template>
you can use $root.$children[0]
Vue.component('editable-text', {
template: '#editable-text-template',
props: {
value: {
type: String,
default: '',
},
contenteditable: {
type: Boolean,
default: true,
},
},
computed: {
listeners() {
return {...this.$listeners, input: this.onInput
};
},
},
mounted() {
this.$refs["editable-text"].innerText = this.value;
},
methods: {
onInput(e) {
this.$emit('input', e.target.innerText);
}
}
});
var vm = new Vue({
el: '#app',
data: {
widget: {
Title: {
Name: ''
}
}
},
async created() {
this.widget.Title.Name = "toto"
},
methods: {
externalChange: function(e) {
this.widget.Title.Name = "changed title";
this.$root.$children[0].$refs["editable-text"].innerText = "changed title";
},
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="app">
<editable-text v-model="widget.Title.Name"></editable-text>
<template>Name : {{widget.Title.Name}}</template>
<br>
<br>
<button v-on:click="externalChange">External update</button>
</div>
<template id="editable-text-template">
<p ref="editable-text" v-bind:contenteditable="contenteditable" v-on="listeners">
</p>
</template>
or use Passing props to root instances
Vue.component('editable-text', {
template: '#editable-text-template',
props: {
value: {
type: String,
default: '',
},
contenteditable: {
type: Boolean,
default: true,
},
},
computed: {
listeners() {
return {...this.$listeners, input: this.onInput
};
},
},
mounted() {
this.$refs["editable-text"].innerText = this.value;
this.$root.$on("titleUpdated",(e)=>{
this.$refs["editable-text"].innerText = e;
})
},
methods: {
onInput(e) {
this.$emit('input', e.target.innerText);
}
}
});
var vm = new Vue({
el: '#app',
data: {
widget: {
Title: {
Name: ''
}
}
},
async created() {
this.widget.Title.Name = "toto"
},
methods: {
externalChange: function(e) {
this.widget.Title.Name = "changed title";
this.$root.$emit("titleUpdated", this.widget.Title.Name);
},
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="app">
<editable-text v-model="widget.Title.Name"></editable-text>
<template>Name : {{widget.Title.Name}}</template>
<br>
<br>
<button v-on:click="externalChange">External update</button>
</div>
<template id="editable-text-template">
<p ref="editable-text" v-bind:contenteditable="contenteditable" v-on="listeners">
</p>
</template>

Vue JS Ajax data not populating Vue-Tribute on load

I am using the Vue Tribute component https://github.com/syropian/vue-tribute
When initially loading the page when the "show" data property is set to true I get "No Match!". However if I set the "show" data property to false on page load then set it to true manually I will get the two results as expected. I have tried to wrap the function call to getTributeOptions() inside of "mounted, created and updated" but I receive the same results. I am using the setTimeout() to mimic the AJAX call I am using to load the remote data.
var app = new Vue({
el: '#myApp',
data: function() {
return {
show: true,
tributeOptions: {
values: []
}
};
},
mounted: function() {
this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
}, 500)
}
}
})
<div id="myApp">
<div v-if="show">
<vue-tribute :options="tributeOptions">
<input type="text" placeholder="#" />
</vue-tribute>
</div>
</div>
https://codepen.io/anon/pen/QBQaNB?editors=1111
I found the answer on this question: Vuejs mount the child components only after data has been loaded
Updated Code:
var app = new Vue({
el: '#myApp',
data: function() {
return {
userDataLoaded: false,
tributeOptions: {
values: []
}
};
},
mounted: function() {
this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
vm.dataLoaded = true;
}, 500)
}
}
})
<div id="myApp">
<template>
<template v-if="dataLoaded">
<vue-tribute :options="tributeOptions">
<input type="text" placeholder="#" />
</vue-tribute>
</template>
</template>
</div>
While your workaround above would probably work, the problem lays in the library you use
In https://github.com/syropian/vue-tribute/blob/master/src/index.js#L19
mounted() {
const $el = this.$slots.default[0].elm;
this.tribute = new Tribute(this.options);
...
}
The options value is only used once in mounted(), and there is no handler for updating the values when the options are changed.
A better way to do it would be to watch for changes in this.options, and update the value inside the component respectively.
Check Vue Tribute source code at Github, you will see it will only create one new Tribute instance in mounted(). That means even you change the value of props=options once mounted, it will not affect anything.
So one solution is make sure tributeOptions is ready before mount, so update the value in created() will be an idea.
var app = new Vue({
el: '#myApp',
data: function() {
return {
tributeOptions: {
values: []
}
};
},
created: function () {
this.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
]
},
mounted: function() {
//this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
}, 500)
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-tribute"></script>
<div id="myApp">
<vue-tribute :options="tributeOptions">
<input type="text" placeholder="#" />
</vue-tribute>
</div>
another solution is download the source codes for Vue Tribute in Github, then implement update Tribute instance by yourself.
Update: create one pull request which implement update Tribute options.
the third solution will be force re-mount by bind different key every time once tributeOptions is updated:
like below demo.
var app = new Vue({
el: '#myApp',
data: function() {
return {
tributeOptions: {
values: []
},
tributeKey: 0
};
},
mounted: function() {
this.getTributeOptions();
},
methods: {
getTributeOptions: function(resource) {
var vm = this;
setTimeout(function() {
vm.tributeOptions.values = [
{ key: 'Phil Heartman', value: 'pheartman' },
{ key: 'Gordon Ramsey', value: 'gramsey' }
];
vm.tributeKey+=1
}, 500)
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-tribute"></script>
<div id="myApp">
<vue-tribute :options="tributeOptions" :key="tributeKey">
<input type="text" placeholder="#" />
</vue-tribute>
</div>

Vue.js global event not working

I've got
<component-one></component-one>
<component-two></component-two>
<component-three></component-three>
Component two contains component three.
Currently I emit an event in <component-one> that has to be caught in <component-three>.
In <component-one> I fire the event like this:
this.$bus.$emit('setSecondBanner', finalBanner);
Then in <component-three> I catch it like this:
mounted() {
this.$bus.$on('setSecondBanner', (banner) => {
alert('Caught');
this.banner = banner;
});
},
But the event is never caught!
I define the bus like this (in my core.js):
let eventBus = new Vue();
Object.defineProperties(Vue.prototype, {
$bus: {
get: () => { return eventBus; }
}
});
What could be wrong here? When I check vue-dev-tools I can see that the event has fired!
This is the working example for vue1.
Object.defineProperty(Vue.prototype, '$bus', {
get() {
return this.$root.bus;
}
});
Vue.component('third', {
template: `<div> Third : {{ data }} </div>`,
props:['data']
});
Vue.component('second', {
template: `<div>Second component <third :data="data"></third></div>`,
ready() {
this.$bus.$on('setSecondBanner', (event) => {
this.data = event.data;
});
},
data() {
return {
data: 'Defautl value in second'
}
}
});
Vue.component('first', {
template: `<div>{{ data }}</div>`,
ready() {
setInterval(() => {
this.$bus.$emit('setSecondBanner', {
data: 'Bus sending some data : '+new Date(),
});
}, 1000);
},
data() {
return {
data: 'Defautl value in first'
}
}
});
var bus = new Vue({});
new Vue({
el: '#app',
data: {
bus: bus
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.js"></script>
<div id="app">
<second></second>
<first></first>
</div>
Have you tried registering the listener in created instead of mounted?
Also, why define the bus with defineProperties and not simply:
Vue.prototype.$bus = new Vue();

Categories

Resources