Spring RESTとAngularJSテーブルによるページ付け

1概要

この記事では、主に Spring REST API と単純なAngularJSフロントエンドでのサーバー側ページ付けの実装に焦点を当てます。

また、Angularで一般的に使用されているhttp://ui-grid.info/[UI Grid]というテーブルグリッドについても説明します。

2依存関係

ここでは、この記事に必要なさまざまな依存関係について詳しく説明します。

2.1. JavaScript

Angular UI Gridが機能するためには、HTMLにインポートされた以下のスクリプトが必要になります。

  • __https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js[Angular]

JS(1.5.8)]__

  • __https://cdn.rawgit.com/angular-ui/bower-ui-grid/master/ui-grid.min.js[Angular

UIグリッド]__

2.2. Maven

バックエンドには Spring Boot を使うので、以下の依存関係が必要になります。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>
  • 注:** ここで他の依存関係は指定されていません。完全なリストについては、https://github.com/eugenp/tutorials/tree/master/spring-rest-angular/[GitHub]プロジェクトの完全な pom.xml を確認してください。

3アプリケーションについて

このアプリケーションは、ページ分割されたテーブルグリッドでユーザーが生徒の詳細を確認できるようにする単純な生徒のディレクトリアプリケーションです。

アプリケーションは Spring Boot を使用し、埋め込みデータベースを持つ埋め込みTomcatサーバーで動作します。

最後に、API側では、ページネーションを行う方法がいくつかあります。リンクに記載されています。この記事。

ここでの私たちの解決策は簡単です - URIクエリのページング情報を次のように持つこと:__/student/get?page = 1&size = 2

4クライアントサイド

まず、クライアントサイドロジックを作成する必要があります。

4.1. UIグリッド

index.html には、必要なインポートとテーブルグリッドの単純な実装があります。

<!DOCTYPE html>
<html lang="en" ng-app="app">
    <head>
        <link rel="stylesheet" href="https://cdn.rawgit.com/angular-ui/          bower-ui-grid/master/ui-grid.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/          1.5.6/angular.min.js"></script>
        <script src="https://cdn.rawgit.com/angular-ui/bower-ui-grid/          master/ui-grid.min.js"></script>
        <script src="view/app.js"></script>
    </head>
    <body>
        <div ng-controller="StudentCtrl as vm">
            <div ui-grid="gridOptions" class="grid" ui-grid-pagination>
            </div>
        </div>
    </body>
</html>

コードを詳しく見てみましょう。

  • ng-app - モジュール app をロードするAngularディレクティブです。すべて

これらの下にある要素は app モジュールの一部になります ** ng-controller - コントローラをロードするAngularディレクティブです

StudentCtrl vmのエイリアス。これらの下にあるすべての要素は StudentCtrl コントローラの一部 ** ui-grid - Angular ui-grid__に属するAngularディレクティブです。

gridOptions をデフォルト設定として使用し、 gridOptions app.js $ scope で宣言されています。

4.2. AngularJSモジュール

最初に app.js でモジュールを定義しましょう。

var app = angular.module('app',['ui.grid','ui.grid.pagination']);

app モジュールを宣言し、UIグリッド機能を有効にするために ui.grid を注入しました。また、ページ付けサポートを有効にするために ui.grid.pagination を注入しました。

次に、コントローラを定義します。

app.controller('StudentCtrl',['$scope','StudentService',
    function ($scope, StudentService) {
        var paginationOptions = {
            pageNumber: 1,
            pageSize: 5,
        sort: null
        };

    StudentService.getStudents(
      paginationOptions.pageNumber,
      paginationOptions.pageSize).success(function(data){
        $scope.gridOptions.data = data.content;
        $scope.gridOptions.totalItems = data.totalElements;
      });

    $scope.gridOptions = {
        paginationPageSizes:[5, 10, 20],
        paginationPageSize: paginationOptions.pageSize,
        enableColumnMenus:false,
    useExternalPagination: true,
        columnDefs:[           { name: 'id' },
           { name: 'name' },
           { name: 'gender' },
           { name: 'age' }
       ],
        onRegisterApi: function(gridApi) {
           $scope.gridApi = gridApi;
           gridApi.pagination.on.paginationChanged(
             $scope,
             function (newPage, pageSize) {
               paginationOptions.pageNumber = newPage;
               paginationOptions.pageSize = pageSize;
               StudentService.getStudents(newPage,pageSize)
                 .success(function(data){
                   $scope.gridOptions.data = data.content;
                   $scope.gridOptions.totalItems = data.totalElements;
                 });
            });
        }
    };
}]);

