Quadro de colaboração AngularJS com Socket.io

Autor: Peter Berry
Data De Criação: 14 Julho 2021
Data De Atualização: 13 Poderia 2024
Anonim
Quadro de colaboração AngularJS com Socket.io - Criativo
Quadro de colaboração AngularJS com Socket.io - Criativo

Contente

  • Conhecimento necessário: JavaScript intermediário
  • Requer: Node.js, NPM
  • Tempo do projeto: 2 horas

O AngularJS é particularmente adequado para criar aplicativos ricos do lado do cliente no navegador e, quando você adiciona um pouco de Socket.io à mistura, as coisas ficam realmente interessantes. Neste artigo, vamos construir uma placa de colaboração em tempo real que usa AngularJS para o aplicativo do lado do cliente e Socket.io para compartilhar o estado entre todos os clientes conectados.

Vamos abordar um pouco as tarefas domésticas antes de começar. Vou presumir que você tenha uma compreensão fundamental de HTML e JavaScript, já que não vou cobrir cada cantinho do código. Por exemplo, não vou citar os arquivos CSS e JavaScript que incluí no cabeçalho do arquivo HTML, pois não há novas informações lá.

Além disso, encorajo você a pegar o código da minha conta do GitHub para acompanhar. Meu bom amigo Brian Ford também tem uma excelente semente Socket.io, na qual baseei algumas de minhas idéias originais.

Os quatro recursos principais que queremos no quadro de colaboração são a capacidade de criar uma nota, ler as notas, atualizar uma nota, excluir uma nota e, por diversão, mover uma nota no quadro. Sim, isso é correto, estamos nos concentrando nos recursos CRUD padrão. Acredito que, ao nos concentrarmos nesses recursos fundamentais, teremos abordado código suficiente para o surgimento de padrões para que você possa tomá-los e aplicá-los em outro lugar.


01. O servidor

Vamos começar com o servidor Node.js primeiro, pois ele servirá como a base sobre a qual construiremos todo o resto.

Vamos construir um servidor Node.js com Express e Socket.io. O motivo pelo qual estamos usando o Express é que ele fornece um bom mecanismo para configurar um servidor de ativos estáticos no Node.js. O Express vem com um monte de recursos realmente incríveis, mas, neste caso, vamos usá-lo para dividir o aplicativo entre o servidor e o cliente.

(Presumo que você tenha Node.js e NPM instalados. Uma rápida pesquisa no Google mostrará como instalá-los, caso não os tenha.)

02. O esqueleto

Portanto, para construir o esqueleto do servidor, precisamos fazer algumas coisas para colocá-lo em funcionamento.

// app.js

// A.1
var express = require ('express'),
app = expresso ();
server = require (’http’). createServer (app),
io = requer ('socket.io'). listen (servidor);

// A.2
app.configure (function () {
app.use (express.static (__ dirname + ’/ public’));
});

// A.3
server.listen (1337);


A.1 Estamos declarando e instanciando nossos módulos Node.js para que possamos usá-los em nosso aplicativo. Estamos declarando o Express, instanciando o Express e, em seguida, criando um servidor HTTP e enviando a instância do Express para ele. E a partir daí estamos instanciando Socket.io e dizendo para ficar de olho em nossa instância de servidor.

A.2 Estamos, então, informando nosso aplicativo Express para usar nosso diretório público para servir arquivos.

A.3 Iniciamos o servidor e dizemos a ele para escutar na porta 1337.

Até agora isso tem sido rápido e indolor. Acredito que temos menos de 10 linhas no código e já temos um servidor Node.js funcional. Avante!

03. Declare suas dependências

// packages.json
{
"nome": "angular-collab-board",
"descrição": "AngularJS Collaboration Board",
"versão": "0.0.1-1",
"privado": verdadeiro,
"dependências": {
"expresso": "3.x",
"socket.io": "0.9.x"
}
}

