0

I have nested form which works fine, but since i've added upload file to it, it can't get image name.

Logic

  1. I add option with type (e.g. type = checkbox, name = test)
  2. Then I add multiple options for it (e.g. name = option1, photo = abc.jpg and name = option2, photo = def.png)

Now, without photo part all is good. but when i added photo part to children it has 2 issues:

  1. Photo name can't be return in that array
  2. Photo for all options will be set to latest selected image (screenshot below)

Screenshot

one

Code

HTML

<el-form-item label="Option Name">
    <el-col :span="10" style="margin-top:15px;">
        <!-- parent -->
        <div v-for="(index, a) in optionParents" :key="a">
        <el-input placeholder="Please input your variation name" v-model="index.name" class="input-with-select">
            <el-select @change="optionType" v-model="index.type" slot="prepend" placeholder="Select">
                <el-option label="Drop-down" value="dropdown"></el-option>
                <el-option label="Checkbox" value="checkbox"></el-option>
                <el-option label="Radio Button" value="radio"></el-option>
            </el-select>
        </el-input>
        </div>

    </el-col>

    <!-- Children -->
    <el-col class="line text-center" :span="3">Option Value(s)</el-col>
    <el-col :span="11" style="margin-top:15px;">

        <div v-if="selecteOption == 'dropdown'">
            <div v-for="(indexx, b) in optionChilds" :key="b">
                <!-- child's -->
                <el-input v-model="indexx.name" placeholder="Please input your option value" class="input-with-select">
                    <el-button slot="append"  @click="addOptionChild(b)"  type="success" icon="el-icon-plus"></el-button>
                    <el-button slot="append" @click="removeOptionChild(b)" v-show="b || ( !b == optionChilds.lenghth > 1)" type="danger" icon="el-icon-delete"></el-button>
                </el-input>
            </div>
        </div>

        <div v-if="selecteOption == 'checkbox'">
            <div v-for="(indexx, b) in optionChilds" :key="b">
                <el-input v-model="indexx.name" :rows="3" placeholder="Please input your option value" class="input-with-select">
                </el-input>
                <!-- upload image -->
                <el-upload
                    class="upload-demo"
                    action="/api/upload/single"
                    :on-change="handleChange"
                    v-model="indexx.photo"  // as you can see my v-model here is same as my input just is set to photo value
                    :limit="1"
                    :multiple="false"
                    :file-list="fileList"
                    :on-exceed="handleExceed"
                    :on-remove="handleRemove"
                    :on-preview="handlePictureCardPreview"
                    :on-success="handleOptionSuccess"
                    :before-remove="beforeRemove"
                    :auto-upload="true">
                    <el-button size="small" type="primary">Click to upload</el-button>
                    <div slot="tip" class="el-upload__tip">jpg/png files with a size less than 500kb</div>
                </el-upload>
                <!-- upload image -->
                <el-button-group>
                    <el-button size="small" @click="addOptionChild(b)"  type="success" icon="el-icon-plus"></el-button>
                    <el-button size="small" @click="removeOptionChild(b)" v-show="b || ( !b == optionChilds.lenghth > 1)" type="danger" icon="el-icon-delete"></el-button>
                </el-button-group>
            </div>
        </div>

        <div v-if="selecteOption == 'radio'">
            <div v-for="(indexx, b) in optionChilds" :key="b">
                <!-- child's -->
                <el-input v-model="indexx.name" :rows="3" placeholder="Please input your option value" class="input-with-select">
                </el-input>
                <!-- upload image -->
                <el-upload
                    class="upload-demo"
                    action="/api/upload/single"
                    :on-change="handleChange"
                    v-model="indexx.photo" // as you can see my v-model here is same as my input just is set to photo value
                    :limit="1"
                    :multiple="false"
                    :file-list="fileList"
                    :on-exceed="handleExceed"
                    :on-remove="handleRemove"
                    :on-preview="handlePictureCardPreview"
                    :on-success="handleOptionSuccess"
                    :before-remove="beforeRemove"
                    :auto-upload="true">
                    <el-button size="small" type="primary">Click to upload</el-button>
                    <div slot="tip" class="el-upload__tip">jpg/png files with a size less than 500kb</div>
                </el-upload>
                <!-- upload image -->
                <el-button-group>
                    <el-button size="small" @click="addOptionChild(b)"  type="success" icon="el-icon-plus"></el-button>
                    <el-button size="small" @click="removeOptionChild(b)" v-show="b || ( !b == optionChilds.lenghth > 1)" type="danger" icon="el-icon-delete"></el-button>
                </el-button-group>
            </div>
        </div>

    </el-col>
    <el-col :span="24" style="margin-top:15px;">
        <el-button type="primary" @click="addOptions" native-type="submit">Save Options</el-button>
    </el-col>
</el-form-item>

Script

Note: I have removed everything else that wasn't related to this options, all you see in code below is related to options functionality.

