An awesome setup for your AngularJS project (1/3)

This is post #1 of 3 to explain about fsstatic2. Beware: it may change forever how you approach web development. 🙂

In the past two years I’ve been working a lot with web development and AngularJS.
FreedomSponsors is an open source web application that I made before that, so it doesn’t have in it all those cool new things that I have learned.

I’d like to change that, so I’m rebuilding FreedomSponsors as a AngularJS-based Single Page Application.
The primary reason I’m doing it is because I believe that if FS has a really friendly development environment, more people will be able to collaborate with it and this will help the project move forward.

There’s also a secondary reason: I want this new website to serve as example for people who want to learn more about good practices for web development – this is what this post is about.

The resulting webapp right now is still far from complete (feature-wise), but its architecure already has some good ideas that you may want to apply on your project. Or, if you’re starting a new project, you can just clone the project and go from there.

The main architectural features it has right now:

  • fshelp – A list of commands easy at hand so we don’t need to memorize anything
  • JsHint: Everyone loves their javascript style checked
  • DOCS (docs.html): A “playground” page that can be used both for 1) developing/testing new components, and 2) documenting how to use them
  • The app (index.html): A single page application that you can run locally (ui-router based)
  • Mock API: With this we can mock all of the backend JSON api. Focus on front-end development first; worry about the real backend api later.
  • Fast save/refresh cicle: Using devbuild/runserver, you end up with a development environment where you can save files and hit refresh, with no build steps in between (except for scss files, at least for now)
  • Javascript tests, with coverage report – Bonus: you can reuse the same mock api in the tests too
  • Production build: Build in production mode with all js and html concatenated in a single file. Bonus: it also works with file:// so it should be possible to port it to mobile with no (or maybe very little) modifications using Cordova (yes I intend to do this with FS in the future)
  • Generated CSS using Sass

Exciting, isn’t it? 🙂

So, let’s dive into it with a little more detail…

Get it up and running

It should be really simple and fast, just follow the readme on github, or watch the video below.

Oh, can you please let me know in the comments below how long it took you? 🙂

Now there’s a good practice worth noting here:
Good Practice #1: Have a project help like this.

fshelp

People working in your project should not have to waste their time memorizing commands (if the predefined commands have autocomplete, even better)
When you have someone new in your team, this should save you some time

Folder structure: What is where, and how the build works.

Here’s a quick summary for you

src/                            # All your code is belong to me
src/pages/index.html            # The main application
src/pages/docs.html             # The DOCS application
src/**/*.[js|html|scss]         # The bits and pieces of the app
src/api/api.js                  # The real API that will talk to the backend
src/api/api_mock.js             # The fake API which is what we'll be using for a while
src/**/docs/**/*.[js|html]      # Documentation pages and unit tests (none of this will see production)
src/**/docs/**/test_*.js        # Unit tests
docs_src/                       # The docs framework
lib/**/*.[js|css]               # Third-party stuff
settings/*.js                   # Some settings that differentiate dev vs. prod environments
dist/                           # The result of our build goes here
dist/index.html                 # src/pages/index.html, after some find/replaces
dist/docs.html                  # Guess :)
dist/css/fs.css                 # src/**/*.scss gets compile here
dist/css/lib[.min].css          # Part of lib/**/*.css gets concatenated here
dist/css/lib[.min].js           # Part of lib/**/*.js gets concatenated here
dist/js/fs.js [P]               # src/**/*.[js|html] gets concatenated here (except for **/docs/**)
dist/js/fsdocs.js [P]           # src/**/docs/**/*.js gets concatenated here
dist/js/docs.js [P]             # docs_src/**/*.[js|html] gets concatenated here
dist/js/fs.js                   # Part of lib/**/*.js gets concatenated here
testlib/**/*                    # Libraries used only in unit tests
gulpfile.js                     # THE build
fsdev.sh                        # Handy commands

(Items marked with [P] are only relevant in the production build)

The video below will demonstrate a little more what the build can do.

There’s something I forgot to show in the video, which is, when you build the app using prodmock, it also works with file:// like this:

fsstatic2_file_protocol

 

A good side effect of this is that in the future we are more likely to be able to make an IOS/Android app out of it using Cordova (yes I intend to to that in the future).

OK, this concludes the first post. In the next two posts I’ll talk about:

  • index.html – How the main application works
  • todo.js – A detailed exampled of how to make a component
  • docs.html – A component catalog AND a playground environment.
  • Tests: running and debugging
  • test_todo.js: how to write good tests

Stay tuned.

Anúncios

Faça você mesmo: mini CRUD com AngularJS

