Laravel在验证后如何保留Select2的旧输入值

11

我在我的 Web 应用程序中使用 Select2。我使用 Ajax 加载我的 Select2 框。当验证失败时,除了 Select2 框之外,所有输入都填充为之前的值。那么在表单验证失败后如何恢复旧值呢?我尝试使用 Request::old('x'),但它会插入值(在我的情况下是用户 ID),而不是选定的文本。因此,例如文本 John 将变成选择框中的 27。如何获取文本?

<select id="customer" name="customer" class="searchselect searchselectstyle">
</select>

JavaScript代码:

token = '{{csrf_token()}}';

$(".searchselect").select2({
    ajax: {
        dataType: "json",
        type: "POST",
        data: function (params) {
            return {
                term: params.term,
                '_token': token,
                'data' : function(){
                    var result = [];
                    var i = 1;
                    $('.searchselect').each(function(){
                        result[i] = $(this).val();
                        i++;
                    });
                    return result;
                }
            };
        },
        url: function() {
            var type = $(this).attr('id');
            return '/get' + type;
        },
        cache: false,
        processResults: function (data) {
            return {
                results: data
            };
        }
    }
});

编辑

到目前为止我发现的唯一(肮脏)的解决方案如下:

 <select id="customer" name="customer" class="searchselect searchselectstyle">
    @if(Request::old('customer') != NULL)
        <option value="{{Request::old('customer')}}">{{$customers->where('id', intval(Request::old('customer')))->first()->name}}</option>
    @endif
</select>

$customers 是所有客户的列表,这意味着对于每个 Select2 框,我需要查询一大堆项目才能使其工作。如果我们谈论每个 Select2 框有数千行,那么这将非常低效。

我猜一定有更好的解决方案。谁可以帮助我?

9个回答

2
通常,如果要以编程方式设置select2的值,则可以使用.val()方法,然后调用.trigger('change') (根据其文档)(以及类似于此类问题在SO上的其他查询)。但是,select2自己在其文档中有关于预选远程数据源选项的内容。
基本上,他们的建议是(在初始化您的基于AJAX的<select>之后):
  • 使用预选的 ID,对新的 API 端点进行另一个 AJAX 调用
  • 在 AJAX 调用完成后,从一个 Promise 函数 (.then()) 动态创建一个新选项并将其附加到底层的 <select>
    • 也可以使用一些常规的 jQuery 回调链接函数来实现此功能
  • 触发 change 事件
  • 触发 select2:select 事件 (并传递整个 data 对象)

假设您已经将旧数据闪存到会话中,在多种方式下,Laravel 提供了方便的访问先前请求的输入,特别是这三种方式:

  • 通过Request类进行静态访问,例如Request::old('customer')(OP中的示例)
  • 全局old() helper,例如old('customer'),如果给定字段没有旧输入,则返回null,并且可以作为第二个参数具有默认值
  • 在控制器中使用Request实例的old()方法,例如$request->old('customer')

全局helper方法更常用于在Blade模板中使用,就像其他答案中的一些示例那样,并且在您不需要操作该值并且可以直接将其插入时非常有用,例如文本输入。

最后一种方法可能会为您提供所需的答案 - 而不是在视图内查询整个集合,您可以从控制器中操作集合(类似于 OP,但应该更好,因为它不会在视图中解析它)或者根据旧 ID 从控制器进行另一个查询,并获取所需的数据而无需遍历集合(开销较小)。
$old_customer = Customer::find($request->old('customer'));

无论如何,您都可以在刀片模板处理任何内容之前轻松获取特定的数据(作为视图变量)。
无论您选择如何注入数据,它仍将遵循select2建议的模式:
- 获取预选数据 - 为其创建一个选项 - 触发相应的事件
唯一的区别是您不需要从另一个API端点获取数据(除非出于其他编程原因需要)。

1
我最终使用了与您类似的流程。但是我的刀片模板使用了htmlcollection包。
控制器:-
假设您在create()方法中。当验证失败时,它将重定向回创建页面。从此页面,您可以重新填充列表。
$customer_list = [];
if(old('customer') != NULL){
    $customer_list = [old('customer') => $customers->where('id', old('customer'))->first()->name];        
}

Blade视图:

{{ Form::select('customer', $customer_list, null,  ['class' => 'searchselect searchselectstyle', 'id' => 'customer']) }}

1

我使用了一个隐藏的输入框来完成它,效果很好:

这个表单是在弹出窗口中显示的,并且使用了Jquery-UJS进行ajax操作。

表单:

<form action="{{ route('store_item', $order) }}" method="POST" data-remote="true">
    {{ csrf_field() }}
    <div class="form-group{{ $errors->has('item_id') ? ' has-error' : '' }}">
        <label class="control-label col-sm-2" for="item_id">Item: </label>
            <div class="col-sm-10">
                <select name="item_id" class="form-control" id="item_id">
                    @if(old('item_id')  != null)
                        <option value="{{ old('item_id') }}" selected="selected">
                            {{ old('item_title') }}
                        </option>
                    @endif
                </select>
            </div>
            {!! $errors->first('item_id', '<p class="text-center text-danger"<strong>:message</strong></p>') !!}
        </div>
        <input type="hidden" id="item_title" name ="item_title" value="{{ old('item_title') }}" />
        <div class="form-group{{ $errors->has('quantity') ? ' has-error' : '' }}">
            <label class="control-label col-sm-2" for="quantity">Cantidad: </label>
            <div class="col-sm-10">
                <input name="quantity" type="number" class="form-control" id="quantity" value="{{ old('quantity') }}"/>
            </div>
            {!! $errors->first('quantity', '<p class="text-center text-danger"><strong>:message</strong></p>') !!}
        </div>
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancelar</button>
        <button type="submit" class="btn btn-primary" data-disable-with="Guardando...">Guardar</button>
    </form>

JAVASCRIPT:

<script type="text/javascript">
        $(document).ready(function(){
            $('#item_id').select2({
                placeholder: 'Elige un item',
                ajax: {
                    url: '{{ route('select_item_data') }}',
                    dataType: 'json',
                    delay: 250,
                    processResults: function (data) {
                        return {
                            results:  $.map(data, function (item) {
                                return {
                                    text: item.title,
                                    id: item.id
                                }
                            })
                        };
                    },
                    cache: true
                }
            });

            $('#item_id').on('change', function(e){
                var title = $(this).select2('data')[0].text;
                $('#item_title').val(title);
            });
        });
    </script>

存储方法中的验证(控制器):

$validator = Validator::make($request->all(), [
            'item_id' => 'required',
            'quantity' => 'required'
        ]);

        if ($validator->fails()) {
            return redirect()
                ->route('create_item', $order)
                ->withInput($request->all())
                ->withErrors($validator);
        }

非常重要的是在重定向中发送“withInput”和“withErrors”,因为我们正在使用弹出窗口和ajax,它们会重新创建并且不保留旧值。

0

你的代码有点混乱。我不明白为什么你要使用 POST 请求来使用 ajax 获取数据并填充 select2 框。

假设使用 ajax 调用返回的 data 格式如下。

   [
     {
     "id": "Some id",
     "text": "Some text"
     },
     {
     "id": "ID 2",
     "text": "Text 2"
     },
    ]

现在你可以通过以下方式在ajax调用中传递额外的参数:

pass in an extra parameter to your ajax call as below

url: function() {
                    var type = $(this).attr('id');
                   @if(old('customer'))
                     return '/get' + type + '?customer='+ {{ old('customer') }};
                   @else
                     return '/get' + type;
                   @endif
                } 

现在,在您的控制器中,当返回数据时,可以为与特定ID匹配的ID抛出一个额外的属性selected:true

if( Request::has('customer') && Request::input('customer') == $id ) 
{
 [
 "id" => $id,
 "text" => $text,
 "selected" => "true"
 ]
}
else
{
 [
 "id" => $id,
 "text" => $text,
 ]
}

你的 url:function(){} 在验证失败后页面重定向时被调用,但您打算如何停留在select2预选择框? - Shiro

0

也许你可以尝试在ajax调用结束后执行以下操作:

var oldCustomer = $('#customer > option[value={{ Request::old('customer') }}]');

if (oldCustomer.length > 0) {
    oldCustomer.attr('selected', 'selected');
}

不会起作用,因为这会将ID设置在框中,而不是关联的文本中。请记住,select2的数据看起来像这样:[id:...,text:...]。因此,您的解决方案将将ID设置为选项的文本,而不是ID关联的文本。 - Markinson

0
同样的问题;我正在使用类似的解决方案:如果设置了旧的 $id,我获取名称并将其用作视图的变量;请注意,我还转发了 id,因为我也使用此方法来预填充表单(来自另一个地方),但在这种情况下,仅应使用名称,并且在视图中可以使用 {{ old('author_id') }} 作为 id:

在控制器中:

elseif (($request->old('author_id') !== null) && ($request->old('author_id') != '')) {
   $my_author_id = $request->old('author_id');
   $my_name = Author::find($my_author_id)->name;
   return view('admin/url_author.create', compact('my_name', 'my_author_id'));
}

而在视图中(更准确地说,在用于创建和编辑的部分中):

@if (isset($record)) // for use in edit case with laravelcollective)
   <select class="form-control js-data-author-ajax" id="author_id" name="author_id">
      <option value="{{ $record->author_id }}">{{ $record->author->name }}</option>
   </select>
@else
@if (isset($my_name)) // old input after validation + pre-filling cases
   <select class="form-control js-data-author-ajax" id="author_id" name="author_id">
      <option value="{{ $my_author_id }}">{{ $my_name }}</option>
   </select>
@else // for create cases
   <select class="form-control js-data-auteur-ajax" id="auteur_id" name="auteur_id">
      <option></option>
   </select>
@endif
@endif

0
如果我理解正确,我可以建议您为每个select2框隐藏输入<input type="hidden" name="customer_name" value="{{old('customer_name', '')}}">,在select2的更改事件之后,您可以插入所选名称(例如John)。因此,如果验证失败,则会出现以下情况:
<select id="customer" name="customer" class="searchselect searchselectstyle">
@if(!is_null(old('customer')))
    <option value="{{old('customer')}}">{{old('customer_name')}}
   </option>
@endif
</select>

0

我认为你的解决方案基本上是正确的。你说$customers列表会变得非常大。

$customers->where('id', intval(Request::old('customer')))->first()

你是否需要将列表存储在变量 $customers 中?你可以直接搜索所需的 ID。
App\Customer::where('id', intval(Request::old('customer')))->first()

按ID搜索不应该低效。否则,您可以将名称与表单一起发送并将其存储在旧请求中。下面显示了一些(肮脏的)JavaScript代码。

$("#form").submit( function() {
  var sel = document.getElementById("customer");
  var text= sel.options[sel.selectedIndex].text;
$('<input />').attr('type', 'hidden')
  .attr('name', "selected_customer_name")
  .attr('value', text)
  .appendTo('#form');
  return true;
});

那就像yrv 16s的回答一样:

<option value="{{old('customer')}}">{{old('selected_customer_name')}}

-1
你可以这样做:
首先在控制器中使用pluck助手将标记传递到视图,如下所示:
public function create()
{
    $tags= Customer::pluck('name','name');

    return view('view',compact('tags'));
}

然后在你的表单中尝试这样做:

   {!! Form::select('tag_list[]',$tags,old('tag_list'),'multiple','id'=>'tag_list']) !!}

别忘了调用select2函数。

    $('#tag_list').select2();

最后在控制器中:

public function store(ArticleRequest $request)
 {
    $model = new Model;
    $tags=$request->input('tag_list');

    $model->tag($tags);
  }  

注意标签函数不是 Laravel 中的帮助程序,您需要自己实现它!该函数接受名称并将其附加到某个实例。

祝好运。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接