export default {
    data() {
        return {
            fileList: [],
            selecteOption: '',
            savedOptions: [],
            optionParents: [
                {
                    name: '',
                    type: ''
                }
            ],
            optionChilds: [
                {
                    name: '',
                    photo: '',
                }
            ],
            form: {
                name: '',
                slug: '',
                price: '',
                new_price: '',
                sku: '',
                qty: 1,
                active: '',
                photo: '',
                shortDesc: '',
                longDesc: '',
                tags: [],
                brand_id: '',
                categories: [],
                user_id: '',
                seoTitle: '',
                seoTags: '',
                seoPhoto: '',
                seoDescription: '',
                variations: [],
                options: [],
                condition: '',
                isbn: '',
                ean: '',
                upc: '',
            },
        }
    },
    methods: {
        addOptionChild(index){
            this.optionChilds.push({name: '', photo: ''});
        },
        removeOptionChild(index){
            this.optionChilds.splice(index, 1);
        },
        optionType: function(value) {
            this.selecteOption = value
        },
        addOptions(e) {
            e.preventDefault();
            axios.post('/api/admin/options/store', {
                childs: this.optionChilds,
                parent: this.optionParents,
            })
            .then(res => {
                this.optionChilds = [
                    {
                        name: '',
                        photo: ''
                    }
                ],
                this.optionParents = [
                    {
                        name: '',
                        type: ''
                    }
                ],
                this.savedOptions.push(res.data.data);
                this.form.options.push(res.data.data.id);
            })
            .catch(error => {
                var errors = error.response.data;
                    let errorsHtml = '<ol>';
                    $.each(errors.errors,function (k,v) {
                            errorsHtml += '<li>'+ v + '</li>';
                    });
                    errorsHtml += '</ol>';

                this.$notify.error({
                    title: 'Error',
                    dangerouslyUseHTMLString: true,
                    message: errorsHtml
                });
            })
        },
        handleOptionSuccess(res, file) {
            this.optionChilds.photo = res.data;  // this s supposed to set uploaded image name in options `photo: ''`
        },
        handleChange(file, fileList) {
            this.fileList = fileList.slice(-3);
        },
        handlePictureCardPreview(file) {
            this.dialogImageUrl = file.url;
            this.dialogVisible = true;
        },
        handleExceed(files, fileList) {
            this.$message.warning(`The limit is 1, you selected ${files.length} files this time, add up to ${files.length + fileList.length} totally, remove old image and try again.`);
        },
        beforeRemove(file) {
            return this.$confirm(`Cancel the transfert of ${ file.name } ?`);
        },
    },

}

Sending data

This is what is sending to back-end when i hit save button

two

Question

  1. How can I update photo: '' data with uploaded image?
  2. How to fix images of getting same file for all options?
18
  • what do you mean of same file of all options ? Commented Jan 23, 2020 at 2:26
  • @Qonvex620 if you see my first screenshot, both my options have file named Asset1-100.jpg(marked with arrow) when i select an image it will be set for both options! Commented Jan 23, 2020 at 2:34
  • @Qonvex620 any idea? Commented Jan 23, 2020 at 3:02
  • I think you cant bind model into your file uploaded. You must attach an event to it and inside of it, get the selected file then initialize that file to your photo Commented Jan 23, 2020 at 3:04
  • 1
    then this code will not work this.optionChilds.photo = res.data; , since your optionChilds is an array right ? Commented Jan 23, 2020 at 3:10

2 Answers 2

1

I think not the good way but will solve your problem as of now.

In your data put new variable like

data() {
  return {
     selected_index: ''
  }

}

now in your button uploaded, set the value of it that match the current index like this

<el-button size="small" type="primary" @click="selected_index = b">Click to upload</el-button>

so now in your handler you could access it's index like this

handleOptionSuccess(res, file) {
    this.optionChilds[this.selected_index].photo = res.data;  // this s supposed to set uploaded image name in options `photo: ''`
},

here how you access it inline with your index. Change handleOptionSuccess to this

:on-success="handleOptionSuccess($event, b)"

now in your function handleOptionSuccess you could do it like

handleOptionSuccess(res, index) {
    this.optionChilds[index].photo = res.data;  // this s supposed to set uploaded image name in options `photo: ''`
},
Sign up to request clarification or add additional context in comments.

15 Comments

there is no other way? because if i change my save button @click to your solution then i will lose ability of saving my options :/
I mean at the moment you select file put this "selected_index = b", I'm not sure if its in your file input maybe
maybe in your <el-upload>, when you click that set the selected_index to b, something like that
we need to send b to handleOptionSuccess i guess
Yes I;m thinking also that but I don't know yet how to do that cause it will overwrite the res and file you pass
|
0

Solved

About my first issue :

I've fixed repeating image issue, by removing :file-list="fileList" after that it each option get separate image

About second issue: (Thanks to Qonvex620 ideas)

I have changed my on-change method to

:on-change="handleChange(b)"

Then added this to my data:

return {
  index: '',
}

And finally my functions to:

handleOptionSuccess(res, file) {
  this.optionChilds[this.index].photo = res.data;
},
handleChange(file, fileList) {
  this.index = file;
},

Now each option of mine gets their stored image name.

Hope it help others as well

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.