$ scope.gridOptions のカスタムページ区切り設定を見てみましょう。

  • paginationPageSizes - 利用可能なページサイズオプションを定義します

  • paginationPageSize - デフォルトのページサイズを定義します

  • enableColumnMenus - 列のメニューを有効/無効にするために使用されます

  • useExternalPagination - あなたがページをめくっている場合は必須です

サーバ側 ** columnDefs - 自動的にマップされるカラム名

サーバーから返されたJSONオブジェクトサーバーから返されたJSONオブジェクト内のフィールド名と定義された列名は一致する必要があります。

  • onRegisterApi - 内部にパブリックメソッドイベントを登録する機能

グリッド。ここでは gridApi.pagination.on.paginationChanged を登録して、ページが変更されるたびにこの関数をトリガーするようにUI-Gridに指示します。

そしてリクエストをAPIに送信します。

app.service('StudentService',['$http', function ($http) {

    function getStudents(pageNumber,size) {
        pageNumber = pageNumber > 0?pageNumber - 1:0;
        return $http({
          method: 'GET',
            url: 'student/get?page='+pageNumber+'&size='+size
        });
    }
    return {
        getStudents: getStudents
    };
}]);

5バックエンドとAPI

5.1. RESTfulサービス

これは、ページ付けをサポートする単純なRESTful API実装です。

@RestController
public class StudentDirectoryRestController {

    @Autowired
    private StudentService service;

    @RequestMapping(
      value = "/student/get",
      params = { "page", "size" },
      method = RequestMethod.GET
    )
    public Page<Student> findPaginated(
      @RequestParam("page") int page, @RequestParam("size") int size) {

        Page<Student> resultPage = service.findPaginated(page, size);
        if (page > resultPage.getTotalPages()) {
            throw new MyResourceNotFoundException();
        }

        return resultPage;
    }
}

@ RestController は、Spring 4.0で @ Controller [email protected]を暗黙的に宣言する便利なアノテーションとして導入されました。

私たちのAPIでは、 page とsizeという2つのパラメータを受け取るように宣言しました。これらは、クライアントに返すレコード数も決定します。

ページ数が総ページ数よりも大きい場合に MyResourceNotFoundException をスローする単純な検証も追加しました。

最後に、応答として Page を返します。これは、ページ区切りデータを保持しているS __pring Data __の非常に役に立つコンポーネントです。

5.2. サービス実装

私達のサービスは単にコントローラーによって提供されたページとサイズに基づいてレコードを返します。

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentRepository dao;

    @Override
    public Page<Student> findPaginated(int page, int size) {
        return dao.findAll(new PageRequest(page, size));
    }
}

5.3. リポジトリの実装

私たちの永続層には、埋め込みデータベースとSpring Data JPAを使用しています。

まず、永続化設定をセットアップする必要があります。

@EnableJpaRepositories("org.baeldung.web.dao")
@ComponentScan(basePackages = { "org.baeldung.web" })
@EntityScan("org.baeldung.web.entity")
@Configuration
public class PersistenceConfig {

    @Bean
    public JdbcTemplate getJdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        EmbeddedDatabase db = builder
          .setType(EmbeddedDatabaseType.HSQL)
          .addScript("db/sql/data.sql")
          .build();
        return db;
    }
}

永続化設定は簡単です - 指定されたパッケージをスキャンしてSpring Data JPAリポジトリインターフェースを見つけるための @ EnableJpaRepositories があります。

すべてのBeanを自動的にスキャンするための @ ComponentScan があり、エンティティクラスをスキャンするために(Spring Bootから) EntityScan があります。

また、起動時に提供されるSQLスクリプトを実行する埋め込みデータベースを使用して、単純なデータソースも宣言しました。

今度はデータリポジトリを作成します。

public interface StudentRepository extends JpaRepository<Student, Long> {}

これが基本的に私達がここでする必要があるすべてです。非常に強力なSpring Data JPAを設定して使用する方法についてもっと深く知りたい場合は、/link-persistence-layer-with-spring-data-jpaを確実にリンクしてください[ここへのガイドを読んでください]。

6. ページ付け要求と応答

APIを呼び出す場合__– http://localhost:8080/student/get?page = 1

{
    "content":[        {"studentId":"1","name":"Bryan","gender":"Male","age":20},
        {"studentId":"2","name":"Ben","gender":"Male","age":22},
        {"studentId":"3","name":"Lisa","gender":"Female","age":24},
        {"studentId":"4","name":"Sarah","gender":"Female","age":26},
        {"studentId":"5","name":"Jay","gender":"Male","age":20}
   ],
    "last":false,
    "totalElements":20,
    "totalPages":4,
    "size":5,
    "number":0,
    "sort":null,
    "first":true,
    "numberOfElements":5
}

