Problems that should be paid attention to when el-upload encounters v-for

Although element-ui is hardly updated now, it cannot be denied its excellence
. Today I want to talk about the excellent component el-upload in this excellent framework.

Friends who have used element-ui may have used its upload component (el-upload)
. The documentation of el-upload alone is very comprehensive, and the operation is also very easy to use, and there is nothing wrong with it.

But when el-upload encounters v-for, there are many more problems to face.
I have encountered it before, so I wrote this article specially

Note: The complete demo is attached at the end of the article


Once, I encountered such a demand:

  • There is an array that can be increased or decreased
  • Each item in the array has the function of uploading pictures



My first reaction, simple:

<!-- 大概看下就行, 不是完整代码 -->
<div v-for="(item, index) in lists">
	<!--something-->
	<el-upload></el-upload>
	<button>删除</button>
</div>
<button>新建</button>

Mess up



Question 1

After several rounds of betting, everything that should be displayed comes out:

<!-- 大概看下就行, 不是完整代码 -->
<div v-for="(item, index) in lists">
	<el-upload :before-upload="handleBeforeUpload">
		<i class="el-icon-plus"></i>
	</el-upload>
	<el-button type="danger" @click="remove(item, index)">删除</el-button>
</div>
<el-button type="primary" @click="add">新建</el-button>
  • Double-click "New", two new items have been created
  • Choose a picture, and it is displayed as you wish
  • Delete the first item, damn it! Why is the second item missing?

This is because v-for lacks the key

Vue is different from react. The key in the loop is not a mandatory requirement. In many cases, there is no problem with missing keys in the list.
However, adding and deleting items must have a key.



Question 2

Because handleBeforeUpload is a public method, only the past picture can not be distinguished from which item the picture is from,
so a parameter index is given to handleBeforeUpload

<!-- 大概看下就行, 不是完整代码 -->
<div v-for="(item, index) in lists" :key="item.key">
	<el-upload :before-upload="handleBeforeUpload(index)">
		<i class="el-icon-plus"></i>
	</el-upload>
	<el-button type="danger" @click="remove(item, index)">删除</el-button>
</div>
<el-button type="primary" @click="add">新建</el-button>
handleBeforeUpload(file) {
    
    
	console.log(file)
},

It's okay if you don't add it, but after adding index, the file received by handleBeforeUpload becomes the value of index, and the original file is gone

This is because the original dom data is covered by index, and there is no trace of file in handleBeforeUpload()

The lack of original data is not easy, just use $event to solve it



Question 3

It's a mess again, the code is changed to this

<!-- 大概看下就行, 不是完整代码 -->
<div v-for="(item, index) in lists" :key="item.key">
	<el-upload :before-upload="handleBeforeUpload($event, index)">
		<i class="el-icon-plus"></i>
	</el-upload>
	<el-button type="danger" @click="remove(item, index)">删除</el-button>
</div>
<el-button type="primary" @click="add">新建</el-button>

But at this time, the console directly reported an error

[VUE WARN]: The attribute or method "$event" is not defined on the instance, but the attribute or method is referenced during presentation. By initializing the property, ensure that this property is reactive, whether in the "data" option, or for class-based components.

As for what does it mean? To be honest, I don’t understand very well, but in my experience, "$event has a problem."

At this time, notice: before-upload="", before-upload is not the attribute that triggers the event, its function is only to pass the method down, so it is unwise to use $event in this position



Question 4

In this situation, I decided to take it hard, and simply use the idea of ​​closure, encapsulating el-upload into a component to use

<!-- 参考文末的完整 demo -->

The idea is probably like this

  1. Encapsulate el-upload into a new component MyUpload
  2. Pass all the parameters and methods required by el-upload to MyUpload
  3. Also pass index to MyUpload
  4. Take before-upload as an example, set el-upload: before-upload="imgBeforeUpload"
  5. When el-upload triggers before-upload, execute imgBeforeUpload
  6. The file is received in imgBeforeUpload. At this time, you only need to use arguments and uploadId to pass the data into the beforeUpload method inherited from MyUpload
  7. Finally, the required file data and corresponding index data can be obtained in the parent component


