I want to use lodash debounce with #input event in my Vue js app. But the defined function is not triggered.
import Vue from "vue";
import debounce from "lodash.debounce";
const GlobalFunctions = {
install(Vue) {
Vue.prototype.$debounce = (func, wait, options) => {
console.log("log1");
return debounce(func, wait, options);
};
}
};
Vue.use(GlobalFunctions);
new Vue({
el: "#test",
methods: {
onInput() {
this.$debounce(this.callApi, 500, {
trailing: true
});
},
callApi() {
console.log("api called!");
},
},
});
<
/script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="test">
<input type="text" #input="onInput" />
</div>
I created an example for my story. What can be the problem ? I shared link to below
you can see on the console it does not triggered
Code example
Related
I am working on a vue project and the vue version is 3.0
And recently I can see these many warnings for some reason.
Template compilation error: v-model value must be a valid JavaScript member expression
I guess it is because I am using long v-model variable name like this.
<textarea v-model="firstVariable.subVariable.subVariableKey" readonly></textarea>
Please let me know if any idea.
Thanks in advance
This is the component and template code.
var myTemplate = Vue.defineComponent({
template: '#myTemplate',
data() {
return {
firstVariable: {}
}
},
mounted() {
loadData();
},
methods:{
loadData() {
axios.get(MY_ROUTES).then(res => {
// let's suppose res.data is going to be {subVariable: {subVariableKey: "val"}}
this.firstVariable = res.data;
})
}
}
});
// template.html
<script type="text/template" id="myTemplate">
<div class="container">
<textarea v-model="firstVariable.subVariable?.subVariableKey"></textarea>
</div>
</script>
In order that your property go reactive you've to define its full schema :
data() {
return {
firstVariable: {
subVariable: {
subVariableKey: ''
}
}
}
},
and use it directly without optional chaining
v-model="firstVariable.subVariable.subVariableKey"
because v-model="firstVariable.subVariable?.subVariableKey" malformed expression like v-model="a+b" like this test
Example
var comp1 = Vue.defineComponent({
name: 'comp1',
template: '#myTemplate',
data() {
return {
firstVariable: {
subVariable: {
subVariableKey: ''
}
}
}
},
mounted() {
this.loadData();
},
methods: {
loadData() {
}
}
});
const {
createApp
} = Vue;
const App = {
components: {
comp1
},
data() {
return {
}
},
mounted() {
}
}
const app = createApp(App)
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" >
vue 3 app
<comp1 />
</div>
<script type="text/template" id="myTemplate">
<div class="container">
<textarea v-model="firstVariable.subVariable.subVariableKey"></textarea>
<div>
{{firstVariable.subVariable.subVariableKey}}
</div>
</div>
</script>
You are adding a new property to an object which is not reactive.
Vue cannot detect property addition or deletion. Since Vue performs
the getter/setter conversion process during instance initialization, a
property must be present in the data object in order for Vue to
convert it and make it reactive. For example:
Source
Instead of
this.firstVariable = res.data;
Use
this.$set(this.firstVariable, 'subVariable', res.data.subVariable);
I want to have a dynamic/async component which will be loaded based on menu click. There will be a lot of links and components.
My problem is :
Cannot find test-component even I registered globally using Vue.Component
Can the component1 in root be changed? If it is possible, I will call component file using Ajax.
Thanks in advance.
Index.html
<script src="~/bundle/site.js"></script>
<div id="app">
<input type="button" v-on:click="changeComponent('test-component')" value="Click me"/>
<component v-bind:is="view"></component>
</div>
site.js
import Vue from 'vue';
import axios from 'axios';
global.Vue = Vue;
global.axios = axios;
var vm = new Vue({
el: '#app',
data: {
view: 'component1'
},
components: {
'component1': {
template: '<div>Dynamic Component master</div>'
}
},
methods: {
changeComponent: function (parComp) {
this.component1 = parComp;
},
}
});
Vue.component('test-component', {
template: '<div v-on:click = "changeName()"><h1>{{msg}}</h1></div>',
data: function () {
return {
msg: "Test Componet"
}
},
methods: {
changeName: function () {
this.msg = "mouse clicked";
},
}
})
Update
change vm = new Vue().... to global.vm = new Vue().....
call vm.changeComponent('test-component')
It works now.
Vue.options.components["test-component"] can access it.
But test-component cannot be access in html like :
<test-component></test-component>
The problem solved by :
change vm = new Vue().... to global.vm = new Vue().....
declare Vue.component('test-component'.... before new Vue()
Thanks
Wilson
How to load external Vue components when I not use npm? I got this error message:
ReferenceError: VueButtonSpinner is not defined
Vue.use(VueButtonSpinner);
var vm = new Vue({
delimiters: ['[[', ']]'],
el: '#test',
data: {
isLoading: false,
status: ''
},
components: {
VueButtonSpinner
},
methods: {
send() {
console.log("ypyoyoyoy");
this.isLoading = true;
}
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://unpkg.com/vue-button-spinner#2.2.0/dist/vue-button-spinner.js"></script>
<div id="test">
<button-spinner
#click="send"
:isLoading="isLoading"
:disabled="isLoading"
:status="status">
<span>Submit</span>
</button-spinner>
</div>
This should work. The trick is that the script gets imported, but the code defaults to the default attribute inside of window['vue-button-spinner']:
Vue.component('button-spinner', window['vue-button-spinner'].default);
var vx = new Vue({
// delimiters: ['[[', ']]'],
el: '#test',
data() {
return {
isLoading: false,
status: ''
};
},
methods: {
send() {
console.log("ypyoyoyoy");
this.isLoading = true;
}
},
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://unpkg.com/vue-button-spinner#2.2.0/dist/vue-button-spinner.js"></script>
<div id="test">
<button-spinner
#click.native="send"
:is-loading="isLoading"
:disabled="isLoading"
:status="status">
<span>Submit</span>
</button-spinner>
</div>
You can use the module loader system, so use <script type="module" src="whatever.js"></script> and import in whatever.js:
From MDN <script>
module: HTML5 For HTML5-compliant browsers the code is treated as a
JavaScript module. The processing of the script contents is not
affected by the charset and defer attributes. For information on using
module, see ES6 in Depth: Modules. Code may behave differently when
the module keyword is used.
whatever.js
import Vue from "https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js";
import {
VueButtonSpinner
} from "https://unpkg.com/vue-button-spinner#2.2.0/dist/vue-button-spinner.js";
const app = new Vue({
el: "#app",
components: {
"button-spinner": VueButtonSpinner
}
});
Question
Given I am in component context, how do I get the component object? By component object I mean the object you get when you import Component from 'Component.vue'.
Current progress
Here's one possibility I found.
const component = {
methods: {
getComponent: () => this,
displayItem () {
console.log('this.getComponent()', this.getComponent()) // undefined
console.log('this', this) // component instance
console.log('component', component) // what I need (component object)
},
},
}
export default component
The downside though is that it kills IDE support.
I also checked this manually.
Ideal solution
The approximation to syntax I'd like to see: this.$component.
What's the point?
Instantiate components via :is="component".
Perform instance of check.
The closer you got is vm.$options:
Vue.component('some-comp', {
template: '<p>{{ message }}</p>',
props: {name: String},
data() {
return {
message: 'Open the console!'
}
},
computed: {
example() {
return this.message.toUpperCase();
}
},
watch: {
message() {
console.log('watcher triggered');
}
},
mounted() {
console.log(this.$options);
console.dir(this.$options.__proto__);
}
});
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<some-comp :name="'Alice'"></some-comp>
</div>
But it seems what you want is constructor:
Vue.component('aaa-aaa', {
template: '<div>AAA component</div>'
})
Vue.component('bbb-bbb', {
template: '<div>BBB component</div>'
})
new Vue({
el: '#app',
mounted() {
console.log(this.$refs.a1);
console.log(this.$refs.a1.constructor);
console.log(this.$refs.b1);
console.log(this.$refs.b1.constructor);
console.log('a1 a2', this.$refs.a1.constructor === this.$refs.a2.constructor)
console.log('a1 b1', this.$refs.a1.constructor === this.$refs.b1.constructor)
console.log('b1 b2', this.$refs.b1.constructor === this.$refs.b2.constructor)
console.log('b2 a2', this.$refs.b2.constructor === this.$refs.a2.constructor)
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<aaa-aaa ref="a1"></aaa-aaa>
<aaa-aaa ref="a2"></aaa-aaa>
<bbb-bbb ref="b1"></bbb-bbb>
<bbb-bbb ref="b2"></bbb-bbb>
</div>
I have experience with both single page apps and multi-page apps (classic websites). In the past I have used AngularJS 1.x on each page, it has been useful because all components can live in separate files and be executed as they appear on each page.
I'm now looking at VueJS to replace AngularJS, but not finding it easy to understand how to architect my multi-page app.
As expected I want to use some components on all the pages, and some on only a few pages.
Example:
I came across SFC - single file components using ES2015 which looked promising, but my backend is Java which outputs my html from JSPs. It appears that .vue files are precompiled by webpack, but if my templates are only ready when the page is rendered that won't be possible will it?
How would one architect a solution so that each component is modular but utilize either an x-template in the html and somehow attach it to a .vue SFC, or is there some other way to have components in separate files which can be imported using ES2015?
I hope this is making sense, can't seem to figure it out.
One possible approach would be to set the template for the Vue Component inline. So this would be to have a Component File like
Home.vue:
<script>
export default {
data() {
return {
msg: 'text',
}
}
}
</script>
import it as a global component for Vue (using require, import, etc.)
Vue.component('home', require('./components/Home.vue'));
and in your server generated HTML you'd have to use an inline template, which will have all the flexibility from normal templates
home.jsp:
<home inline-template>
<h2 v-text="msg"></h2>
</home>
Update
I've added an example on GitHub here
If I understand your question, you have want to make single file components out of HTML.
If this is the case, you should make use of the render() function and regular components.
The render function decides what to use as a template for a component:
<!DOCTYPE html>
<html>
<head>
<title>Vue</title>
</head>
<body>
<div id="app">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
render (createElement) {
return createElement({
template: '<div>Hello World</div>'
})
},
})
</script>
</body>
</html>
will render Hello World to the screen.
Now, let's see how this function is reactive:
<script type="text/javascript">
new Vue({
el: '#app',
data: {
count: 0
},
render (createElement) {
return createElement({
template: '<div>Hello World ' + this.count + '</div>'
})
},
created () {
setTimeout(() => {
this.count++
}, 2000)
}
})
</script>
Here, after 2 seconds, the counter in <div>Hello World ' + this.count + '</div> will increment from 0 to 1.
Now, what if we want to separate the template from the data?
<script type="text/javascript">
new Vue({
el: '#app',
render (createElement) {
return createElement({
template: '<div>Hello World {{ count }}</div>',
data () {
return {foo: 'bar'}
}
})
}
})
</script>
This code will display Hello World bar.
Now, let's see what happen if we try to load our template over http. We'll use the axios library to do so. Let's create a remote.html file to contain our html code:
<div>
I'm a remote component {{ foo }}
</div>
Now, let's try to load it via Ajax:
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
template: null
},
render (createElement) {
return createElement({
template: this.template ? this.template : '<div>Hello World {{ foo }}</div>',
data () {
return {foo: 'bar'}
}
})
},
created () {
axios({
url: '/remote.html',
method: 'get'
}).then(response => {
this.template = response.data
})
}
})
</script>
This code will display I'm a remote component {{ foo }} as soon as remote.html has been loaded from the browser.
Note that the object passed to the createElement function is actually a component structure. You can use the same methods on it:
render (createElement) {
return createElement({
template: this.template ? this.template : '<div>Hello World {{ foo }}</div>',
data () {
return {foo: 'bar'}
},
mounted () {
alert('Hello from mounted')
}
})
}
will trigger an alert on the browser.
Anyway, here is a complete example with nested components:
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Vue</title>
</head>
<body>
<div id="app">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.min.js"></script>
<script type="text/javascript">
const headerComponent = {
data () {
return {
template: '<div>Loading...</div>'
}
},
render (createElement) {
return createElement({
template: this.template,
data () {
return {
search: ''
}
}
})
},
created () {
axios('/header.html').then(response => {
this.template = response.data
})
}
}
new Vue({
el: '#app',
data: {
template: null
},
render (createElement) {
return createElement({
template: this.template ? this.template : 'Loading...',
data () {
return {foo: 'bar'}
},
components: {
'my-header': headerComponent
}
})
},
created () {
axios({
url: '/remote.html',
method: 'get'
}).then(response => {
this.template = response.data
})
}
})
</script>
</body>
</html>
header.html
<div>
<label>Search</label>
<input v-model="search" name=""> The search is: {{ search }}
</div>
I'm not sure that this is really the best approach and if I'm really responding to the question, but it will at list give you some tips on how Vue handles rendering and components...