Depositphotos_2421161_xs

Vira e mexe me perguntam sobre CRUD com AngularJS.
Aparentemente muita gente precisa disso.

Eu particularmente no meu trabalho no QMágico não mexo muito com CRUD então tenho pouca experiência no assunto: “existe algum componente pronto pra isso?” – aliás, se vc tiver, por favor conte pra gente aí nos comentários.

Maaas, pra demonstrar como é fácil criar componentes genéricos com Angular, eu fiz uma sequência de plunkers que mostram como criar um componente de CRUD genérico, muito rudimentar, mas que cumpre o propósito didático. Se alguém quiser continuar o serviço, criar um projetinho no Github, etc, com certeza a comunidade agradece.

A lista dos plunkers, passo a passo:

Eu tb coloquei esse código no Github pra que vc possa ver o diff de um passo pro outro: https://github.com/tonylampada/angular_mini_crud

E os diffs: https://github.com/tonylampada/angular_mini_crud/commits/gh-pages

E esse projeto no github está na branch gh-pages, então vc consegue ver o resultado final aqui também:

http://tonylampada.github.io/angular_mini_crud/

Então eu vou só dar uma explicadinha superficial em cada passo.

1) LocalStorage

Nesse plunker eu demonstro como usar o LocalStorage – a api do browser que permite vc persistir dados no disco do usuario.
No nosso exemplo, a gente vai usar o LocalStorage pra fazer de conta que eh um banco de dados no backend.

Se vc ainda não conhece o localStorage, dá uma olhada aqui no blog do Zeno: http://zenorocha.com/html5-local-storage/

2) CrudApi

Nesse plunker eu faço um CrudApi – um serviço que faz de conta que fala ajax com um backend, mas que na verdade usa $timeout e o LocalTableStorage por baixo dos panos.

O MyCtrl nem desconfia de nada.

As ações agora tem um “lag” de 500ms.

3) Arquitetura

Nessa terceira parte eu montei só o esqueleto da parada.
Temos uma diretiva <crud>, uma diretiva <crud-grid> e outra diretiva <crud-form>.
Essas diretivas não fazem nada, mas elas já sabem que vão precisar de um CrudModel pra implementar a lógica por trás delas.

Além disso, temos também um objeto models, que conhece os modelos dos objetos que a gente quer persistir.

Pra deixar mais claro, fiz um desenho com um diagrama “CRC” – que mostra responsabilidades e colaborações:

crud-angular (1)

3a) Arquitetura + raio frontentizador do Hugo Almeida

Meu amigo Hugo Almeida deu um trato no css desse cara pra ele ficar um pouquinho mais bonito.
Valeu Hugo!

4) cRuD (READ e DELETE prontos)

Agora o negócio começa a tomar forma…

Nesse 4o plunker eu mexi na diretiva <crud-grid> – ela agora lista as entidades e permite que vc apague.
Pra isso, eu tive que implementar essas funcionalidades no CrudModel – que é o modelo que a diretiva usa (também compartilhado com <crud> e <crud-form>)

Entao, se vc criou uns Tony’s e Maria’s com os exemplos 1 e 2, vc pode usar o exemplo 4 pra apagá-los.

Tah ficando bom hein 🙂

UPDATE: Tem um bug na deleção – vai ser resolvido no passo 7 (sorry :P)

5) crud5 – options

Nessa versão, pela primeira vez estou passando um options (veja no arquivo script.js) com um dicionário pra gerar nomes mais amigáveis pros campos dos objetos. Eu mudei a <crud-grid> pra usar isso aih nos headers. Tá fazendo sentido isso aih?

Agora, tem um problema meio fundamental na nossa implementação:

O CrudModel.entities é um array que tá cheio de objetos não-tipados ({nome, idade}).
O ideal é que ele tivesse objetos Pessoa(nome, idade). Por exemplo, eu não consigo mandar um objeto desses fazer aniversário.

Antes de continuar lendo e ver como resolver isso, dá uma pensada aih: Qual o lugar “correto” pra resolver esse problema?

Dica: Dá uma lida no meu post anterior (aquele da macarronada), e veja o diagrama de responsabilidades acima. Tá faltando alguma responsabilidade nova? Tem alguém que não tá cumprindo sua responsabilidade direito?

Não existe resposta “certa”, mas o processo de pensar a respeito é muito bom.

6) CrudApi, eu quero objetos tipados!

Pensou? Então tá. A minha resposta é a seguinte:

Parece que é do CrudApi a responsabilidade de listar Pessoas() ou Animais(), né?

Não faz nenhum sentido fazer isso no LocalTableStorage – se a gente trocar esse banco fake por uma api com $http, por exemplo, já era.

