I made a component for a drag and drop element. What I want to do is use my .vue template file, instead of writing all the HTML on the single line after the template. (When I do write it in the component itself, it works fine, but just doesn't look good.) I need it to be a component because it can be that there is more than one component on one page.
Here is my .js file:
import Vue from 'vue';
import SingleFileUpload from './single-file-upload.vue'
Vue.component ('test', SingleFileUpload, {
delimiters: ['${', '}'], // Avoid Twig conflicts
data() {
return filelist // Store our uploaded files
},
methods: {
onChange() {
this.filelist = [...this.$refs.file.files];
},
remove(i) {
this.filelist = [];
},
dragover(event) {
event.preventDefault();
// Add some visual fluff to show the user can drop its files
if (!event.currentTarget.classList.contains('highlight')) {
event.currentTarget.classList.remove('bg-haze');
event.currentTarget.classList.add('highlight');
}
},
dragleave(event) {
// Clean up
event.currentTarget.classList.add('bg-haze');
event.currentTarget.classList.remove('highlight');
},
drop(event) {
event.preventDefault();
this.$refs.file.files = event.dataTransfer.files;
this.onChange(); // Trigger the onChange event manually
// Clean up
event.currentTarget.classList.add('bg-haze');
event.currentTarget.classList.remove('highlight');
}
},
template: SingleFileUpload
})
new Vue({
el: '#App',
})
This is my .vue file
<template>
<div class="file-upload__wrapper">
<div
class="file-upload__drop-area"
#dragover="dragover"
#dragleave="dragleave"
#drop="drop"
>
<input
type="file"
name="file-upload-bank"
id="file-upload-bank"
class="d-none"
#change="onChange"
ref="file"
accept=".pdf,.jpg,.jpeg,.png"
/>
<ul v-if="this.filelist.length" v-cloak>
<li v-for="file in filelist" v-bind:key="file.id">
<span>Bestand: ${ file.name }</span
><button
type="button"
#click="remove(filelist.indexOf(file))"
title="Verwijder bestand"
>
<i class="icon-cross"></i>
</button>
</li>
</ul>
<label for="file-upload-bank" class="block cursor-pointer" v-else>
<div>Kies een bestand of drag and drop het bestand</div>
</label>
</div>
</div>
</template>
In my page I use to place it.
Unfortunately, I get this error (which I get for all methods I use in the template).
vue.common.dev.js:630 [Vue warn]: Property or method "dragover" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> <Test> at assets/scripts/vue-components/forms/single-file-upload.vue
<Root>
Who knows what I do wrong and how to fix it?
Related
first sorry for my bad English.
I have one component, this component only working for upload image.
I'm running this component to 2 form. First add form, second edit form. Edit modal open and send to props Image URL.
This..
<ua-single-upload :propsImage="editSingleImage" #uploadImage="addSingleImage = $event"></ua-single-upload>
This is so good working. Image:
If I'm reload new photo, working and console give this error: "[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "propsImage""
AND...
This component not working with ADD FORM. I select image, not showing not uploading...
Please help me friends..
I want to be able to add a new image and update the existing one with a component.
This is my Component Codes...
<template>
<div class="singleImageUpdate p-4">
<div class="p-4">
<h4>Kapak Fotoğrafı Seçiniz</h4>
</div>
<div class="p-4">
<input
type="file"
name="fileUrl"
id="file"
ref="fileInput"
#change="onFileChange" />
<label for="file">Yeni Fotoğraf Ekle</label>
<button
class="ml-4"
type="button"
v-if="this.propsImage != null"
#click="onFileDelete"> Fotoğrafı Kaldır </button>
<button
class="ml-4"
type="button"
v-else
disabled
#click="onFileDelete"> Fotoğrafı Kaldır </button>
</div>
<div class="p-4 mt-4">
<small v-if="this.propsImage">
Fotoğraf kırpılmamaktadır, görüntü temsilidir.
</small>
<img
class="mt-4 shadow-lg"
v-if="this.propsImage"
:src="propsImage" />
</div>
</div>
</template>
<script>
export default{
data(){
return{}
},
props: {
propsImage: String
},
methods: {
onFileChange(event) {
const file = event.target.files[0];
this.propsImage = URL.createObjectURL(file);
this.$emit("updateSingleImage", 1);
this.$emit("uploadImage",event.target.files[0]);
},
onFileDelete() {
this.propsImage = "";
const input = this.$refs.fileInput;
input.type = "text";
input.type = "file";
this.$emit("updateSingleImage", 0);
this.$emit("uploadImage", null);
},
}
}
Id say the warning is pretty descriptive, you are mutating the property directly which is a bad practice, since the parent might change the prop value and will therefore overwrite it.
What you should do instead is perhaps:
Create a reactive property inside the data function and use the prop as an initial value:
props: {
propsImage:string
},
data(){
return {
image: this.propsImage
}
}
or if you want to update image whenever propsImage changes:
watch: {
propsImage(newValue){
this.image = newValue
}
}
or If you want to update the prop in the parent component emit the event
computed: {
image: {
get(){
return this.propsImage
},
set(newValue)
{
this.$emit('update:props-image',newValue)
}
}
}
and change the property inside the template of the parent component to <my-component :props-image.sync="myValue" />
Also there is no this context bound to the vue instance in the template is there?
I am trying to update the props data sent to a component on a button click to a single component in vue.
Button click triggers an action loads the data from a config. But this throws the error and the error message was not clear. Find the error here https://imgur.com/a/0psUWKr
If I pass the data directly without the button actions, it works fine.
My Main component
<template>
<div>
<MyList v-if="listItems" :elements="listItems"/>
<button #click="showSlider">Show Slider</button>
</div>
</template>
<script>
// imports the components and config files
export default {
name: "ListView",
data() {
return {
listItems: []
};
},
components: {
MyList
},
methods: {
showSlider: function() {
this.listItems.push(configs['elements'])
},
</script>
NOTE: If i provide the data to listItems by default it works
And MyList file
<template>
<ul>
<li v-for="each in elements" :key="each.id">
{{each.name}}
</li>
</ul>
<template>
<script>
// imports the components and config files
export default {
name: "MyList",
props: {
elements: {
type: Array
}
}
</script>
It should work, in general. But in my case, the issue is with push in the following function.
showSlider: function() {
this.listItems.push(configs['elements'])
},
Storing the same from Vuex and using dispatcher to update the same works like a charm. And I use computed property to load the state from the vueX and pass it to MyList
I've read quite a few docs and tutorials but I'm still not understanding what I'm doing wrong. I've tried rebuilding the simple component several times with no luck. I'm getting the following errors:
[Vue warn]: Error in data(): "ReferenceError: products is not defined"
found in
---> <Products> at resources/assets/js/components/Products.vue
<Root>
app.js:19613:7
ReferenceError: products is not defined
[Vue warn]: Property or method "data" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> <Products> at resources/assets/js/components/Products.vue
<Root>
app.js:19613:7
[Vue warn]: Error in render: "TypeError: _vm.data is undefined"
found in
---> <Products> at resources/assets/js/components/Products.vue
<Root>
Here's my app.js file:
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('products', require('./components/Products.vue'));
const app = new Vue({
el: '.main-container',
data: {
products: []
}
});
and here's the Products.vue:
<template>
<div class="row">
<div class="columns large-3 medium-6" v-for="product in data.products" :key="product.product_key">
<div class="card">
<div class="card-divider">
#{{ product.title }}
</div>
<a :href="product.product_key" target="_blank"><img :src="product.image"></a>
<div class="card-section">
<p>#{{ product.product_description }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return products
},
mounted () {
this.getProducts();
},
methods: {
getProducts() {
axios.get('/products/').then((response) => {
this.products = response.data.results;
}).catch( error => { console.log(error); });
}
}
}
</script>
I know I've probably confused the .vue file with some of the properties I was attempting to set in the app.js-- can anyone set me straight on how the data should be accessed?
Change Products.vue from:
data: function () {
return products
},
To
data() {
return {
products: [],
}
}
There is a difference when it comes to setting the data() object when it's either in the Vue Root object or when it's in components.
In your case, you shouldn't put return products in app.js. The products object will exist in that Products.vue component and can be accessed by this.products.
Sup people!
I got this HTML code here:
// index.html
<div data-init="component-one">
<...>
<div data-init="component-two">
<button #click="doSomething($event)">
</div>
</div>
This basically references a Vue instance inside another Vue instance if I understood everything correctly. The respective JS code is split up in two files and looks like this:
// componentOne.js
new Vue(
el: '[data-init="component-one"]',
data: {...},
methods: {...}
);
// componentTwo.js
new Vue(
el: '[data-init="component-two"]'
data: {...}
methods: {
doSomething: function(event) {...}
}
);
Now, the problem with this is, that doSomething from componentTwo never gets called.
But when I do some inline stuff, like {{ 3 + 3 }}, it gets computed like it should. So Vue knows there is something. And it also removes the #click element on page load.
I tried fiddling around with inline-template as well, but it doesn't really work as I'd expect it to in this situation. And I figured it isn't meant for this case anyway, so I dropped it again.
What would the correct approach be here? And how can I make this work the easiest way possible with how it's set up right now?
The Vue version we use is 2.1.8.
Cheers!
The problem is that you have two vue instances nested to each other.
If the elements are nested, then you should use the same instance or try components
https://jsfiddle.net/p16y2g16/1/
// componentTwo.js
var item = Vue.component('item',({
name:'item',
template:'<button #click="doSomething($event)">{{ message2 }</button>',
data: function(){
return{
message2: 'ddddddddddd!'
}},
methods: {
doSomething: function(event) {alert('s')}
}
}));
var app = new Vue({
el: '[data-init="component-one"]',
data: {
message: 'Hello Vue!'
}
});
<div data-init="component-one">
<button >{{ message }}</button>
<item></item>
</div>
Separate instances work if they are independant of each other.
as follows:
https://jsfiddle.net/p16y2g16/
var app = new Vue({
el: '[data-init="component-one"]',
data: {
message: 'Hello Vue!'
}
});
// componentTwo.js
var ddd = new Vue({
el: '[data-init="component-two"]',
data: {
message: 'ddddddddddd!'
},
methods: {
doSomething: function(event) {alert('s')}
}
});
But when I do some inline stuff, like {{ 3 + 3 }}, it gets computed like it should. So Vue knows there is something.
Because you have parent instance 'componentOne'. It activated Vue for this template. If you need to set another instance inside, you have to separate part of template. Example (it can lag in snippet!) .
Alternative
https://jsfiddle.net/qh8a8ebg/2/
// componentOne.js
new Vue({
el: '[data-init="component-one"]',
data: {
text: 'first'
},
methods: {}
});
// componentTwo.js
new Vue({
el: '[data-init="component-two"]',
data: {
text: 'second'
},
template: `<button #click="doSomething($event)">{{text}}</button>`,
methods: {
doSomething: function(event) {
console.log(event);
}
}
});
<script src="https://vuejs.org/js/vue.min.js"></script>
<div data-init="component-one">
{{text}}
</div>
<div data-init="component-two">
</div>
The button element inside component-two is referenced as a slot in Vue.
The evaluation of the #click directive value happens in the parent component (component-one, which host component-two). Therefor, you need to declare the click handler over there (over component-one).
If you want the handler to be handled inside component-two, you should declare a click directive for the slot element in it's (component-two) template, and pass the handler function, for instance, as a pop.
good luck.
You're doing everything right except you've nested the 2nd Vue instance inside the 1st. Just put it to the side and it will work as expected.
Vue ignores binding more than once to the same element to avoid infinite loops, which is the only reason it doesn't work nested.
Use vue-cli to create a webpack starter app. vue init app --webpack
Then, try to structure your components this way. Read more: https://v2.vuejs.org/v2/guide/components.html#What-are-Components
This is main.js
import Vue from 'vue'
import ComponentOne from './ComponentOne.vue'
import ComponentTwo from './ComponentTwo.vue'
new Vue({
el: '#app',
template: '<App/>',
components: {
ComponentOne,
ComponentTwo
}
})
This is ComponentOne.vue
<template>
<div class="user">
<div v-for="user in users">
<p>Username: {{ user.username }}</p>
</div>
</div>
</template>
<script>
export default {
data () {
return {
users: [
{username: 'Bryan'},
{username: 'Gwen'},
{username: 'Gabriel'}
]
}
}
}
</script>
This is ComponentTwo.vue
<template>
<div class="two">
Hello World
</div>
</template>
<script>
export default {
}
</script>
<div th:if="${msg.replyFloor}">
<div class="msg-lists-item-left">
<span class="msg-left-edit"
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">您在</span>
<span th:text="${msg.topic.title}"
class="msg-left-edit-res"
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">问题回答</span>
<span th:text="${msg.type.name}"
class="msg-left-edit "
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">帖子相关</span>
<span class="msg-left-edit-number" >
产生了<span th:text="${msg.unreadCount} ? : ${msg.unreadCount} + '条新' : ${msg.unreadCount} + '条' "
th:class="${msg.unreadCount} ? : 'number-inner':''">2132条</span>回复
</span>
</div>
<div class="msg-lists-item-right">
<span th:text="${msg.lastShowTime}">2017-8-10</span>
</div>
</div>
First of all : I'm using laravel spark and the given setup of vue that comes with spark.
I have a "home" component with the prop "custom". Within custom there's a "passwords" array. (Entry added by code of directive, it's initialized empty)
My component ( alist) which should be bound against the data
<template id="passwords-list-template">
<div class="password" v-for="password in list">
<ul>
<li>{{ password.name }}</li>
<li>{{ password.description }}</li>
</ul>
</div>
</template>
<script>
export default {
template: '#passwords-list-template',
props: ['list'],
};
</script>
Usage
<passwords-list :list="custom.passwords"></passwords-list>
Using vue devtools I can see that my data is updating, however my list is not. Also other bindings like
<div v-show="custom.passwords.length > 0">
Are not working ...
UPDATE : Parent component (Home)
Vue.component('home', {
props: ['user', 'custom'],
ready : function() {
}
});
Usage
<home :user="user" :custom="spark.custom" inline-template>
Update 2: I played around a little bit using jsfiddle. It seems like changing the bound data object using $root works fine for me when using a method of a component. However it does not work when trying to access it using a directive
https://jsfiddle.net/wa21yho2/1/
There were a lot of errors in your Vue code. First of all, your components where isolated, there wasn't an explicit parent-child relationship.Second, there were errors in the scope of components, you were trying to set data of the parent in the child, also, you were trying to set the value of a prop, and props are by default readonly, you should have written a setter function or change them to data. And finally, I can't understand why were you trying to use a directive if there were methods and events involve?
Anyway, I rewrote your jsfiddle, I hope that you find what you need there. The chain is Root > Home > PasswordList. And the data is in the root but modified in home, the last component only show it. the key here are twoWay properties, otherwise you wouldn't be able to modify data through properties.
Here is a snippet of code
Home
var Home = Vue.component('home', {
props: {
user: {
default: ''
},
custom: {
twoWay: true
}
},
components: {
passwordList: PasswordList
},
methods: {
reset: function () {
this.custom.passwords = [];
}
}
});
// template
<home :custom.sync="spark.custom" inline-template>
{{custom | json}}
<button #click="reset">
reset in home
</button>
<password-list :list="custom.passwords"></password-list>
<password-list :list="custom.passwords"></password-list>
</home>
Here is the full jsfiddle