Escolher a ferramenta certa para a finalidade certa é um longo caminho, especialmente quando se trata de criar aplicativos da web modernos. Muitos de nós estão familiarizados com o AngularJS e como ele facilita o desenvolvimento de front-ends de aplicativos da web robustos. Embora muitos argumentem contra o uso dessa estrutura da web popular, ela certamente tem muito a oferecer e pode ser uma escolha apropriada para uma ampla gama de necessidades. Por outro lado, os componentes que você usa no back-end ditarão muito sobre o desempenho do aplicativo da web, pois eles têm influência na experiência geral do usuário. Toque é uma estrutura da web de alta velocidade para Java e Scala. Ele é baseado em uma arquitetura leve, sem estado e amigável para a web e segue os padrões e princípios MVC semelhantes a Rails e Django.
Neste artigo, vamos dar uma olhada em como podemos usar AngularJS e Play para construir um aplicativo de blog simples com um mecanismo de autenticação básico e a capacidade de fazer postagens e comentários. Desenvolvimento AngularJS , com algum Twitter Bootstrap brindes, nos permitirá alimentar uma experiência de aplicativo de página única em cima de um back-end de API REST baseado em Play.
Os aplicativos AngularJS e Play residirão nos diretórios do cliente e do servidor de acordo. Por enquanto, devemos criar o diretório “cliente”.
mkdir -p blogapp/client
Para criar um esqueleto de aplicativo AngularJS, usaremos Yeoman - uma ferramenta de scaffolding incrível. Instalar Yeoman é fácil . Usá-lo para criar um esqueleto de aplicativo AngularJS simples é provavelmente ainda mais fácil:
cd blogapp/client yo angular
A execução do segundo comando será seguida por algumas opções que você precisa escolher. Para este projeto, não precisamos de “Sass (com Compass)”. Precisaremos do Boostrap junto com os seguintes plug-ins AngularJS:
Neste ponto, depois de finalizar suas seleções, você começará a ver a saída NPM e Bower em seu terminal. Quando os downloads forem concluídos e os pacotes instalados, você terá um esqueleto do aplicativo AngularJS pronto para ser usado.
A forma oficial de criar um novo aplicativo Play envolve o uso da ferramenta Typesafe Activator. Antes de poder usá-lo, você deve baixá-lo e instalá-lo em seu computador. Se você estiver no Mac OS e usar o Homebrew, poderá instalar essa ferramenta com uma única linha de comando:
brew install typesafe-activator
Criar um aplicativo Play a partir da linha de comando é muito fácil:
cd blogapp/ activator new server play-java cd server/
Para importar o aplicativo em um IDE como Eclipse ou IntelliJ, você precisa “eclipsar” ou “idealizar” seu aplicativo. Para fazer isso, execute o seguinte comando:
activator
Depois de ver um novo prompt, digite “eclipse” ou “ideia” e pressione Enter para preparar o código do aplicativo para Eclipse ou IntelliJ, respectivamente.
Para resumir, vamos cobrir apenas o processo de importação do projeto para o IntelliJ neste artigo. O processo de importá-lo para o Eclipse deve ser igualmente simples. Para importar o projeto para o IntelliJ, comece ativando a opção “Projeto de fontes existentes…” encontrada em “Arquivo -> Novo”. Em seguida, selecione o arquivo build.sbt e clique em “OK”. Ao clicar em “OK” novamente na próxima caixa de diálogo, o IntelliJ deve começar a importar seu aplicativo Play como um projeto SBT.
O Typesafe Activator também vem com uma interface gráfica do usuário, que você pode usar para crie este código de aplicativo esquelético .
Agora que importamos nosso aplicativo Play para o IntelliJ, devemos também importar nosso aplicativo AngularJS para a área de trabalho. Podemos importá-lo como um projeto separado ou como um módulo para o projeto existente onde reside o aplicativo Play.
Aqui, importaremos o aplicativo Angular como um módulo. No menu “File”, selecionaremos a opção “New -> Module From Existing Sources…”. Na caixa de diálogo, escolheremos o diretório “cliente” e clicaremos em “OK”. Nas próximas duas telas, clique em “Avançar” e “Concluir”, respectivamente.
Neste ponto, deve ser possível iniciar o aplicativo AngularJS como uma tarefa Grunt do IDE. Expanda sua pasta de cliente e clique com o botão direito em Gruntfile.js. No menu pop-up, selecione “Show Grunt Tasks”. Um painel denominado “Grunt” aparecerá com uma lista de tarefas:
Para começar a servir o aplicativo, clique duas vezes em “servir”. Isso deve abrir imediatamente seu navegador da web padrão e apontá-lo para um endereço de host local. Você deve ver um esboço da página AngularJS com o logotipo de Yeoman nele.
Em seguida, precisamos iniciar nosso servidor de aplicativos back-end. Antes de prosseguirmos, devemos resolver alguns problemas:
Para contornar esses dois problemas, tudo o que precisamos fazer é usar um proxy Grunt para que todas as solicitações AJAX para o aplicativo Play sejam enviadas por proxy. Com isso, em essência, esses dois servidores de aplicativos estarão disponíveis no mesmo número de porta aparente.
Vamos primeiro alterar o número da porta do servidor de aplicativos Play para 9090. Para fazer isso, abra a janela “Run / Debug Configurations” clicando em “Run -> Edit Configurations”. Em seguida, altere o número da porta no campo “Url para abrir”. Clique em “OK” para aprovar esta alteração e fechar a janela. Clicar no botão “Executar” deve iniciar o processo de resolução de dependências - os logs desse processo começarão a aparecer.
Depois de fazer isso, você pode navegar para http: // localhost: 9090 em seu navegador da web e, em alguns segundos, deverá ser capaz de ver seu aplicativo Play. Para configurar um proxy Grunt, primeiro precisamos instalar um pequeno pacote Node.js usando NPM:
cd blogapp/client npm install grunt-connect-proxy --save-dev
Em seguida, precisamos ajustar nosso Gruntfile.js. Nesse arquivo, localize a tarefa 'conectar' e insira a chave / valor 'proxies' depois dela:
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 } ],
O Grunt agora fará o proxy de todas as solicitações de “/ app / *” para o aplicativo Play de back-end. Isso nos poupará de ter que colocar na lista de permissões todas as chamadas para o back-end. Além disso, também precisamos ajustar nosso comportamento de carga de fígado:
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; } } },
Finalmente, precisamos adicionar uma nova dependência “’ configureProxies: server ”para a tarefa“ servir ”:
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' ]); });
Ao reiniciar o Grunt, você deve observar as seguintes linhas em seus registros, indicando que o proxy está em execução:
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
Começaremos criando um formulário de inscrição para nosso aplicativo de blog. Isso também nos permitirá verificar se tudo está funcionando como deveria. Podemos usar o Yeoman para criar um controlador de inscrição e visualizar no AngularJS:
yo angular:controller signup yo angular:view signup
Em seguida, devemos atualizar o roteamento de nosso aplicativo para fazer referência a essa visão recém-criada e remover o controlador e a visão “sobre” redundantes gerados automaticamente. De dentro do arquivo “app / scripts / app.js”, remova as referências a “app / scripts / controllers / about.js” e “app / views / about.html”, deixando-o com:
.config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl' }) .when('/signup', { templateUrl: 'views/signup.html', controller: 'SignupCtrl' }) .otherwise({ redirectTo: '/' });
Da mesma forma, atualize o arquivo “app / index.html” para remover os links redundantes e adicione um link para a página de inscrição:
Além disso, remova a tag de script para “about.js”:
Email Password Sign up!
Em seguida, adicione um formulário ao nosso arquivo “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); }); }; });
Precisamos fazer com que o formulário seja processado pelo controlador Angular. É importante notar que não precisamos adicionar especificamente o atributo “ng-controller” em nossas visualizações, pois nossa lógica de roteamento em “app.js” aciona um controlador automaticamente antes que nossa visualização seja carregada. Tudo o que precisamos fazer para conectar este formulário é ter uma função de “inscrição” adequada definida em $ scope. Isso deve ser feito no arquivo “signup.js”:
public static Result signup() { return ok('Success!'); }
Agora, vamos abrir o console do desenvolvedor Chrome, mudar para a guia “Rede” e tentar enviar o formulário de inscrição.
Veremos que o back-end do Play responde naturalmente com uma página de erro “Ação não encontrada”. Isso é esperado porque ainda não foi implementado. Mas também significa que nossa configuração de proxy Grunt está funcionando corretamente!
A seguir, vamos adicionar uma “Ação” que é essencialmente um método no controlador de aplicativo Play. Na classe “Application” do pacote “app / controllers”, adicione um novo método “signup”:
POST /app/signup controllers.Application.signup
Agora abra o arquivo “conf / routes” e adicione a seguinte linha:
db.default.driver=org.h2.Driver db.default.url='jdbc:h2:mem:play' db.default.user=sa db.default.password='' ... ebean.default='models.*'
Finalmente, voltamos ao nosso navegador da web, http: // localhost: 9000 / # / signup. Clicar no botão “Enviar” desta vez deve resultar em algo diferente:
Você deve estar vendo o valor codificado retornado, aquele que escrevemos no método de inscrição. Se for esse o caso, estamos prontos para prosseguir, pois nosso ambiente de desenvolvimento está pronto e funcionando para os aplicativos Angular e Play.
Antes de definir modelos, vamos primeiro escolher um armazenamento de dados. Neste artigo, usaremos o banco de dados H2 na memória. Para habilitar isso, encontre e descomente as seguintes linhas no arquivo “application.conf”:
applyEvolutions.default=true
E adicione a seguinte linha:
// 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); } } }
Nosso modelo de domínio de blog é bastante simples. Em primeiro lugar, temos usuários que podem criar postagens e, em seguida, cada postagem pode ser comentada por qualquer usuário conectado. Vamos criar nossos modelos 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; }
Agora vamos criar nossa primeira ação real, permitindo que os usuários se inscrevam:
yo angular:service alerts
Observe que a autenticação usada neste aplicativo é muito básica e não é recomendada para uso em produção.
A parte interessante é que estamos usando os formulários do Play para lidar com os formulários de inscrição. Definimos algumas restrições em nossa classe de formulário SignUp. A validação será feita para nós automaticamente, sem a necessidade de lógica de validação explícita.
Se voltarmos ao nosso aplicativo AngularJS no navegador da web e clicarmos em “Enviar” novamente, veremos que o servidor agora responde com um erro apropriado - que esses campos são obrigatórios.
Portanto, estamos recebendo um erro do servidor, mas o usuário do aplicativo não tem ideia do que está acontecendo. O mínimo que podemos fazer é mostrar o erro ao nosso usuário. Idealmente, precisaríamos entender que tipo de erro estamos obtendo e exibir uma mensagem amigável. Vamos criar um serviço de alerta simples que nos ajudará a exibir o erro.
Primeiro, precisamos gerar um modelo de serviço com 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; } );
Em seguida, adicione este código a “alerts.js”:
yo angular:controller alerts
Agora, vamos criar um controlador separado responsável pelos alertas:
angular.module('clientApp') .controller('AlertsCtrl', function ($scope, alertService) { $scope.alerts = alertService.get(); });
bower install angular-bootstrap --save
Agora precisamos realmente mostrar boas mensagens de erro do Bootstrap. A maneira mais fácil é usar UI angular . Podemos usar o Bower para instalá-lo:
angular .module('clientApp', [ 'ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch', 'ui.bootstrap' ])
Em seu “app.js”, anexe o módulo de IU Angular:
{{ alert.msg }}
Vamos adicionar a diretiva de alerta ao nosso arquivo “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!'); } }) }; });
Finalmente, precisamos atualizar o controlador de inscrição:
yo angular:view dashboard yo angular:controller dashboard
Agora, se enviarmos o formulário vazio novamente, veremos os erros exibidos acima do formulário:
Agora que os erros foram tratados, precisamos fazer algo quando a inscrição do usuário for bem-sucedida. Podemos redirecionar o usuário para uma página do painel onde ele pode adicionar postagens. Mas primeiro, devemos criá-lo:
angular.module('clientApp') .controller('SignupCtrl', function ($scope, $http, $log, alertService, $location) { // .. .success(function(data) { if(data.hasOwnProperty('success')) { $location.path('/dashboard'); } });
Modifique o método de inscrição do controlador “signup.js” para que, em caso de sucesso, redirecione o usuário:
.when('/dashboard', { templateUrl: 'views/dashboard.html', controller: 'DashboardCtrl' })
Adicione uma nova rota em “apps.js”:
yo angular:service user
Também precisamos rastrear se o usuário está conectado. Vamos criar um serviço separado para isso:
// 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');; } });
E também modifique o controlador de inscrição para definir o usuário como aquele que acabou de se registrar:
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; }
Antes de adicionarmos a principal funcionalidade de adição de postagens, vamos cuidar de alguns outros recursos importantes, como a capacidade de fazer login e logout, exibir informações do usuário no painel e também adicionar suporte de autenticação no back-end.
Vamos pular para o nosso aplicativo Play e implementar as ações de login e logout. Adicione essas linhas a “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(); } }
A seguir, vamos adicionar a capacidade de permitir chamadas de back-end específicas apenas para usuários autenticados. Crie “Secured.java” com o seguinte código:
yo angular:controller menu
Usaremos esta classe mais tarde para proteger novas ações. Em seguida, devemos ajustar nosso menu principal do aplicativo AngularJS para que ele exiba os links de nome de usuário e logout. Para isso, precisamos criar o controlador:
// 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
Também precisamos de uma visualização e um controlador para a página de 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 }} Em seguida, ajustamos o menu para que ele possa exibir os dados do usuário:
yo angular:view addpost
Agora, se você fizer login no aplicativo, deverá ser capaz de ver a seguinte tela:
Agora que temos mecanismos básicos de inscrição e autenticação em vigor, podemos começar a implementar a funcionalidade de postagem. Vamos adicionar uma nova visualização e controlador para adicionar postagens.
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' })
Em seguida, atualizamos “app.js” para incluir:
Em seguida, modificamos “index.html” para adicionar um link para nossa visualização “addpost” no menu do painel:
// 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; } }
Agora, no lado do aplicativo Play, vamos criar um novo Post controlador com o método addPost:
POST /app/post controllers.Post.addPost
Adicione uma nova entrada ao arquivo de rotas para poder lidar com os métodos recém-adicionados no roteamento:
// Application.java public static Result getPosts() { return ok(Json.toJson(BlogPost.find.findList())); }
Neste ponto, você deve ser capaz de adicionar novas postagens.
Adicionar postagens tem pouco valor, se não pudermos exibi-las. O que queremos fazer é listar todas as postagens na página principal. Começamos adicionando um novo método em nosso controlador de aplicativo:
GET /app/posts controllers.Application.getPosts
E registrando em nosso arquivo de rotas:
// 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(); });
Em seguida, em nosso aplicativo AngularJS, modificamos nosso controlador principal:
{{ post.subject }}
{{ post.content }}
Post by: {{ post.user.email }} | Comments {{ post.commentCount }}
Por fim, remova tudo de “main.html” e adicione:
yo angular:controller viewpost yo angular:view viewpost
Agora, se você carregar a página inicial do seu aplicativo, deverá ver algo semelhante a isto:
Provavelmente também deveríamos ter uma visão separada para postagens individuais.
// 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' })
E a rota 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)); }
Como antes, adicionamos um novo método ao nosso controlador de aplicativo:
GET /app/post/:id controllers.Application.getPost(id: Long)
… E uma nova rota:
// 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(); });
Agora, se você navegar para http: // localhost: 9000 / # / viewpost / 1, poderá carregar uma visualização para uma postagem específica. A seguir, vamos adicionar a capacidade de ver as postagens do usuário no painel:
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))); }
Adicione também um novo método ao Post controller, seguido por uma rota correspondente a este método:
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; }
Agora, quando você criar postagens, elas serão listadas no painel:
Para implementar a funcionalidade de comentários, começaremos adicionando um novo método no controlador Post:
POST /app/comment controllers.Post.addComment
E como sempre, precisamos registrar uma nova rota para este método:
$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(); }); };
Em nosso aplicativo AngularJS, adicionamos o seguinte ao “viewpost.js”:
By: {{ comment.user.email }}
{{ comment.content }} Login to comment
Add comment
Comment Add comment
E, finalmente, adicione as seguintes linhas a “viewpost.html”:
|_+_|
Agora, se você abrir qualquer postagem, poderá adicionar e visualizar comentários.
Neste tutorial, construímos um blog AngularJS com um aplicativo Play servindo como um back-end da API REST. Embora o aplicativo não tenha validação de dados robusta (especialmente no lado do cliente) e segurança, esses tópicos estavam fora do escopo deste tutorial. O objetivo era demonstrar uma das muitas maneiras possíveis de construir um aplicativo desse tipo. Por conveniência, o código-fonte deste aplicativo foi carregado para um repositório GitHub .
Se você achar esta combinação de AngularJS e Play no desenvolvimento de aplicativos da web interessante, recomendo que você analise mais detalhadamente os seguintes tópicos: