Решение проблемы с приведением типов при передаче данных из Laravel в Vue.js
При работе с Laravel и Vue.js типичным шаблоном инициализации наших компонентов Vue данными является передача данных контроллера из Laravel в реквизиты Vue. Это позволяет нам использовать контроллеры Laravel для управления нашими данными в стиле MVC.
Хотя это обычная практика, я столкнулся с сложной проблемой приведения типов, когда работаю с отфильтрованными данными. Я покажу вам, как воссоздать проблему и решить ее, чтобы вы могли избежать ее в своих проектах.
Настраивать
Начнем с обычного проекта Laravel. Чтобы узнать о том, как это настроить, вы можете ознакомиться с этим контрольным списком. Помимо базовой настройки, нам нужно будет подделать некоторые данные в нашем HomeController.php
.
В этом файле мы добавим массив $results
в функцию index
.
public function index()
{
$results = [
[
'id' => 1,
'name' => 'Foo',
'status' => 'active',
],
[
'id' => 2,
'name' => 'Bar',
'status' => 'inactive',
],
[
'id' => 3,
'name' => 'Baz',
'status' => 'active',
],
[
'id' => 4,
'name' => 'Biz',
'status' => 'inactive',
],
];
}
Затем мы превратим этот массив в коллекцию, чтобы мы могли применить преобразования, вызывающие проблему.
public function index()
{
$results = collect([
[
'id' => 1,
'name' => 'Foo',
'status' => 'active',
],
[
'id' => 2,
'name' => 'Bar',
'status' => 'inactive',
],
[
'id' => 3,
'name' => 'Baz',
'status' => 'active',
],
[
'id' => 4,
'name' => 'Biz',
'status' => 'inactive',
],
]);
}
Затем мы вернем наше home
представление с этими данными, чтобы мы могли использовать его в нашем компоненте Vue.
public function index()
{
$results = collect([
[
'id' => 1,
'name' => 'Foo',
'status' => 'active',
],
[
'id' => 2,
'name' => 'Bar',
'status' => 'inactive',
],
[
'id' => 3,
'name' => 'Baz',
'status' => 'active',
],
[
'id' => 4,
'name' => 'Biz',
'status' => 'inactive',
],
]);
return view('home', compact('results'));
}
Позаботившись о наших данных, мы можем сосредоточиться на добавлении нашего компонента Vue.
Внутри нашей папки компонентов мы можем добавить файл WhereExample.vue
. Здесь мы просто примем опору results
и отрендерим ее с помощью v-for
.
<template> <div> <p v-for="result in results" :key="result.id">{{ result.name }}</p><br/> </div> </template>
<script> export default { props: { results: { type: Array, required: true, }, }, } </script>
Теперь мы можем зарегистрировать это в нашем app.js
файле и добавить в home.blade.php
.
// app.js Vue.component('where-example', require('./components/WhereExample.vue'));
//home.blade.php <where-example :results="{{ $results }}"></where-example>
Разобравшись с нашими настройками, давайте углубимся в проблему.
Эта проблема
Обратите внимание, когда мы определили нашу results
опору в нашем компоненте Vue, мы указали, что ожидаем массив. Прямо сейчас, если мы проверим, что происходит в наших инструментах разработки Vue, мы увидим, что все работает, как ожидалось.
Теперь о проблеме. Когда мы хотим отфильтровать наши данные в контроллере, мы увидим, что наша опора приводится как объект, а не как массив. Для этого мы добавим предложение ->where()
в нашу $results
коллекцию, чтобы отображались только активные результаты.
public function index()
{
$results = collect([
[
'id' => 1,
'name' => 'Foo',
'status' => 'active',
],
[
'id' => 2,
'name' => 'Bar',
'status' => 'inactive',
],
[
'id' => 3,
'name' => 'Baz',
'status' => 'active',
],
[
'id' => 4,
'name' => 'Biz',
'status' => 'inactive',
],
])
->where('status', 'active');
return view('home', compact('results'));
}
Вернувшись к нашим инструментам разработки Vue, мы видим, что наш массив превратился в объект! И мы видим, что Vue выдает полезную ошибку в консоли.
Это довольно большое дело. Представьте, что мы хотели показать сообщение, если у нас нет никаких результатов. С помощью массива мы могли бы легко использовать .length
, чтобы увидеть, есть ли какие-либо результаты. Но мы не можем использовать .length
с объектом. Внезапно наш компонент Vue сломался бы!
Это всего лишь один пример, но есть множество функций, которые мы могли бы создать, ожидая, что в качестве данных будет массив, который сломается, если получит объект. К счастью, есть довольно простое решение этой проблемы.
Решение
Предложение ->where()
превращает наш массив в объект потому, что оно поддерживает индекс отфильтрованных результатов. Когда Vue получает эти данные с нелинейными индексами, он предполагает, что это должен быть объект.
Хотя это довольно разумно и даже желательно в большинстве случаев, здесь мы все же в недоумении. Но мы можем использовать предложение ->values()
после ->where()
, чтобы сбросить индексы до их линейных значений.
В результате наши результаты снова будут похожи на массив для Vue. Итак, в нашем примере мы можем добавить ->values()
в конец нашей $results
Коллекции, и все снова заработает.
public function index()
{
$results = collect([
[
'id' => 1,
'name' => 'Foo',
'status' => 'active',
],
[
'id' => 2,
'name' => 'Bar',
'status' => 'inactive',
],
[
'id' => 3,
'name' => 'Baz',
'status' => 'active',
],
[
'id' => 4,
'name' => 'Biz',
'status' => 'inactive',
],
])
->where('status', 'active')
->values();
return view('home', compact('results'));
}
Теперь у нас есть отфильтрованные результаты, работающие так, как мы и ожидали.
Заключение
Я определенно потратил некоторое время, пытаясь понять, почему мои компоненты Vue перестали работать из-за простого предложения ->where()
в моем контроллере. Это часто бывает сложно отладить, потому что проблема так далеко удалена от того, где она появляется.
Когда я впервые столкнулся с этой проблемой, я, вероятно, не понял бы ее, если бы не использовал лучшие практики Vue, определяя свои определения опор. Это показывает, насколько они могут быть полезны в сложной ситуации.
Я надеюсь, что этот пост поможет вам избежать или, по крайней мере, отладить эту проблему в ваших проектах. Как всегда, не стесняйтесь задавать мне любые вопросы в Твиттере. И до следующего раза, удачных исследований!