Um dos melhores recursos do NPM é a capacidade de declarar suas dependências em um packages.json arquivo e, em seguida, instalá-los automaticamente via npm install na linha de comando.


04. Conecte o Socket.io

Já definimos os principais recursos que queremos no aplicativo e, portanto, precisamos configurar ouvintes de eventos Socket.io e um encerramento apropriado para tratar o evento para cada operação.

No código a seguir, você notará que é essencialmente uma configuração de ouvintes de eventos e retornos de chamada. O primeiro evento é o conexão evento, que usamos para conectar nossos outros eventos no encerramento.

io.sockets.on ('conexão', função (soquete) {
socket.on ('createNote', function (data) {
socket.broadcast.emit (’onNoteCreated’, data);
});

socket.on ('updateNote', função (dados) {
socket.broadcast.emit (’onNoteUpdated’, data);
});

socket.on ('deleteNote', function (data) {
socket.broadcast.emit ('onNoteDeleted', dados);
});

socket.on ('moveNote', function (data) {
socket.broadcast.emit ('onNoteMoved', dados);
});
});

A partir daqui, adicionamos ouvintes para createNote, updateNote, deleteNote e moveNote. E na função de retorno de chamada, estamos simplesmente transmitindo o evento que aconteceu para que qualquer cliente que estiver ouvindo possa ser notificado de que o evento aconteceu.

Existem algumas coisas que vale a pena apontar sobre as funções de retorno de chamada nos manipuladores de eventos individuais. Um, se você deseja enviar um evento para todos, exceto para o cliente que emitiu o evento que você inseriu transmissão antes de o emitir chamada de função. Em segundo lugar, estamos simplesmente repassando a carga útil do evento às partes interessadas para que possam processá-lo da maneira que acharem adequado.

05. Liguem seus motores!

Agora que definimos nossas dependências e configuramos nosso aplicativo Node.js com os poderes Express e Socket.io, é bastante simples inicializar o servidor Node.js.

Primeiro você instala suas dependências Node.js assim:

npm install

E então você inicia o servidor assim:

node app.js

E depois! Você acessa este endereço em seu navegador. Bam!

06. Alguns pensamentos sinceros antes de continuar

Eu sou principalmente um desenvolvedor front-end e inicialmente fiquei um pouco intimidado em conectar um servidor Node.js ao meu aplicativo. A parte do AngularJS foi fácil, mas JavaScript do lado do servidor? Coloque na fila a música assustadora de um filme de terror.

Mas, fiquei absolutamente chocado ao descobrir que poderia configurar um servidor da web estático em apenas algumas linhas de código e em mais algumas linhas usar Socket.io para lidar com todos os eventos entre os navegadores. E ainda era apenas JavaScript! Por uma questão de oportunidade, estamos cobrindo apenas alguns recursos, mas espero que até o final do artigo você verá que é fácil nadar - e o fundo da piscina não é tão assustador.

07. O cliente

Agora que temos nossa base sólida com nosso servidor, vamos passar para minha parte favorita - o cliente! Estaremos usando AngularJS, jQueryUI para a parte arrastável e Twitter Bootstrap para uma base de estilo.

08. O esqueleto

Por uma questão de preferência pessoal, quando eu inicio um novo aplicativo AngularJS, gosto de definir rapidamente o mínimo que sei que vou precisar para começar e, em seguida, começar a iterar isso o mais rápido possível.

Todo aplicativo AngularJS precisa ser inicializado com pelo menos um controlador presente e, geralmente, é aqui que eu sempre começo.

Para inicializar automaticamente o aplicativo, você precisa simplesmente adicionar ng-app para o nó HTML no qual você deseja que o aplicativo resida. Na maioria das vezes, adicioná-lo à tag HTML será perfeitamente aceitável. Eu também adicionei um atributo para ng-app para dizer que eu quero usar o aplicativo módulo, que definirei em alguns instantes.