E a gente até podia fazer essa conversão no CrudModel também, mas aí qualquer outro cliente do CrudApi eventualmente teria que duplicar esse tipo de código. Então é o CrudApi.list() e o CrudApi.get() que são os errados das história. Esses caras deviam retornar Pessoas() e Animais() ao invés de object.

Mas aí tem um probleminha: Pra isso, o CrudApi precisa conhecer o models né? Tava tão bonitinho nosso diagrama, agora vai ter uma seta saindo lá de baixo voltando lá pra cima…

Além do mais, o CrudApi estava totalmente genérico e independente. Agora ele vai ficar acoplado com o meu models. Não parece um negócio muito bom, principalmente se eu quiser liberar esse componente como open source (e portanto podendo ser usado com outros models). E agora?

Bom, não tem jeito. O CrudApi precisa saber criar Pessoas().
Mas nem por isso a gente precisa injetar o models nele durante a inicialização do angular – criando o que eu chamo de dependência forte.

A gente pode fazer que ele seja esperto o suficiente pra: caso tenha um models, ok, trabalha com o models. Senão, beleza também, trabalha com objetos não tipados.

Daí a gente injeta o models nele usando um setter da vida, e programa o if(models){ bem } else { bom tambem }. Assim a dependência pro models fica mais fraca.

Então nosso diagrama agora fica assim.

crud-angular (2)

E pronto.

No script.js a gente faz a configuração do CrudApi com o models,
O crudapi.js foi refatorado pra usar o models de acordo.
E se vc botar um breakpoint no CrudModel.list(), vai ver que agora ele recebeu uma lista de Pessoas() do CrudApi.

Agora sim. Já podemos começar a pensar no form…

7) CCCCreating!

Alterações nesse passo:

  • Resolvi um bug que não deletava entidades do banco. A alteração foi no CrudModel.remove() e na implementação interna do localStorage correspondente.
  • As “classes” Pessoa e Animal no models só conheciam os nomes dos seus atributos. Pra implementar o form, eu tive que mudar isso, pq cada campo do form precisa ser renderizado de acordo com o seu tipo. Então Pessoa.crud_fields não é mais um array de strings e sim um array de object({name, type})
  • O CrudModel sabe qual template usar pra mostrar um campo desde que o tipo seja “id”, “string”, ou “int”. É fácil adicionar novos tipos no framework, basta criar novos templates de acordo.

No próximo passo: EDIT

8) CRUD Completinho

Agora sim, o crud completo.

Repare que pra editar um objeto a gente cria uma copia dele (veja no CrudModel.update()).
Isso eh pra dar pro usuario a chance de cancelar as alterações dele sem refletir as mudanças no grid.

Eu tb tive que mudar o comportamento do CrudModel.save()if(creating){adiciona no array} else { atualiza o objeto }

O próximo e último passo é pra permitir a gente trocar entre pessoa e animal.

9) Trocando de Crud

Nesse último exemplo eu só mexi no index.html / script.js pra permitir que o usuario alterne entre o crud de pessoas e animais.
Veja que bastou trocar o model na diretiva <crud model=”model”>.

Bom, na verdade eu tive que fazer uma mudancinha no controller da diretiva <crud-grid> também, pra atualizar a lista de entidades na tabela.

Mas, é isso aih. Super tranquilo 🙂

10) E agora? Próximos passos?

Então, se vc realmente precisa de um framework como esse, o esqueleto ta aí mas ainda tem muito trabalho pela frente.
Algumas tarefas pra ficar bão:

  • Faça uma vídeo-aula explicando esse código e coloque no Youtube 🙂
  • Se vc imaginar que o <crud> vai ser um componente open source, então o CrudModel deveria ser capaz de receber o CrudApi como “dependência fraca” (via um .configure() da vida), e não como serviço injetado pelo Angular. Pensando bem, devia ter feito assim desde o começo. Sóre.
  • Faz um CrudApi que fala com um backend de verdade (usando $http, ou $resource, ou restangular). Obs: levar em conta as preocupações com segurança nessa hora: aplicações de crud tem uma tendência de expor uma api que é basicamente uma console sql pro seu banco. Seu backend precisa tomar cuidado com isso pra permitir escrita no lugar certo pro usuário certo.
  • Não é uma boa ideia implementar o grid e o form na unha, do zero. O ideal é usar coisas prontas pra isso, como o ng-grid e o formly
  • Paginação: teria que mudar o contrato do CrudApi.list(filters, options). A idéia é fazer o back retornar dados aos poucos, de acordo com parâmetros de paginação no filters.
  • Formulário de busca: o grid deveria ter um formulário de busca, idealmente customizado pelo crud_options passado pra diretiva <crud>.
  • Validação: O formulário precisa de validação de campos. Talvez o formly ajude nisso.
  • Outra opção de customização via crud_options seria escolher quais campos deveriam aparecer no grid e no form. Talvez esconder “id” por default seja uma boa ideia.
  • E outra ainda seria passar um template customizado pro form. Talvez no crud de animal eu queira usar o grid default, mas com um formulário com um comportamento diferente do padrão.
  • E se vc fez tudo isso acima, bota no GitHub!!

