Vue.js+Laavel을 사용하여 페이지 스타일을 구현하는 노트

30797 단어 LaravelVue.jstech

컨디션

  • Nuxt 3.0.0-rc.1
  • Laravel 9.9.0
  • https://github.com/tekihei2317/vue-laravel-pagination

    페이지 스타일


    페이지 스타일은 목록을 표시할 때 숫자를 여러 페이지로 나누는 것을 말한다.

    SP A의 페이지 문자 예


    좀 복잡하니까 그림으로 정리해 주세요.

    먼저 페이지의 페이지 번호(①)를 클릭합니다.
    앞에서 예를 들어 페이지 번호 2를 눌렀을 때 ?page=2와 같이 조회 파라미터를 추가하여 요청을 보낸다(②).
    API는 쿼리 매개변수에 따라 OFFSET 문 또는 LIMIT 문을 사용하여 DB에서 해당 데이터를 가져옵니다(③, ④).
    마지막으로 얻은 데이터에 따라 화면을 업데이트한다(⑤).

    API 구현


    Laavel의 API 리소스를 사용하여 수행할 수 있습니다.페이지가 나뉘어진 데이터를 API 리소스로 보내면 페이지 스타일은 필요한 정보(총 페이지 수 및 현재 페이지 번호 등)를 제공합니다.
    우선, 컨트롤러는 모델을 사용하여 데이터를 얻어pagenate 방법으로 붙인다.그런 다음 붙여넣은 데이터를 API 리소스에 전달하여 성형합니다.
    backend/app/Http/Controllers/TaskController.php
    class TaskController extends Controller
    {
        public function __construct(
            private Task $taskModel
        ) {
        }
    
        public function index()
        {
            return TaskResource::collection($this->taskModel->with('status')->paginate(10));
        }
    }
    
    API 리소스는 작업 정보 외에도 관계를 사용하여 작업 상태 이름을 가져옵니다.
    backend/app/Http/Resources/TaskResource.php
    class TaskResource extends JsonResource
    {
        public function toArray($request)
        {
            /** @var \App\Models\Task */
            $task = $this->resource;
            assert($task->relationLoaded('status')); // N+1が起きないようにロードされていることを確認
    
            return [
                'id' => $task->id,
                'name' => $task->name,
                'description' => $task->description,
                'status' => $task->status->name,
            ];
        }
    }
    
    페이지 나누기 데이터(페이지 이름 지정자)를 API 리소스에 제출하면 다음과 같은 정보가 추가됩니다.

    디테일
    links
    첫 페이지, 마지막 페이지, 취득한 페이지의 앞뒤 페이지의 URL입니다.
    meta
    현재 페이지 번호, 마지막 페이지 번호, 페이지 수, 총 수량 등.
    meta.links
    페이지 스타일을 표시하는 링크 집합입니다.후퇴 링크, 현재 페이지의 앞뒤 3페이지, 시작과 마지막 2페이지의 링크를 포함한다.
    예를 들어 총 수량 100건, 각 페이지 3건, 페이지 번호 10이 API를 실행할 때 다음과 같다.
    응답 예제
    $ curl 'localhost:8000/api/tasks?page=10' | jq .
    {
      "data": [
        {
          "id": 28,
          "name": "todo28",
          "description": "28個目",
          "status": "OPEN"
        },
        {
          "id": 29,
          "name": "todo29",
          "description": "29個目",
          "status": "OPEN"
        },
        {
          "id": 30,
          "name": "todo30",
          "description": "30個目",
          "status": "CLOSED"
        }
      ],
      "links": {
        "first": "http://localhost:8000/api/tasks?page=1",
        "last": "http://localhost:8000/api/tasks?page=34",
        "prev": "http://localhost:8000/api/tasks?page=9",
        "next": "http://localhost:8000/api/tasks?page=11"
      },
      "meta": {
        "current_page": 10,
        "from": 28,
        "last_page": 34,
        "links": [
          {
            "url": "http://localhost:8000/api/tasks?page=9",
            "label": "« Previous",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=1",
            "label": "1",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=2",
            "label": "2",
            "active": false
          },
          {
            "url": null,
            "label": "...",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=7",
            "label": "7",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=8",
            "label": "8",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=9",
            "label": "9",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=10",
            "label": "10",
            "active": true
          },
          {
            "url": "http://localhost:8000/api/tasks?page=11",
            "label": "11",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=12",
            "label": "12",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=13",
            "label": "13",
            "active": false
          },
          {
            "url": null,
            "label": "...",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=33",
            "label": "33",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=34",
            "label": "34",
            "active": false
          },
          {
            "url": "http://localhost:8000/api/tasks?page=11",
            "label": "Next »",
            "active": false
          }
        ],
        "path": "http://localhost:8000/api/tasks",
        "per_page": 3,
        "to": 30,
        "total": 100
      }
    }
    
    Laavel의 라우터는 컨트롤러 작업을 수행한 후 작업의 반환값을 성형한 후 커널로 돌아갑니다.동작의 반환값을 성형할 때 반환값toResponse을 호출하는 방법(반환값Illuminate\Contracts\Support\Responsable이 인터페이스를 실시할 때.API 리소스의 상속자인 Json Resource 클래스가 여기에 적용됩니다.
    자원 수집 toResponse 방법에는 페이지 명명기 클래스를 건네주는 경우의 처리가 적혀 있습니다.이쪽을 쫓아가면 상술한 정보를 추가한 처리를 확인할 수 있습니다.
    ResourceCollection.php
    public function toResponse($request)
    {
        if ($this->resource instanceof AbstractPaginator || $this->resource instanceof AbstractCursorPaginator) {
            return $this->preparePaginatedResponse($request);
        }
    
        return parent::toResponse($request);
    }
    
    참조: Illuminate\Http\Resources\Json\ResourceCollection | Laravel API

    프런트엔드 설치


    API 리소스의 meta.links를 사용하여 페이지 스타일의 페이지 번호를 만들었습니다.
    페이지 번호를 클릭하면 API 끝점의 질의 문자열에서 페이지 번호를 가져오고 데이터를 다시 가져옵니다.
    frontend/pages/index.vue
    <script setup>
    const page = ref(1)
    const { data: taskPaginator, refresh } = await useFetch(() => `/api/tasks?page=${page.value}`, {
      baseURL: 'http://localhost:8000',
    })
    
    const onClickLink = (url) => {
      page.value = new URL(url).searchParams.get('page')
      refresh()
    }
    </script>
    
    <template>
      <div>
        <!-- 一覧の表示 -->
        <h2>タスク一覧</h2>
        <table>
          <thead>
            <tr>
              <th>ID</th>
              <th>タスク名</th>
              <th>説明名</th>
              <th>ステータス</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="task in taskPaginator.data">
              <td>{{ task.id }}</td>
              <td>{{ task.name }}</td>
              <td>{{ task.description }}</td>
              <td>{{ task.status }}</td>
            </tr>
          </tbody>
        </table>
    
        <!-- ページネーションの表示 -->
        <div class="pagination">
          <template v-for="link in taskPaginator.meta.links">
            <button
              :disabled="link.url === null"
              class="pagination-link"
              :class="{
                'pagination-link-enabled': link.url !== null,
                'pagination-link-active': link.active,
              }"
              @click="onClickLink(link.url)"
            >
              {{ link.label }}
            </button>
          </template>
        </div>
      </div>
    </template>
    

    페이지 레이아웃 구성 요소


    다른 페이지에서도 페이지 하이픈을 사용할 수 있도록 구성 요소를 만들어 보십시오.
    어셈블리 면:
    frontend/components/custom/Pagination.vue
    <script lang="ts" setup>
    type PaginationLink = {
      url: string | null
      label: string
      active: boolean
    }
    
    type Props = {
      links: PaginationLink[]
      onClickLink: (url: string) => void
    }
    
    const { links, onClickLink } = defineProps<Props>()
    </script>
    
    <template>
      <div class="pagination">
        <template v-for="link in links">
          <button
            :disabled="link.url === null"
            class="pagination-link"
            :class="{
              'pagination-link-enabled': link.url !== null,
              'pagination-link-active': link.active,
            }"
            @click="onClickLink(link.url)"
          >
            {{ link.label }}
          </button>
        </template>
      </div>
    </template>
    
    호출자:
    <custom-pagination
      :links="taskPaginator.meta.links"
      :on-click-link="onClickLink"
      class="task-pagination"
    />
    
    잘 나온다.

    총결산

  • SPA의 페이지 문자는 페이지 번호를 클릭할 때 데이터를 요청하고 화면을 업데이트합니다
  • .
  • 호출기를 Laavel의 API 리소스에 전달할 때 호출 목적지에 필요한 정보를 부여
  • 좋은 웹페이지 즐겨찾기