// public / index.html
html ng-app = "app">

Eu sei que vou precisar de pelo menos um controlador e então vou chamar isso usando ng-controlador e atribuindo a ele uma propriedade de MainCtrl.

body ng-controller = "MainCtrl"> / body>

Então, agora estamos no gancho para um módulo chamado aplicativo e um controlador chamado MainCtrl. Vamos em frente e criá-los agora.

Criar um módulo é bastante simples. Você o define chamando angular.module e dando-lhe um nome. Para referência futura, o segundo parâmetro de uma matriz vazia é onde você pode injetar submódulos para uso no aplicativo. Está fora do escopo deste tutorial, mas é útil quando seu aplicativo começa a crescer em complexidade e necessidades.

// public / js / collab.js
var app = angular.module (’app’, []);

Vamos declarar alguns marcadores de posição vazios no aplicativo módulo começando com o MainCtrl abaixo de.Vamos preencher todos esses itens mais tarde, mas eu queria ilustrar a estrutura básica desde o início.

app.controller (’MainCtrl’, function ($ scope) {});

Também vamos envolver a funcionalidade Socket.io em um tomada serviço para que possamos encapsular esse objeto e não deixá-lo flutuando no namespace global.

app.factory (’socket’, function ($ rootScope) {});

E já que estamos nisso, vamos declarar uma diretiva chamada notas adesivas que vamos usar para encapsular a funcionalidade de nota adesiva em.

app.directive (’stickyNote’, function (socket) {});

Portanto, vamos revisar o que fizemos até agora. Inicializamos o aplicativo usando ng-app e declarou nosso controlador de aplicativo no HTML. Também definimos o módulo do aplicativo e criamos o MainCtrl controlador, o tomada serviço e o notas adesivas diretiva.

09. Criação de uma nota adesiva

Agora que temos o esqueleto do aplicativo AngularJS no lugar, começaremos a construir o recurso de criação.

