特に最新のWebアプリケーションの構築に関しては、適切な目的に適切なツールを選択することは大いに役立ちます。私たちの多くは、AngularJSと、それが堅牢なWebアプリケーションフロントエンドの開発をいかに簡単にするかをよく知っています。多くの人がこの人気のあるWebフレームワークの使用に反対するでしょうが、それは確かに提供するものがたくさんあり、 適切な選択 幅広いニーズに対応します。一方、バックエンドで使用するコンポーネントは、全体的なユーザーエクスペリエンスに影響を与えるため、Webアプリケーションのパフォーマンスに大きく影響します。 演奏する JavaとScala用の高速Webフレームワークです。これは、軽量でステートレスなWebフレンドリーなアーキテクチャに基づいており、RailsやDjangoと同様のMVCパターンと原則に従います。
この記事では、AngularJSとPlayを使用して、基本認証メカニズムと投稿やコメントを作成する機能を備えたシンプルなブログアプリケーションを構築する方法を見ていきます。 AngularJS開発 、 一部で Twitterブートストラップ これにより、PlayベースのRESTAPIバックエンドに加えて単一ページのアプリケーションエクスペリエンスを強化できます。
AngularJSおよびPlayアプリは、それに応じてクライアントおよびサーバーディレクトリに常駐します。今のところ、「クライアント」ディレクトリを作成します。
mkdir -p blogapp/client
AngularJSアプリケーションスケルトンを作成するために、素晴らしいスキャフォールディングツールであるYeomanを使用します。 ヨーマンのインストールは簡単です 。これを使用して、単純な骨格のAngularJSアプリケーションをスキャフォールディングする方がおそらくさらに簡単です。
cd blogapp/client yo angular
2番目のコマンドを実行すると、選択する必要のあるいくつかのオプションが続きます。このプロジェクトでは、「Sass(コンパス付き)」は必要ありません。次のAngularJSプラグインとともにBoostrapが必要になります。
この時点で、選択を確定すると、端末にNPMとBowerの出力が表示されるようになります。ダウンロードが完了し、パッケージがインストールされると、AngularJSアプリケーションスケルトンを使用できるようになります。
新しいPlayアプリケーションを作成する公式の方法には、TypesafeActivatorツールの使用が含まれます。使用する前に、ダウンロードしてコンピュータにインストールする必要があります。 Mac OSを使用していて、Homebrewを使用している場合は、次の1行のコマンドでこのツールをインストールできます。
brew install typesafe-activator
コマンドラインからPlayアプリケーションを作成するのはとても簡単です。
cd blogapp/ activator new server play-java cd server/
EclipseやIntelliJなどのIDEにアプリケーションをインポートするには、アプリケーションを「Eclipse化」または「理想化」する必要があります。これを行うには、次のコマンドを実行します。
activator
新しいプロンプトが表示されたら、「eclipse」または「idea」と入力し、Enterキーを押して、それぞれEclipseまたはIntelliJのアプリケーションコードを準備します。
PHP7の新機能
簡潔にするために、この記事ではプロジェクトをIntelliJにインポートするプロセスのみを取り上げます。それをEclipseにインポートするプロセスも同様に単純でなければなりません。プロジェクトをIntelliJにインポートするには、「ファイル->新規」の下にある「既存のソースからのプロジェクト…」オプションをアクティブ化することから始めます。次に、build.sbtファイルを選択し、「OK」をクリックします。次のダイアログでもう一度[OK]をクリックすると、IntelliJはPlayアプリケーションをSBTプロジェクトとしてインポートし始めるはずです。
Typesafe Activatorには、グラフィカルユーザーインターフェイスも付属しています。 この骨格アプリケーションコードを作成します 。
PlayアプリケーションをIntelliJにインポートしたので、AngularJSアプリケーションもワークスペースにインポートする必要があります。個別のプロジェクトとして、またはPlayアプリケーションが存在する既存のプロジェクトへのモジュールとしてインポートできます。
ここでは、Angularアプリケーションをモジュールとしてインポートします。 「ファイル」メニューの下で、「新規->既存のソースからのモジュール…」オプションを選択します。ダイアログから「クライアント」ディレクトリを選択し、「OK」をクリックします。次の2つの画面で、それぞれ「次へ」と「完了」をクリックします。
この時点で、IDEからAngularJSアプリケーションをGruntタスクとして開始できるはずです。クライアントフォルダを展開し、Gruntfile.jsを右クリックします。ポップアップメニューで「GruntTasksを表示」を選択します。 「Grunt」というラベルの付いたパネルが表示され、タスクのリストが示されます。
アプリケーションの提供を開始するには、「提供」をダブルクリックします。これにより、すぐにデフォルトのWebブラウザーが開き、ローカルホストアドレスを指すようになります。 Yeomanのロゴが付いたスタブAngularJSページが表示されます。
次に、バックエンドアプリケーションサーバーを起動する必要があります。先に進む前に、いくつかの問題に対処する必要があります。
これらの問題の両方を回避するには、Gruntプロキシを使用して、PlayアプリケーションへのすべてのAJAXリクエストがプロキシされるようにするだけです。これにより、本質的に、これらのアプリケーションサーバーは両方とも同じ見かけのポート番号で使用できるようになります。
まず、Playアプリケーションサーバーのポート番号を9090に変更します。これを行うには、[実行]-> [構成の編集]をクリックして[構成の実行/デバッグ]ウィンドウを開きます。次に、「UrlToOpen」フィールドのポート番号を変更します。 「OK」をクリックしてこの変更を承認し、ウィンドウを閉じます。 「実行」ボタンをクリックすると、依存関係の解決プロセスが開始されます。このプロセスのログが表示され始めます。
完了すると、Webブラウザでhttp:// localhost:9090に移動でき、数秒でPlayアプリケーションが表示されるはずです。 Gruntプロキシを構成するには、最初にNPMを使用して小さなNode.jsパッケージをインストールする必要があります。
cd blogapp/client npm install grunt-connect-proxy --save-dev
次に、Gruntfile.jsを微調整する必要があります。そのファイルで、「接続」タスクを見つけ、その後に「プロキシ」キー/値を挿入します。
proxies: [ { context: '/app', // the context of the data service host: 'localhost', // wherever the data service is running port: 9090, // the port that the data service is running on changeOrigin: true } ],
Gruntは、「/ app / *」へのすべてのリクエストをバックエンドのPlayアプリケーションにプロキシするようになりました。これにより、バックエンドへのすべての呼び出しをホワイトリストに登録する必要がなくなります。さらに、livereloadの動作を微調整する必要もあります。
livereload: { options: { open: true, middleware: function (connect) { var middlewares = []; // Setup the proxy middlewares.push(require('grunt-connect-proxy/lib/utils').proxyRequest); // Serve static files middlewares.push(connect.static('.tmp')); middlewares.push(connect().use( '/bower_components', connect.static('./bower_components') )); middlewares.push(connect().use( '/app/styles', connect.static('./app/styles') )); middlewares.push(connect.static(appConfig.app)); return middlewares; } } },
最後に、新しい依存関係「 ’configureProxies:server」を「serve」タスクに追加する必要があります。
grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { if (target === 'dist') { return grunt.task.run(['build', 'connect:dist:keepalive']); } grunt.task.run([ 'clean:server', 'wiredep', 'concurrent:server', 'autoprefixer:server', 'configureProxies:server', 'connect:livereload', 'watch' ]); });
Gruntを再起動すると、プロキシが実行されていることを示す次の行がログに記録されます。
Running 'autoprefixer:server' (autoprefixer) task File .tmp/styles/main.css created. Running 'configureProxies:server' (configureProxies) task Running 'connect:livereload' (connect) task Started connect web server on http://localhost:9000
まず、ブログアプリケーションのサインアップフォームを作成します。これにより、すべてが正常に機能していることを確認することもできます。 Yeomanを使用して、サインアップコントローラーを作成し、AngularJSで表示できます。
yo angular:controller signup yo angular:view signup
次に、この新しく作成されたビューを参照するようにアプリケーションのルーティングを更新し、自動生成された冗長な「about」コントローラーとビューを削除する必要があります。ファイル「app / scripts / app.js」内から、「app / scripts / controllers /about.js」および「app / views / about.html」への参照を削除し、次のようにします。
.config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl' }) .when('/signup', { templateUrl: 'views/signup.html', controller: 'SignupCtrl' }) .otherwise({ redirectTo: '/' });
同様に、「app / index.html」ファイルを更新して冗長リンクを削除し、サインアップページにリンクを追加します。
また、「about.js」のスクリプトタグを削除します。
Email Password Sign up!
次に、「signup.html」ファイルにフォームを追加します。
angular.module('clientApp') .controller('SignupCtrl', function ($scope, $http, $log) { $scope.signup = function() { var payload = { email : $scope.email, password : $scope.password }; $http.post('app/signup', payload) .success(function(data) { $log.debug(data); }); }; });
フォームをAngularコントローラーで処理する必要があります。 「app.js」のルーティングロジックは、ビューが読み込まれる前にコントローラーを自動的に起動するため、ビューに「ng-controller」属性を具体的に追加する必要がないことに注意してください。このフォームを接続するために必要なのは、$ scopeで定義された適切な「サインアップ」関数を用意することだけです。これは、「signup.js」ファイルで実行する必要があります。
public static Result signup() { return ok('Success!'); }
次に、Chromeデベロッパーコンソールを開き、[ネットワーク]タブに切り替えて、登録フォームを送信してみましょう。
Playバックエンドが「アクションが見つかりません」というエラーページで自然に応答することがわかります。まだ実装されていないため、これは予想されます。しかし、それが意味するのは、Gruntプロキシのセットアップが正しく機能しているということです。
次に、基本的にPlayアプリケーションコントローラーのメソッドである「アクション」を追加します。 「app / controllers」パッケージのクラス「Application」に、新しいメソッド「signup」を追加します。
POST /app/signup controllers.Application.signup
次に、ファイル「conf / routers」を開き、次の行を追加します。
db.default.driver=org.h2.Driver db.default.url='jdbc:h2:mem:play' db.default.user=sa db.default.password='' ... ebean.default='models.*'
最後に、Webブラウザhttp:// localhost:9000 /#/ signupに戻ります。今回は「送信」ボタンをクリックすると、別の結果が得られるはずです。
サインアップメソッドで記述したハードコードされた値が返されるはずです。その場合は、開発環境の準備が整い、AngularアプリケーションとPlayアプリケーションの両方で機能しているので、次に進む準備ができています。
モデルを定義する前に、まずデータストアを選択しましょう。この記事では、H2インメモリデータベースを使用します。これを有効にするには、ファイル「application.conf」で次の行を見つけてコメントを解除します。
applyEvolutions.default=true
そして、次の行を追加します。
// User.java @Entity public class User extends Model { @Id public Long id; @Column(length = 255, unique = true, nullable = false) @Constraints.MaxLength(255) @Constraints.Required @Constraints.Email public String email; @Column(length = 64, nullable = false) private byte[] shaPassword; @OneToMany(cascade = CascadeType.ALL) @JsonIgnore public List posts; public void setPassword(String password) { this.shaPassword = getSha512(password); } public void setEmail(String email) { this.email = email.toLowerCase(); } public static final Finder find = new Finder( Long.class, User.class); public static User findByEmailAndPassword(String email, String password) { return find .where() .eq('email', email.toLowerCase()) .eq('shaPassword', getSha512(password)) .findUnique(); } public static User findByEmail(String email) { return find .where() .eq('email', email.toLowerCase()) .findUnique(); } public static byte[] getSha512(String value) { try { return MessageDigest.getInstance('SHA-512').digest(value.getBytes('UTF-8')); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } }
私たちのブログドメインモデルはかなり単純です。まず、投稿を作成できるユーザーがいます。次に、サインインしたユーザーが各投稿にコメントを付けることができます。 Ebeanモデルを作成しましょう。
// BlogPost.java @Entity public class BlogPost extends Model { @Id public Long id; @Column(length = 255, nullable = false) @Constraints.MaxLength(255) @Constraints.Required public String subject; @Column(columnDefinition = 'TEXT') @Constraints.Required public String content; @ManyToOne public User user; public Long commentCount; @OneToMany(cascade = CascadeType.ALL) public List comments; public static final Finder find = new Finder( Long.class, BlogPost.class); public static List findBlogPostsByUser(final User user) { return find .where() .eq('user', user) .findList(); } public static BlogPost findBlogPostById(final Long id) { return find .where() .eq('id', id) .findUnique(); } }
// PostComment.java @Entity public class PostComment extends Model { @Id public Long id; @ManyToOne @JsonIgnore public BlogPost blogPost; @ManyToOne public User user; @Column(columnDefinition = 'TEXT') public String content; public static final Finder find = new Finder( Long.class, PostComment.class); public static List findAllCommentsByPost(final BlogPost blogPost) { return find .where() .eq('post', blogPost) .findList(); } public static List findAllCommentsByUser(final User user) { return find .where() .eq('user', user) .findList(); } }
// Application.java public static Result signup() { Form signUpForm = Form.form(SignUp.class).bindFromRequest(); if ( signUpForm.hasErrors()) { return badRequest(signUpForm.errorsAsJson()); } SignUp newUser = signUpForm.get(); User existingUser = User.findByEmail(newUser.email); if(existingUser != null) { return badRequest(buildJsonResponse('error', 'User exists')); } else { User user = new User(); user.setEmail(newUser.email); user.setPassword(newUser.password); user.save(); session().clear(); session('username', newUser.email); return ok(buildJsonResponse('success', 'User created successfully')); } } public static class UserForm { @Constraints.Required @Constraints.Email public String email; } public static class SignUp extends UserForm { @Constraints.Required @Constraints.MinLength(6) public String password; } private static ObjectNode buildJsonResponse(String type, String message) { ObjectNode wrapper = Json.newObject(); ObjectNode msg = Json.newObject(); msg.put('message', message); wrapper.put(type, msg); return wrapper; }
それでは、ユーザーがサインアップできるように、最初の実際のアクションを作成しましょう。
yo angular:service alerts
このアプリで使用される認証は非常に基本的なものであり、本番環境での使用はお勧めしません。
興味深いのは、Playフォームを使用してサインアップフォームを処理していることです。サインアップフォームクラスにいくつかの制約を設定します。検証は、明示的な検証ロジックを必要とせずに自動的に行われます。
WebブラウザでAngularJSアプリケーションに戻り、もう一度[送信]をクリックすると、サーバーが適切なエラーで応答することがわかります。これらのフィールドは必須です。
そのため、サーバーからエラーが発生しますが、アプリケーションユーザーは何が起こっているのかわかりません。少なくとも私たちにできることは、ユーザーにエラーを表示することです。理想的には、発生しているエラーの種類を理解し、ユーザーフレンドリーなメッセージを表示する必要があります。エラーの表示に役立つ簡単なアラートサービスを作成しましょう。
まず、Yeomanを使用してサービステンプレートを生成する必要があります。
angular.module('clientApp') .factory('alertService', function($timeout) { var ALERT_TIMEOUT = 5000; function add(type, msg, timeout) { if (timeout) { $timeout(function(){ closeAlert(this); }, timeout); } else { $timeout(function(){ closeAlert(this); }, ALERT_TIMEOUT); } return alerts.push({ type: type, msg: msg, close: function() { return closeAlert(this); } }); } function closeAlert(alert) { return closeAlertIdx(alerts.indexOf(alert)); } function closeAlertIdx(index) { return alerts.splice(index, 1); } function clear(){ alerts = []; } function get() { return alerts; } var service = { add: add, closeAlert: closeAlert, closeAlertIdx: closeAlertIdx, clear: clear, get: get }, alerts = []; return service; } );
次に、このコードを「alerts.js」に追加します。
yo angular:controller alerts
それでは、アラートを担当する別のコントローラーを作成しましょう。
angular.module('clientApp') .controller('AlertsCtrl', function ($scope, alertService) { $scope.alerts = alertService.get(); });
bower install angular-bootstrap --save
次に、実際に素晴らしいBootstrapエラーメッセージを表示する必要があります。最も簡単な方法は Angular UI 。 Bowerを使用してインストールできます。
angular .module('clientApp', [ 'ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch', 'ui.bootstrap' ])
「app.js」にAngularUIモジュールを追加します。
{{ alert.msg }}
「index.html」ファイルにアラートディレクティブを追加しましょう。
angular.module('clientApp') .controller('SignupCtrl', function ($scope, $http, $log, alertService, $location, userService) { $scope.signup = function() { var payload = { email : $scope.email, password : $scope.password }; $http.post('app/signup', payload) .error(function(data, status) { if(status === 400) { angular.forEach(data, function(value, key) { if(key === 'email' || key === 'password') { alertService.add('danger', key + ' : ' + value); } else { alertService.add('danger', value.message); } }); } if(status === 500) { alertService.add('danger', 'Internal server error!'); } }) }; });
最後に、SignUpコントローラーを更新する必要があります。
yo angular:view dashboard yo angular:controller dashboard
ここで、空のフォームを再度送信すると、フォームの上にエラーが表示されます。
エラーが処理されたので、ユーザーのサインアップが成功したときに何かをする必要があります。ユーザーをダッシュボードページにリダイレクトして、投稿を追加することができます。しかし、最初に、それを作成する必要があります。
angular.module('clientApp') .controller('SignupCtrl', function ($scope, $http, $log, alertService, $location) { // .. .success(function(data) { if(data.hasOwnProperty('success')) { $location.path('/dashboard'); } });
「signup.js」コントローラーのサインアップメソッドを変更して、成功するとユーザーをリダイレクトするようにします。
.when('/dashboard', { templateUrl: 'views/dashboard.html', controller: 'DashboardCtrl' })
「apps.js」に新しいルートを追加します。
yo angular:service user
また、ユーザーがログインしているかどうかを追跡する必要があります。そのために別のサービスを作成しましょう。
// user.js angular.module('clientApp') .factory('userService', function() { var username = ''; return { username : username }; });
.success(function(data) { if(data.hasOwnProperty('success')) { userService.username = $scope.email; $location.path('/dashboard');; } });
また、サインアップコントローラーを変更して、ユーザーを登録したばかりのユーザーに設定します。
public static Result login() { Form loginForm = Form.form(Login.class).bindFromRequest(); if (loginForm.hasErrors()) { return badRequest(loginForm.errorsAsJson()); } Login loggingInUser = loginForm.get(); User user = User.findByEmailAndPassword(loggingInUser.email, loggingInUser.password); if(user == null) { return badRequest(buildJsonResponse('error', 'Incorrect email or password')); } else { session().clear(); session('username', loggingInUser.email); ObjectNode wrapper = Json.newObject(); ObjectNode msg = Json.newObject(); msg.put('message', 'Logged in successfully'); msg.put('user', loggingInUser.email); wrapper.put('success', msg); return ok(wrapper); } } public static Result logout() { session().clear(); return ok(buildJsonResponse('success', 'Logged out successfully')); } public static Result isAuthenticated() { if(session().get('username') == null) { return unauthorized(); } else { ObjectNode wrapper = Json.newObject(); ObjectNode msg = Json.newObject(); msg.put('message', 'User is logged in already'); msg.put('user', session().get('username')); wrapper.put('success', msg); return ok(wrapper); } } public static class Login extends UserForm { @Constraints.Required public String password; }
投稿を追加する主な機能を追加する前に、ログインとログアウトの機能、ダッシュボードへのユーザー情報の表示、バックエンドでの認証サポートの追加など、その他の重要な機能について説明します。
Playアプリケーションにジャンプして、ログインアクションとログアウトアクションを実装しましょう。これらの行を「Application.java」に追加します。
public class Secured extends Security.Authenticator { @Override public String getUsername(Context ctx) { return ctx.session().get('username'); } @Override public Result onUnauthorized(Context ctx) { return unauthorized(); } }
次に、認証されたユーザーにのみ特定のバックエンド呼び出しを許可する機能を追加しましょう。次のコードで「Secured.java」を作成します。
yo angular:controller menu
このクラスは、後で新しいアクションを保護するために使用します。次に、AngularJSアプリケーションのメインメニューを微調整して、ユーザー名とログアウトのリンクを表示する必要があります。そのためには、コントローラーを作成する必要があります。
// menu.js angular.module('clientApp') .controller('MenuCtrl', function ($scope, $http, userService, $location) { $scope.user = userService; $scope.logout = function() { $http.get('/app/logout') .success(function(data) { if(data.hasOwnProperty('success')) { userService.username = ''; $location.path('/login'); } }); }; $scope.$watch('user.username', function (newVal) { if(newVal === '') { $scope.isLoggedIn = false; } else { $scope.username = newVal; $scope.isLoggedIn = true; } }); });
yo angular:controller login yo angular:view login
ログインページ用のビューとコントローラーも必要です。
Email Password Log in
// login.js angular.module('clientApp') .controller('LoginCtrl', function ($scope, userService, $location, $log, $http, alertService) { $scope.isAuthenticated = function() { if(userService.username) { $log.debug(userService.username); $location.path('/dashboard'); } else { $http.get('/app/isauthenticated') .error(function() { $location.path('/login'); }) .success(function(data) { if(data.hasOwnProperty('success')) { userService.username = data.success.user; $location.path('/dashboard'); } }); } }; $scope.isAuthenticated(); $scope.login = function() { var payload = { email : this.email, password : this.password }; $http.post('/app/login', payload) .error(function(data, status){ if(status === 400) { angular.forEach(data, function(value, key) { if(key === 'email' || key === 'password') { alertService.add('danger', key + ' : ' + value); } else { alertService.add('danger', value.message); } }); } else if(status === 401) { alertService.add('danger', 'Invalid login or password!'); } else if(status === 500) { alertService.add('danger', 'Internal server error!'); } else { alertService.add('danger', data); } }) .success(function(data){ $log.debug(data); if(data.hasOwnProperty('success')) { userService.username = data.success.user; $location.path('/dashboard'); } }); }; });
Toggle Dropdown
{{ username }} 次に、メニューを微調整して、ユーザーデータを表示できるようにします。
yo angular:view addpost
これで、アプリケーションにログインすると、次の画面が表示されるはずです。
基本的なサインアップと認証のメカニズムが整ったので、投稿機能の実装に取り掛かることができます。投稿を追加するための新しいビューとコントローラーを追加しましょう。
Subject Post Submit post
yo angular:controller addpost
// addpost.js angular.module('clientApp') .controller('AddpostCtrl', function ($scope, $http, alertService, $location) { $scope.post = function() { var payload = { subject : $scope.subject, content: $scope.content }; $http.post('/app/post', payload) .error(function(data, status) { if(status === 400) { angular.forEach(data, function(value, key) { if(key === 'subject' || key === 'content') { alertService.add('danger', key + ' : ' + value); } else { alertService.add('danger', value.message); } }); } else if(status === 401) { $location.path('/login'); } else if(status === 500) { alertService.add('danger', 'Internal server error!'); } else { alertService.add('danger', data); } }) .success(function(data) { $scope.subject = ''; $scope.content = ''; alertService.add('success', data.success.message); }); }; });
.when('/addpost', { templateUrl: 'views/addpost.html', controller: 'AddpostCtrl' })
次に、「app.js」を更新して次のものを含めます。
次に、「index.html」を変更して、ダッシュボードメニューの「addpost」ビューへのリンクを追加します。
// Post.java public class Post extends Controller { public static Result addPost() { Form postForm = Form.form(PostForm.class).bindFromRequest(); if (postForm.hasErrors()) { return badRequest(postForm.errorsAsJson()); } else { BlogPost newBlogPost = new BlogPost(); newBlogPost.commentCount = 0L; newBlogPost.subject = postForm.get().subject; newBlogPost.content = postForm.get().content; newBlogPost.user = getUser(); newBlogPost.save(); } return ok(Application.buildJsonResponse('success', 'Post added successfully')); } private static User getUser() { return User.findByEmail(session().get('username')); } public static class PostForm { @Constraints.Required @Constraints.MaxLength(255) public String subject; @Constraints.Required public String content; } }
Playアプリケーション側で、addPostメソッドを使用して新しいコントローラーPostを作成しましょう。
POST /app/post controllers.Post.addPost
ルートファイルに新しいエントリを追加して、ルーティングで新しく追加されたメソッドを処理できるようにします。
// Application.java public static Result getPosts() { return ok(Json.toJson(BlogPost.find.findList())); }
この時点で、新しい投稿を追加できるはずです。
投稿を表示できない場合、投稿を追加してもほとんど価値がありません。私たちがやりたいのは、メインページにすべての投稿をリストすることです。まず、アプリケーションコントローラーに新しいメソッドを追加します。
GET /app/posts controllers.Application.getPosts
そしてそれをルートファイルに登録します:
// main.js angular.module('clientApp') .controller('MainCtrl', function ($scope, $http) { $scope.getPosts = function() { $http.get('app/posts') .success(function(data) { $scope.posts = data; }); }; $scope.getPosts(); });
次に、AngularJSアプリケーションで、メインコントローラーを変更します。
{{ post.subject }}
{{ post.content }}
Post by: {{ post.user.email }} | Comments {{ post.commentCount }}
最後に、「main.html」からすべてを削除し、これを追加します。
yo angular:controller viewpost yo angular:view viewpost
これで、アプリケーションのホームページをロードすると、次のようなものが表示されるはずです。
また、おそらく個々の投稿に対して個別のビューを用意する必要があります。
// viewpost.js angular.module('clientApp') .controller('ViewpostCtrl', function ($scope, $http, alertService, userService, $location) { $scope.user = userService; $scope.params = $routeParams; $scope.postId = $scope.params.postId; $scope.viewPost = function() { $http.get('/app/post/' + $scope.postId) .error(function(data) { alertService.add('danger', data.error.message); }) .success(function(data) { $scope.post = data; }); }; $scope.viewPost(); });
{{ post.subject }}
{{ post.content }}
Post by: {{ post.user.email }} | Comments {{ post.commentCount }}
app.js: .when('/viewpost/:postId', { templateUrl: 'views/viewpost.html', controller: 'ViewpostCtrl' })
そしてAngularJSルート:
// Application.java public static Result getPost(Long id) { BlogPost blogPost = BlogPost.findBlogPostById(id); if(blogPost == null) { return notFound(buildJsonResponse('error', 'Post not found')); } return ok(Json.toJson(blogPost)); }
以前と同様に、アプリケーションコントローラーに新しいメソッドを追加します。
GET /app/post/:id controllers.Application.getPost(id: Long)
…そして新しいルート:
// dashboard.js angular.module('clientApp') .controller('DashboardCtrl', function ($scope, $log, $http, alertService, $location) { $scope.loadPosts = function() { $http.get('/app/userposts') .error(function(data, status) { if(status === 401) { $location.path('/login'); } else { alertService.add('danger', data.error.message); } }) .success(function(data) { $scope.posts = data; }); }; $scope.loadPosts(); });
これで、http:// localhost:9000 /#/ viewpost / 1に移動すると、特定の投稿のビューをロードできるようになります。次に、ダッシュボードにユーザーの投稿を表示する機能を追加しましょう。
My Posts
No posts yet. Add a post {{ post.subject }} | Comments {{ post.commentCount }}
// Post.java public static Result getUserPosts() { User user = getUser(); if(user == null) { return badRequest(Application.buildJsonResponse('error', 'No such user')); } return ok(Json.toJson(BlogPost.findBlogPostsByUser(user))); }
また、Postコントローラーに新しいメソッドを追加し、その後にこのメソッドに対応するルートを追加します。
GET /app/userposts controllers.Post.getUserPosts
// Post.java public static Result addComment() { Form commentForm = Form.form(CommentForm.class).bindFromRequest(); if (commentForm.hasErrors()) { return badRequest(commentForm.errorsAsJson()); } else { PostComment newComment = new PostComment(); BlogPost blogPost = BlogPost.findBlogPostById(commentForm.get().postId); blogPost.commentCount++; blogPost.save(); newComment.blogPost = blogPost; newComment.user = getUser(); newComment.content = commentForm.get().comment; newComment.save(); return ok(Application.buildJsonResponse('success', 'Comment added successfully')); } } public static class CommentForm { @Constraints.Required public Long postId; @Constraints.Required public String comment; }
これで、投稿を作成すると、ダッシュボードに一覧表示されます。
コメント機能を実装するために、Postコントローラーに新しいメソッドを追加することから始めます。
POST /app/comment controllers.Post.addComment
そしていつものように、このメソッドの新しいルートを登録する必要があります。
$scope.addComment = function() { var payload = { postId: $scope.postId, comment: $scope.comment }; $http.post('/app/comment', payload) .error(function(data, status) { if(status === 400) { angular.forEach(data, function(value, key) { if(key === 'comment') { alertService.add('danger', key + ' : ' + value); } else { alertService.add('danger', value.message); } }); } else if(status === 401) { $location.path('/login'); } else if(status === 500) { alertService.add('danger', 'Internal server error!'); } else { alertService.add('danger', data); } }) .success(function(data) { alertService.add('success', data.success.message); $scope.comment = ''; $scope.viewPost(); }); };
AngularJSアプリケーションでは、「viewpost.js」に以下を追加します。
By: {{ comment.user.email }}
{{ comment.content }} Login to comment
Add comment
Comment Add comment
そして最後に、「viewpost.html」に次の行を追加します。
|_+_|
これで、投稿を開くと、コメントを追加して表示できるようになります。
このチュートリアルでは、RESTAPIバックエンドとして機能するPlayアプリケーションを使用してAngularJSブログを作成しました。アプリケーションには堅牢なデータ検証(特にクライアント側)とセキュリティが欠けていますが、これらのトピックはこのチュートリアルの範囲外でした。この種のアプリケーションを構築するための多くの可能な方法の1つを示すことを目的としていました。便宜上、このアプリケーションのソースコードはにアップロードされています GitHubリポジトリ 。
Webアプリケーション開発におけるAngularJSとPlayのこの組み合わせが興味深いと思われる場合は、次のトピックをさらに確認することを強くお勧めします。