我正在开发一个带有Jetstream和惯性的Laravel 8应用程序,我创建了一个具有两个文本域和一个图像上传域的表单。问题是,当我编辑资源并上传新图像时,一切正常,但当我在第一次之后立即尝试保存资源时,它看起来像是再次上传图像,即使第一次上传字段后的数据是图像的URL。这样做的目的是为了防止在第二次编辑时上传第二张图像,因为图像是相同的-没有变化。如果您在第一次编辑后重新加载页面并再次保存数据,它将正常工作,没有图像上传。
我上传了一段视频here。
我假设书签下面的加载指示器是在发出ajax请求时由InertiaJS以某种方式完成的。或者它正在重新加载整个页面?
我在这上面花了几个小时,用谷歌搜索了各种东西,查看并修补了代码,阅读了InertiaJS文档,但什么也没有找到!
我正在使用S3上传图像到。此外,我还使用了Spatie的media-library包来处理图像。
下面是我的Edit页面组件:
<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组件:
<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类:
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,我只复制了edit和update方法,因为它们是唯一相关的方法:
<?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类,它处理与作者相关的逻辑:
<?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类,它提供了一些验证规则:
<?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规则,它用于验证上传的图片:
<?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不会使用新值进行更新。虽然不知道为什么。
发布于 2021-09-15 21:27:37
解决方案很奇怪,仍然不知道为什么会发生这种情况。也许这是一个惯性错误。因此,在成功提交请求后,只需执行以下操作:
this.form.photo = this.author.photo;它在Forms.js中有自己的方法,重复一遍,它在onSuccess回调中被调用。
https://stackoverflow.com/questions/69184807
复制相似问题