app.controller (’MainCtrl’, function ($ scope, socket) {// B.1
$ scope.notes = []; // B.2

// Incoming
socket.on (’onNoteCreated’, function (data) {// B.3
$ scope.notes.push (dados);
});

// Extrovertido
$ scope.createNote = function () {// B.4
var note = {
id: new Date (). getTime (),
título: 'Nova Nota',
corpo: 'Pendente'
};

$ scope.notes.push (nota);
socket.emit ('createNote', nota);
};

B.1 AngularJS tem um recurso de injeção de dependência embutido, então estamos injetando um $ scope objeto e o tomada serviço. O $ scope object serve como ViewModel e é basicamente um objeto JavaScript com alguns eventos incorporados para permitir a ligação de dados bidirecional.

B.2 Estamos declarando a matriz na qual usaremos para vincular a visualização.

B.3 Estamos adicionando um ouvinte para o onNoteCreated evento no tomada serviço e empurrando a carga útil do evento para o $ scope.notes variedade.

B.4 Declaramos um createNote método que cria um padrão Nota objeto e o empurra para o $ scope.notes variedade. Ele também usa o tomada serviço para emitir o createNote evento e passar o nova nota objeto junto.

Portanto, agora que temos um método para criar a nota, como o chamamos? Essa é uma boa pergunta! No arquivo HTML, adicionamos a diretiva AngularJS integrada ng-click ao botão e, em seguida, adicione o createNote chamada de método como o valor do atributo.

button id = "createButton" ng-click = "createNote ()"> Criar nota / botão>

É hora de uma rápida revisão do que fizemos até agora. Nós adicionamos uma matriz ao $ scope objeto no MainCtrl isso vai conter todas as notas para o aplicativo. Também adicionamos um createNote método no $ scope objeto para criar uma nova nota local e, em seguida, transmitir essa nota para os outros clientes através do tomada serviço. Também adicionamos um ouvinte de eventos no tomada serviço para que possamos saber quando outros clientes criaram uma nota para que possamos adicioná-la à nossa coleção.

10. Exibindo as notas adesivas

Agora temos a capacidade de criar um objeto de nota e compartilhá-lo entre os navegadores, mas como realmente o exibimos? É aqui que entram as diretivas.

As diretivas e suas complexidades são um assunto vasto, mas a versão resumida é que elas fornecem uma maneira de estender elementos e atributos com funcionalidade personalizada. As diretivas são facilmente minha parte favorita sobre AngularJS porque permitem que você crie essencialmente uma DSL (Domain Specific Language) inteira em torno de seu aplicativo em HTML.

É natural que, uma vez que iremos criar notas adesivas para o nosso quadro de colaboração, devemos criar um notas adesivas diretiva. As diretivas são definidas chamando o método de diretiva em um módulo que você deseja declarar e passando um nome e uma função que retorna um objeto de definição de diretiva. O objeto de definição de diretiva tem muitas propriedades possíveis que você pode definir nele, mas vamos usar apenas algumas para nossos propósitos aqui.

Recomendo que você verifique a documentação do AngularJS para ver todas as listas de propriedades que você pode definir no objeto de definição de diretiva.

app.directive (’stickyNote’, function (socket) {
var linker = função (escopo, elemento, atributos) {};

var controlador = função ($ escopo) {};

Retorna {
restringir: 'A', // C.1
link: linker, // C.2
controlador: controlador, // C.3
escopo: {// C.4
nota: ’=’,
ondelete: ’&’
}
};
});

C.1 Você pode restringir sua diretiva a um certo tipo de elemento HTML. Os dois mais comuns são elemento ou atributo, que você declara usando E e UMA respectivamente. Você também pode restringi-lo a uma classe CSS ou a um comentário, mas eles não são tão comuns.

C.2 A função de link é onde você coloca todo o seu código de manipulação DOM. Existem algumas exceções que eu encontrei, mas isso é sempre verdade (pelo menos 99 por cento das vezes). Esta é uma regra básica fundamental do AngularJS e é por isso que a enfatizei.

C.3 A função do controlador funciona exatamente como o controlador principal que definimos para a aplicação, mas o $ scope objeto que estamos passando é específico para o elemento DOM em que a diretiva reside.

C.4 AngularJS tem um conceito de escopo isolado, que permite definir explicitamente como o escopo de uma diretiva se comunica com o mundo externo. Se não tivéssemos declarado o escopo, a diretiva teria herdado implicitamente do escopo pai com um relacionamento pai-filho. Em muitos casos, isso não é o ideal. Ao isolar o escopo, minimizamos as chances de que o mundo externo possa afetar inadvertidamente e adversamente o estado de sua diretiva.

Eu declarei a vinculação de dados bidirecional para Nota com o = símbolo e uma expressão ligada a ondelete com o & símbolo. Por favor, leia a documentação do AngularJS para uma explicação completa do escopo isolado, pois é um dos assuntos mais complicados da estrutura.

Então, vamos realmente adicionar uma nota adesiva ao DOM.

Como qualquer boa estrutura, o AngularJS vem com alguns recursos realmente excelentes, prontos para uso. Um dos recursos mais úteis é ng-repeat. Esta diretiva AngularJS permite que você passe um array de objetos e duplica qualquer tag que esteja tantas vezes quanto houver itens no array. No caso abaixo, estamos iterando no notas array e duplicando o div elemento e seus filhos para o comprimento do notas variedade.

div sticky-note ng-repeat = "nota nas notas" note = "nota" ondelete = "deleteNote (id)">
tipo de botão = "botão" ng-click = "deleteNote (note.id)"> × / botão>
input ng-model = "note.title" ng-change = "updateNote (note)" type = "text">
textarea ng-model = "note.body" ng-change = "updateNote (nota)"
> {{note.body}} / textarea>
/ div>

A beleza de ng-repeat é que ele está vinculado a qualquer array que você passar e, ao adicionar um item ao array, seu elemento DOM será atualizado automaticamente. Você pode dar um passo adiante e repetir não apenas os elementos DOM padrão, mas também outras diretivas personalizadas. É por isso que você vê notas adesivas como um atributo no elemento.

Existem dois outros bits de código personalizado que precisam ser esclarecidos. Isolamos o escopo no lembretes diretiva em duas propriedades. O primeiro é o escopo isolado definido pela ligação no Nota propriedade. Isso significa que sempre que o objeto de nota muda no escopo pai, ele atualiza automaticamente o objeto de nota correspondente na diretiva e vice-versa. O outro escopo isolado definido está no ondelete atributo. O que isso significa é que quando ondelete é chamado na diretiva, ele chamará qualquer expressão que estiver no ondelete atributo no elemento DOM que instancia a diretiva.

Quando uma diretiva é instanciada, ela é adicionada ao DOM e a função de link é chamada. Esta é uma oportunidade perfeita para definir algumas propriedades DOM padrão no elemento. O parâmetro do elemento que estamos passando é na verdade um objeto jQuery e, portanto, podemos realizar operações jQuery nele.

(Na verdade, o AngularJS vem com um subconjunto do jQuery integrado, mas se você já incluiu a versão completa do jQuery, o AngularJS irá adiá-lo.)

app.directive (’stickyNote’, function (socket) {
var linker = function (escopo, elemento, atributos) {
// Alguma iniciação DOM para torná-lo bom
element.css ('left', '10px');
element.css ('top', '50px');
element.hide (). fadeIn ();
};
});

No código acima, estamos simplesmente posicionando a nota adesiva no palco e fazendo com que ela apareça gradualmente.

11. Excluindo uma nota adesiva

Agora que podemos adicionar e exibir uma nota adesiva, é hora de excluí-la. A criação e exclusão de notas adesivas é uma questão de adicionar e excluir itens do array ao qual as notas estão vinculadas. É responsabilidade do escopo pai manter esse array, e é por isso que originamos a solicitação de exclusão de dentro da diretiva, mas deixamos o escopo pai fazer o trabalho pesado real.

É por isso que passamos por todo o trabalho de criar um escopo isolado definido por expressão na diretiva: para que a diretiva pudesse receber o evento delete internamente e passá-lo para seu pai para processamento.

Observe o HTML dentro da diretiva.

tipo de botão = "botão" ng-click = "deleteNote (note.id)"> × / botão>

A próxima coisa que vou dizer pode parecer um longo caminho, mas lembre-se que estamos do mesmo lado e fará sentido depois que eu elaborar. Quando o botão no canto superior direito do post-it é clicado, estamos chamando deleteNote no controlador da diretiva e passando no note.id valor. O controlador então chama ondelete, que então executa qualquer expressão que conectamos a ela. Até agora tudo bem? Estamos chamando um método local no controlador que, em seguida, o transfere chamando qualquer expressão definida no escopo isolado. A expressão que é chamada no pai por acaso é chamada deleteNote também.

app.directive (’stickyNote’, function (socket) {
var controlador = função ($ escopo) {
$ scope.deleteNote = function (id) {
$ scope.ondelete ({
Eu fiz
});
};
};

Retorna {
restringir: 'A',
link: linker,
controlador: controlador,
alcance: {
nota: ’=’,
ondelete: ’&’
}
};
});

(Ao usar o escopo isolado definido pela expressão, os parâmetros são enviados em um mapa de objetos.)

No escopo pai, deleteNote é chamado e faz uma exclusão razoavelmente padrão usando o angular.forEach função de utilidade para iterar sobre a matriz de notas. Uma vez que a função cuida de seus negócios locais, ela segue em frente e emite o evento para que o resto do mundo reaja de acordo.

app.controller (’MainCtrl’, function ($ scope, socket) {
$ scope.notes = [];

// Incoming
socket.on (’onNoteDeleted’, function (data) {
$ scope.deleteNote (data.id);
});

// Extrovertido
$ scope.deleteNote = function (id) {
var oldNotes = $ scope.notes,
newNotes = [];

angular.forEach (oldNotes, function (note) {
if (note.id! == id) newNotes.push (note);
});

$ scope.notes = newNotes;
socket.emit (’deleteNote’, {id: id});
};
});

12. Atualizando uma nota adesiva

Estamos fazendo um progresso fantástico! A esta altura, espero que você esteja começando a ver alguns padrões emergindo deste tour turbulento que estamos fazendo. O próximo item da lista é o recurso de atualização.

Vamos começar com os elementos DOM reais e segui-los até o servidor e de volta ao cliente. Primeiro, precisamos saber quando o título ou o corpo do post-it está sendo alterado. O AngularJS trata os elementos do formulário como parte do modelo de dados para que você possa conectar a vinculação de dados bidirecional em um piscar de olhos. Para fazer isso, use o modelo ng diretiva e coloque na propriedade que você deseja vincular. Neste caso, vamos usar note.title e note.body respectivamente.

Quando qualquer uma dessas propriedades muda, queremos capturar essas informações para repassá-las. Conseguimos isso com o ng-change diretiva e usá-lo para chamar updateNote e passe o próprio objeto de nota. AngularJS faz algumas verificações sujas muito inteligentes para detectar se o valor de tudo o que está em modelo ng mudou e, em seguida, executa a expressão que está em ng-change.

input ng-model = "note.title" ng-change = "updateNote (note)" type = "text">
textarea ng-model = "note.body" ng-change = "updateNote (note)"> {{note.body}} / textarea>

A vantagem de usar ng-change é que a transformação local já aconteceu e somos apenas responsáveis ​​por transmitir a mensagem. No controlador, updateNote é chamado e a partir daí vamos emitir o updateNote evento para o nosso servidor transmitir para os outros clientes.

app.directive (’stickyNote’, function (socket) {
var controlador = função ($ escopo) {
$ scope.updateNote = function (note) {
socket.emit ('updateNote', nota);
};
};
});

E no controlador de diretiva, estamos ouvindo o onNoteUpdated evento para saber quando uma nota de outro cliente foi atualizada para que possamos atualizar nossa versão local.

var controlador = função ($ escopo) {
// Incoming
socket.on (’onNoteUpdated’, function (data) {
// Atualizar se a mesma nota
if (data.id == $ scope.note.id) {

$ scope.note.title = data.title;
$ scope.note.body = data.body;
}
});
};

13. Movendo uma nota adesiva

Neste ponto, basicamente demos uma volta ao redor da piscina infantil CRUD e a vida é boa! Apenas por causa de um truque de salão para impressionar seus amigos, vamos adicionar a capacidade de mover notas pela tela e atualizar as coordenadas em tempo real. Não entre em pânico - são apenas mais algumas linhas de código. Todo esse trabalho duro vai valer a pena. Eu prometo!

Convidamos um convidado especial, jQueryUI, para a festa e fizemos tudo pelos draggables. Adicionar a capacidade de arrastar uma nota localmente leva apenas uma linha de código. Se você adicionar element.draggable (); para sua função de vinculador, você começará a ouvir "Eye of the Tiger" de Survivor porque agora você pode arrastar suas notas.

Queremos saber quando o arrastamento parou e capturar as novas coordenadas para passar adiante. jQueryUI foi construída por algumas pessoas muito inteligentes, então quando o arrastar para, você simplesmente precisa definir uma função de retorno de chamada para o evento de parada. Nós pegamos o note.id fora do objeto de escopo e os valores CSS esquerdo e superior do ui objeto. Com esse conhecimento, fazemos o que sempre fizemos: emitir!

app.directive (’stickyNote’, function (socket) {
var linker = function (escopo, elemento, atributos) {
element.draggable ({
parar: função (evento, ui) {
socket.emit (’moveNote’, {
id: scope.note.id,
x: ui.position.left,
y: ui.position.top
});
}
});

socket.on ('onNoteMoved', function (data) {
// Atualizar se a mesma nota
if (data.id == scope.note.id) {
element.animate ({
esquerda: data.x,
topo: data.y
});
}
});
};
});

Neste ponto, não deve ser surpresa que também estejamos ouvindo um evento relacionado à movimentação do serviço de soquete. Neste caso é o onNoteMoved e se a nota for uma correspondência, atualizamos as propriedades CSS esquerda e superior. Bam! Feito!

14. O bônus

Esta é uma seção de bônus que eu não incluiria se não estivesse absolutamente confiante de que você poderia alcançá-la em menos de 10 minutos. Vamos implantar em um servidor ativo (ainda estou surpreso com a facilidade de fazer isso).

Primeiro, você precisa se inscrever para uma avaliação gratuita do Nodejitsu. A avaliação é gratuita por 30 dias, o que é perfeito para você molhar os pés.

Depois de criar sua conta, você precisa instalar o pacote jitsu, o que você pode fazer a partir da linha de comando via $ npm install jitsu -g.

Então você precisa fazer o login a partir da linha de comando via $ jitsu login e insira suas credenciais.

Certifique-se de que está no seu aplicativo diretamente, digite $ jitsu deploy e analise as perguntas. Normalmente deixo o máximo possível como padrão, o que significa que dou um nome ao meu aplicativo, mas não um subdomínio etc.

E, meus queridos amigos, isso é tudo! Você obterá a URL de seu aplicativo a partir da saída do servidor, uma vez que ele tenha sido implementado e esteja pronto para funcionar.

15. Conclusão

Abordamos muitos aspectos do AngularJS neste artigo e espero que você tenha se divertido muito no processo. Acho que é realmente legal o que você pode realizar com AngularJS e Socket.io em aproximadamente 200 linhas de código.

Houve algumas coisas que não abordei para me concentrar nos pontos principais, mas eu o encorajo a puxar para baixo a fonte e brincar com o aplicativo. Construímos uma base sólida, mas ainda há muitos recursos que você pode adicionar. Comece a hackear!

Lukas Ruebbelke é um entusiasta da tecnologia e é coautor de AngularJS in Action for Manning Publications. Sua coisa favorita a fazer é deixar as pessoas tão empolgadas quanto ele com as novas tecnologias. Ele dirige o Phoenix Web Application User Group e já hospedou vários hackathons com seus colegas parceiros no crime.

Gostou disso? Leia isso!

  • Como fazer um aplicativo
  • Nossas fontes favoritas da web - e elas não custam um centavo
  • Descubra o que vem por aí em Realidade Aumentada
  • Baixe texturas grátis: alta resolução e pronto para usar agora
Recomendado Por Nós
Crie um pôster clássico com serifa
Descobrir

Crie um pôster clássico com serifa

Como de igner gráfico , tendemo a eguir alguma regra de ouro: a men agem deve er clara, a core devem ter alguma harmonia e o texto deve er equilibrado e legível.Ma à veze , para criar a...
As melhores alternativas do Slack
Descobrir

As melhores alternativas do Slack

Ante de começarmo com no a li ta de alternativa ao lack, vamo dar uma olhada no próprio lack. Lançado pela primeira vez em 2013 como uma plataforma de men agen , o lack cre ceu e e torn...
Como modelar um retrato feminino 3D realista
Descobrir

Como modelar um retrato feminino 3D realista

Fazer um retrato feminino reali ta empre foi algo que e tava no topo da minha li ta de tarefa . Vai er uma longa jornada, e fazer i o da maneira certa empre exigirá muito tempo e paciência. ...