首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么我的表单再次上传图像?

为什么我的表单再次上传图像?
EN

Stack Overflow用户
提问于 2021-09-14 21:41:55
回答 1查看 141关注 0票数 0

我正在开发一个带有Jetstream和惯性的Laravel 8应用程序,我创建了一个具有两个文本域和一个图像上传域的表单。问题是,当我编辑资源并上传新图像时,一切正常,但当我在第一次之后立即尝试保存资源时,它看起来像是再次上传图像,即使第一次上传字段后的数据是图像的URL。这样做的目的是为了防止在第二次编辑时上传第二张图像,因为图像是相同的-没有变化。如果您在第一次编辑后重新加载页面并再次保存数据,它将正常工作,没有图像上传。

我上传了一段视频here

我假设书签下面的加载指示器是在发出ajax请求时由InertiaJS以某种方式完成的。或者它正在重新加载整个页面?

我在这上面花了几个小时,用谷歌搜索了各种东西,查看并修补了代码,阅读了InertiaJS文档,但什么也没有找到!

我正在使用S3上传图像到。此外,我还使用了Spatiemedia-library包来处理图像。

下面是我的Edit页面组件:

代码语言:javascript
复制
<template>
    <app-layout title="Edit author information">
        <div>
            <div class="max-w-7xl mx-auto py-5 sm:px-6 lg:px-8">
                <div>
                    <create-or-update-author-form :author="author" method="PUT" :blank_avatar_image="$page.props.assets.blank_avatar_image"/>
                </div>
            </div>
        </div>
    </app-layout>
</template>

<script>
    import AppLayout from '@/Layouts/AppLayout.vue'
    import CreateOrUpdateAuthorForm from '@/Pages/Authors/Partials/CreateOrUpdateAuthorForm.vue'

    export default {
        props: ['sessions', 'author'],

        components: {
            AppLayout,
            CreateOrUpdateAuthorForm,
        },
    }
</script>

下面是我的CreateOrUpdateAuthorForm组件:

代码语言:javascript
复制
<template>
    <jet-form-section @submitted="save">
        <template #title>
            <span v-if="method === 'POST'">Create new author</span>
            <span v-else-if="method === 'NONE'">Viewing author</span>
            <span v-else>Update author information</span>
        </template>

        <template #form>
            <!-- Photo -->
            <div class="col-span-6 sm:col-span-4">
                <!-- Photo File Input -->
                <input type="file" class="hidden"
                            ref="photo"
                            @change="updatePhotoPreview">

                <jet-label for="photo" value="Photo" />

                <!-- Current Photo -->
                <div class="mt-2" v-show="! photo_preview">
                    <img v-if="photo_or_blank_image" :src="photo_or_blank_image" :alt="author.name" class="rounded-full h-20 w-20 object-cover">
                </div>

                <!-- New Photo Preview -->
                <div class="mt-2" v-show="photo_preview">
                    <span class="block rounded-full w-20 h-20"
                          :style="'background-size: cover; background-repeat: no-repeat; background-position: center center; background-image: url(\'' + photo_preview + '\');'">
                    </span>
                </div>

                <jet-secondary-button v-if="method !== 'NONE'" class="mt-2 mr-2" type="button" @click.prevent="selectNewPhoto">
                    Select A New Photo
                </jet-secondary-button>

                <jet-secondary-button v-if="author.photo && method !== 'NONE' && !author_photo_is_blank" type="button" class="mt-2" @click.prevent="deletePhoto">
                    Remove Photo
                </jet-secondary-button>

                <jet-input-error :message="form.errors.photo" class="mt-2" />
            </div>

            <!-- Name -->
            <div class="col-span-6 sm:col-span-4">
                <jet-label for="name" value="Name" />
                <jet-input id="name" type="text" class="mt-1 block w-full disabled:bg-gray-100" v-model="form.name" autocomplete="name" :disabled="disabled" />
                <jet-input-error :message="form.errors.name" class="mt-2" />
            </div>

            <!-- Email -->
            <div class="col-span-6 sm:col-span-4">
                <jet-label for="bio" value="Bio" />
                <jet-input id="bio" type="text" class="mt-1 block w-full disabled:bg-gray-100" v-model="form.bio" :disabled="disabled" />
                <jet-input-error :message="form.errors.bio" class="mt-2" />
            </div>
        </template>

        <template v-if="!disabled" #actions>
            <jet-button :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
                <span v-if="method === 'POST'">Create</span>
                <span v-else>Save</span>
            </jet-button>

            <progress-bar :progress="form.progress"/>

            <jet-action-message :on="form.wasSuccessful" class="ml-3">
                <span v-if="method === 'POST'">Created.</span>
                <span v-else>Saved.</span>
            </jet-action-message>
        </template>
    </jet-form-section>