Sobre orientação a objeto, responsabilidades e comida italiana

tulum-trattoria-romana

Faz tempo que eu venho pensando sobre um assunto e só há poucos dias conseguir botar no papel.

Tem a ver com orientação a objetos, responsabilidades, colaboração/comunicação, e comida italiana.
Aqui vai…

Quando eu comecei a programar, lá em 2000, eu tive contato com esse conceito de “orientação a objeto”.
Classes, atributos, métodos, herança, polimorfismo, pá.
As pessoas diziam que era melhor programar usando esse paradigma. Mas, olhando pra trás, nunca ninguém me explicou porquê exatamente isso era melhor.

O tempo passou e eu ouvi uma metáfora que ficou na cabeça: usando orientação a objetos, seu código fica menos parecido com um prato de espaghetti e mais parecido com um prato de ravioli. Cada ravioli faz uma coisa, os raviolis falam com outros raviolis quando necessário, e seu sistema fica muito mais fácil de manter e evoluir,

Só que… na prática não era bem assim.

De lá pra cá eu programei muita coisa OO, e tive contato com muito código OO. Herança, Polimorfismo, classes, tá tudo lá, sempre. Mas ainda assim, muitas vezes o código, mesmo OO, ainda tinha aquele cheirinho de spaghetti.

E aí a medida que eu fui ficando mais experiente, eu me pegava cada vez mais pensando em termos de responsabilidades e “conhecimento”:

  • Onde eu devo colocar esse método? De quem deve ser a responsabilidade por essa tarefa?
  • Hum, essa responsabilidade tá no lugar errado, precisa refatorar isso aqui…
  • Xi, esse componente aqui não deveria conhecer aquele componente acolá.

(Veja que eu estou usando a palavra componente de maneira muito genérica aqui. Pode ser um <input>, uma classe java, um módulo python, uma função javascript. Neste contexto, um componente é um pedaço do seu software que tem uma responsabilidade ok?)

E aí tem vezes que um sistema crescendo lembra até uma empresa: vira e mexe vc se pega perguntando “quem é/deveria ser a pessoa/área responsável por essa atividade?” – alguém aí já passou por isso?

Peraí que eu tô viajando muito, deixa eu aterrisar aqui…

Então, refletindo de maneira um pouco mais profunda sobre isso, cheguei à conclusão que:

Pensar meramente em termos de OO não causa código raviólico.

Pra fazer código raviólico vc precisa enxergar seu software como uma rede de componentes que colaboram entre si.

Acho que, idealmente, isso deve se parecer com a estrutura B abaixo.

unnamed

  • Cada componente tem uma responsabilidade bem definida – senão vc vai ficar perdido scrollando no meio de um monte de código.
  • Cada componente deve conhecer apenas alguns poucos vizinhos mais íntimos – senão gera alto acoplamento: os componentes perdem a capacidade de variar independentemente. Aí se vc muda um negócio aqui -> causa vários impactos (ou bugs) acolá.
  • E cada componente pode ser quebrado em componentes menores. Vc pode ter uma rede de componentes dentro de um componente.

No nosso trabalho, além de programar as responsabilidades de cada componente, a gente precisa definir responsabilidades e organizar essa rede de modo a facilitar a comunicação entre os componentes; de modo que comunicação entre eles flua de maneira ótima.

Fazer software bem feito é a arte de ser CEO do sistema! 🙂

Então: no dia que vc for ensinar programação pra alguém que tá começando: antes de falar sobre OO, herança, etc; por favor considere falar primeiro sobre raviolis. O ravioli que é o importante.

Antes de bitolar no OO, o caboco precisa entender porquê dividir um sistema em partes menores, com responsabilidades bem definidas, e tão independentes umas das outras quanto possível.
O importante é focar na rede, e na otimização da interação entre os elementos da rede. OO é só uma ferramenta pra ajudar a construir a rede.

Pq se o caboco não tiver pensando em ravioli desde o começo, vai sair espaghetti.

Ah vai.