to sum up

  • v-for to add key
  • For example, before-upload, you cannot use $event, if you pass in data, it will overwrite the original data
  • Encapsulate the el-upload component again and make it a new component, so that it can save the original data and pass in custom data


Complete demo

<!DOCTYPE html>
<html>

	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta charset="utf-8">
		<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
		<title>当 el-upload 遇上 v-for 时应该注意的问题</title>
		<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/theme-chalk/index.min.css">
		<!--<link rel="stylesheet" href="./jvw/element-ui/[email protected]/element-ui.2.4.0.css">-->
	</head>

	<body>

		<div id="swq">
			<App></App>
		</div>

		<script type="text/x-template" id="App-template">
			<div>
				<div v-for="(item, index) in lists" :key="item.key">
					<my-upload :action="ossSign.host" :uploadId="index" :param="param" :fileList="item.faceList" :before-upload="handleBeforeUpload" :onSuccess="handleOnSuccess" :onError="handleOnError"></my-upload>
					<el-button type="danger" @click="remove(item, index)">删除</el-button>
				</div>
				<el-button type="primary" @click="add">新建</el-button>
			</div>
		</script>

		<script type="text/x-template" id="MyUpload-template">
			<div>
				<el-upload :action="action" :limit="1" :data="param" :file-list="fileList" :before-upload="imgBeforeUpload" :on-success="imgSuccess" :on-error="imgError" list-type="picture-card">
					<i class="el-icon-plus"></i>
				</el-upload>
			</div>
		</script>

		<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.6/vue.min.js"></script>
		<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.14.1/index.js"></script>
		<!--<script src="./jvw/vue/vue.2.5.16.js"></script>-->
		<!--<script src="./jvw/element-ui/[email protected]/element-ui.2.4.0.js"></script>-->

		<script type="text/javascript">
			var MyUpload = {
     
     
				template: "#MyUpload-template",
				props: {
     
     
					action: null,
					param: {
     
     },
					uploadId: null, //接收到的自定义的参数
					beforeUpload: Function,
					onSuccess: Function,
					onError: Function,
					onRemove: Function,
					fileList: null,
				},
				data: function() {
     
     
					return {
     
     }
				},
				methods: {
     
     
					imgRemove() {
     
     
						this.onRemove(...arguments, this.uploadId);
					},
					imgSuccess() {
     
     
						this.onSuccess(...arguments, this.uploadId);
					},
					imgError() {
     
     
						this.onError(...arguments, this.uploadId);
					},
					imgBeforeUpload(file) {
     
     
						this.beforeUpload(...arguments, this.uploadId);
					},
				},
			};
			var App = {
     
     
				template: "#App-template",
				data: function() {
     
     
					return {
     
     
						ossSign: {
     
     
							host: 'https://jsonplaceholder.typicode.com/posts/',
							key: ''
						},

						param: {
     
     },

						lists: [{
     
     
							faceList: [],
							key: this.getRandom(),
						}],

						dialogImageUrl: '',
						dialogVisible: false,
					}
				},
				components: {
     
     
					MyUpload,
				},
				methods: {
     
     
					handleOnSuccess(response, file, fileList, uploadId) {
     
     
						console.log("handleOnSuccess: ", response, file, fileList, uploadId)
					},

					handleOnError(response, file, fileList, uploadId) {
     
     
						console.log("handleOnError: ", response, file, fileList, uploadId)
					},

					handleBeforeUpload(file, uploadId) {
     
     
						console.log("handleBeforeUpload: ", file, uploadId)
					},

					add() {
     
     
						this.lists.push({
     
     
							faceList: [],
							key: this.getRandom(),
						})
					},

					remove(item, index) {
     
     
						this.lists.splice(this.lists.indexOf(item), 1);
					},

					getRandom() {
     
     
						// 生成个临时 key
						return ~~(Math.random() * 10000) + "" + Date.now()
					},

				},
			};
			var vu = new Vue({
     
     
				el: "#swq",
				components: {
     
     
					App: App,
				},
			})
		</script>
	</body>

</html>

//end

Guess you like

Origin blog.csdn.net/u013970232/article/details/111217629