ここで気をつけなければならないのは、サーバーが org.springframework.data.domain.Page DTOを返して、 Student リソースをラップするということです。

Page オブジェクトには以下のフィールドがあります。

  • last - 最後のページであれば true に設定し、そうでなければfalse

  • first - 最初のページの場合は true に設定し、それ以外の場合はfalse

  • totalElements - 行/レコードの総数。この例では、

これを ui-grid オプション $ scope.gridOptions.totalItems に渡します。 利用可能なページ数を決定する ** totalPages - 派生したページの総数

totalElements/size ) ** size - 1ページあたりのレコード数。これは

パラメータ size を介したクライアント ** number - クライアントから送信されたページ番号。

バックエンドでは _Student sの配列を使用しているため、numberは0です。 これは0から始まるインデックスなので、バックエンドではページをデクリメントします。 1による番号 ** sort_ - ページのソートパラメータ

  • numberOfElements - ページに対して返される行/レコードの数

7. ページ付けをテストする

RestAssured を使用して、ページネーションロジックのテストを設定しましょう。 RestAssured についてさらに学ぶために、あなたはこのリンクを見ることができる:/rest-assured-tutorial[チュートリアル]。

7.1. テストの準備

テストクラスの開発を容易にするために、静的インポートを追加します。

io.restassured.RestAssured.**
io.restassured.matcher.RestAssuredMatchers.**
org.hamcrest.Matchers.**

次に、Spring対応のテストを設定します。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest("server.port:8888")

@ SpringApplicationConfiguration は、Springが ApplicationContextのロード方法を知るのに役立ちます。この場合は、 Application.jtextを使用して__ApplicationContextを構成しました。

@ WebAppConfiguration は、ロードする ApplicationContext WebApplicationContext. にする必要があることをSpringに通知するために定義されました

テストを実行するときに @ IntegrationTest がアプリケーションの起動をトリガーするように定義されているため、RESTサービスをテストに使用できます。

7.2. テスト

これが最初のテストケースです。

@Test
public void givenRequestForStudents__whenPageIsOne__expectContainsNames() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .assertThat().body("content.name", hasItems("Bryan", "Ben"));
}

上記のこのテストケースは、ページ1とサイズ2がRESTサービスに渡されるときにサーバーから返されるJSONコンテンツの名前が Bryan Ben. であることをテストすることです

テストケースを詳しく見てみましょう。

  • given - RestAssured の一部であり、

リクエストした場合は、 with() を使用することもできます。 ** get - RestAssured の一部であり、使用された場合はgetリクエストをトリガーします。

投稿要求にpost()を使用する ** hasItems - 値に値があるかどうかをチェックするハムクレストの一部

一致

さらにいくつかテストケースを追加します。

@Test
public void givenRequestForStudents__whenResourcesAreRetrievedPaged__thenExpect200() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .statusCode(200);
}

このテストは、ポイントが実際に呼び出されたときにOK応答が受信されることを表明します。

@Test
public void givenRequestForStudents__whenSizeIsTwo__expectNumberOfElementsTwo() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .assertThat().body("numberOfElements", equalTo(2));
}

このテストは、2のページサイズが要求されたときに返されるページサイズが実際には2であることを主張します。

@Test
public void givenResourcesExist__whenFirstPageIsRetrieved__thenPageContainsResources() {
    given().params("page", "0", "size", "2").get(ENDPOINT)
      .then()
      .assertThat().body("first", equalTo(true));
}

このテストは、リソースが最初に呼び出されたときに最初のページ名の値がtrueであることを表明します。

リポジトリにはもっとたくさんのテストがあるので、https://github.com/eugenp/tutorials/tree/master/spring-rest-angular/[GitHub]プロジェクトを見てください。

8結論

この記事では、 AngularJS UI-Grid を使用してデータテーブルグリッドを実装する方法と、必要なサーバーサイドのページ区切りを実装する方法について説明しました。

これらの例とテストの実装はhttps://github.com/eugenp/tutorials/tree/master/spring-rest-angular/[GitHubプロジェクト]にあります。これはMavenプロジェクトなので、そのままインポートして実行するのは簡単なはずです。

Springブートプロジェクトを実行するには、単に mvn spring-boot:run を実行します。 そして http://localhost:8080/ . でローカルにアクセスします。