</template>

<script>
    import JetButton from '@/Jetstream/Button.vue'
    import JetFormSection from '@/Jetstream/FormSection.vue'
    import JetInput from '@/Jetstream/Input.vue'
    import JetInputError from '@/Jetstream/InputError.vue'
    import JetLabel from '@/Jetstream/Label.vue'
    import JetActionMessage from '@/Jetstream/ActionMessage.vue'
    import JetSecondaryButton from '@/Jetstream/SecondaryButton.vue'
    import ProgressBar from '@/Shared/Elements/ProgressBar.vue'
    import Utils from '@/Shared/Utils'
    import Forms from '@/Shared/Forms'

    export default {
        components: {
            JetActionMessage,
            JetButton,
            JetFormSection,
            JetInput,
            JetInputError,
            JetLabel,
            JetSecondaryButton,
            ProgressBar,
        },

        props: {
            author: {
                type: Object,
                default: {
                    id: null,
                    name: '',
                    bio: '',
                    photo: null,
                }
            },
            blank_avatar_image: {
                type: String,
                default: null,
            },
            method: {
                type: String,
                default: 'POST',
            },
            disabled: {
                default: false
            }
        },

        data() {
            return {
                form: this.$inertia.form({
                    _method: this.method,
                    id: this.author.id,
                    name: this.author.name,
                    bio: this.author.bio,
                    photo: this.author.photo,
                }),

                formDetails: {
                    routes: {
                        store: route('authors.store'),
                        update: this.author.id 
                         ? route('authors.update', { author : this.author.id})
                         : null,
                    },
                    errorBag: 'createOrUpdateAuthor',
                },

                photo_preview: null,
            }
        },

        methods: Forms,

        computed: {
            photo_or_blank_image() {
                return this.author.photo ? this.author.photo : this.blank_avatar_image;
            },
            author_photo_is_blank() {
                return Utils.isBlankAvatarImage(this.author.photo);
            }
        }
    }
</script>

下面是我的Forms类:

代码语言:javascript
复制
var Forms = {
    save() {
        var self = this;

        // if method is NONE don't submit form
        if (this.method === 'NONE') {
            return false;
        }
        
        if (this.$refs.photo && this.$refs.photo.files[0]) {
            this.form.photo = this.$refs.photo.files[0];
        }

        var request = {
            errorBag: this.formDetails.errorBag,
            preserveScroll: true,
            forceFormData: true,
        };

        var route = this.formDetails.routes.store;

        if (this.method === 'PUT') {
            route = this.formDetails.routes.update;
        }

        this.form.wasSuccessful = false;
        this.form.post(route, request);
    },

    selectNewPhoto() {
        this.$refs.photo.click();
    },

    updatePhotoPreview() {
        const photo = this.$refs.photo.files[0];

        if (! photo) return;

        this.author.photo = photo;

        const reader = new FileReader();

        reader.onload = (e) => {
            this.photo_preview = e.target.result;
        };

        reader.readAsDataURL(photo);
    },

    deletePhoto() {
        this.author.photo = null;
        this.form.photo = null;
        this.photo_preview = this.blank_avatar_image;
        this.clearPhotoFileInput();
    },

    clearPhotoFileInput() {
        if (this.$refs.photo?.value) {
            this.$refs.photo.value = null;
        }
    }
}

export default Forms;

下面是处理请求的AuthorsController,我只复制了editupdate方法,因为它们是唯一相关的方法:

代码语言:javascript
复制
<?php

namespace App\Http\Controllers;

use App\Actions\Zavoon\Authors\AuthorActions;
use App\Models\Author;
use Illuminate\Http\Request;
use Laravel\Jetstream\Jetstream;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;

class AuthorsController extends Controller
{
    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $author = Author::where('id', $id)
            ->where('team_id', Auth::user()->currentTeam->id)
            ->first();

        if (!$author) {
            abort(404);
        }

        return Inertia::render('Authors/Edit', ['author' => $author->id])
            ->with('author', $author);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id, AuthorActions $action)
    {
        $author = $action->update($request->all());

        $success = 'Author information updated.';

        session()->flash('success', $success);

        return Redirect::route('authors.edit', ['author' => $author->id]);
    }
}

下面是我的AuthorActions类,它处理与作者相关的逻辑:

代码语言:javascript
复制
<?php

namespace App\Actions\Zavoon\Authors;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use App\Models\Author;
use App\Rules\Rules;
use Illuminate\Validation\Rule;

class AuthorActions
{
    /**
     * Creates new author
     */
    public function create(array $input)
    {
        if ($this->validate($input, 'create')) {
            $user = Auth::user();
            $input['user_id'] = $user->id;
            $input['team_id'] = $user->currentTeam->id;
            $author = new Author();

            $author->fill($input);
            $author->save();

            $author->updatePhoto($input);

            return $author;
        }

        return false;
    }
    
    /**
     * Updates author
     *
     * @param  array  $input
     * @return boolean|Author
     */
    public function update(array $input)
    {
        if ($this->validate($input, 'update')) {
            $author = Author::find($input['id']);

            $author->fill($input);
            $author->save();

            $author->updatePhoto($input);

            return $author;
        }

        return false;
    }

    /**
     * Validates input
     */
    public function validate(array $input, $type)
    {
        $user = Auth::user();
     
        $rules = [
            'name' => ['required', 'string', 'max:255'],
            'bio' => ['required', 'string', 'max:4000'],
            'photo' => Rules::photo($type),
        ];

        if ($type === 'update') {
            $rules['id'] = [
                'required', 
                Rule::exists('authors')->where(function ($query) use ($input, $user) {
                    return $query
                        ->where('id', $input['id'])
                        ->where('team_id', $user->currentTeam->id);
                }),
            ];
        }

        Validator::make($input, $rules)->validateWithBag('createOrUpdateAuthor');

        return true;
    }
}

下面是我的Rules类,它提供了一些验证规则:

代码语言:javascript
复制
<?php

namespace App\Rules;

use App\Rules\ImageOrLink;

class Rules
{
    public static function photo($type = 'create')
    {
        if ($type === 'create') {
            return ['nullable', self::mimes(), self::maxImageSize()];
        } else {
            return ['nullable', new ImageOrLink()];
        }
    }

    public static function mimes()
    {
        return 'mimes:jpg,jpeg,png';
    }

    public static function maxImageSize() 
    {
        return 'max:5120';
    }
}

最后是我的ImageOrLink规则,它用于验证上传的图片:

代码语言:javascript
复制
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Validator;
use App\Rules\Link;

class ImageOrLink implements Rule
{
    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Determine if the validation rule passes.
     * True if it is an image upload or a valid url.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        if (Link::isLink($value)) { 
            return true;    
        } else {
            $validator = Validator::make([
                'image' => $value
            ], [
                'image' => ['required', Rules::mimes(), Rules::maxImageSize()],
            ]);

            if ($validator->fails()) {
                return false;
            }

            return true;
        }

        return false;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'This needs to be an image upload or a link to an image.';
    }
}

任何想法都很感谢!我真的不明白为什么会这样。

编辑1:这似乎与惯性的形态助手有关。当表单提交一次时,form.photo不会使用新值进行更新。虽然不知道为什么。

EN

回答 1

Stack Overflow用户

发布于 2021-09-15 21:27:37

解决方案很奇怪,仍然不知道为什么会发生这种情况。也许这是一个惯性错误。因此,在成功提交请求后,只需执行以下操作:

代码语言:javascript
复制
this.form.photo = this.author.photo;

它在Forms.js中有自己的方法,重复一遍,它在onSuccess回调中被调用。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69184807

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档