Jails – Aplicação Flickr

E aí meus queridos!

No post passado dei uma pequena introdução sobre meu projeto solitário Jails. Neste post gostaria de refazer aquela nossa aplicação Flickr véia que fiz na primeira conversa sobre o padrão MVC no javascript. Para quem não viu ou não se lembra, tinha feito um screencast sobre como montar uma aplicação usando alguns conceitos do MVC sob meu ponto de vista.

Ela é uma aplicação bem simples, trazendo uma lista de imagens do flickr de acordo com uma busca feita num campo de texto.

Agora com o Jails, vou refazer toda a lógica para demonstrar na prática o uso desse modesto framework. Sem muito papo, vamos à Model:

/**
 * @class Model
 */

;(function(namespace){

	namespace.Home = {};

	Jails.extend('Model', Home.Model = {

		data   :{},

		flickr :{
			url :'http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?',
			params :{
				tags :'',
				format : 'json'
			}
		}
	});

})(window)

Iniciamos a variável Home que carregará as 3 camadas. Definimos uma propriedade flickr que armazenará dados como os parâmetros ajax que enviaremos para o rest do flickr e a url do ajax. tags é o parâmetro variável que pegaremos do campo de texto.

Iniciada nossa model, vamos à View:

;(function(Model){

    //Static

	var list, load, form;

	Jails.extend('View', Home.View = {

		loader	:'loading',

		templates :{
			flickr :'<li><div class="image"><img src="@{image}" alt="@{title}" /></div></li>'
		},

		initialize :function(){

			list = $('#list');
			load = $('.load');
			form = $('form');

			form.submit( this.submit );
		},

		submit :function(){
			Jails.url.redirect('/' + this.search.value);
			this.search.value = '';
			return false;
		},

		wait :function(){
			load.addClass(Home.View.loader);
		},

		loaded :function(){
			load.removeClass(Home.View.loader);
		}		

	});

})(Home.Model)

Lá em cima pegamos a Model (já criada) como argumento para se quisermos usarmos na nossa View. Estendemos nosso objeto Home.View com o Jails, e construímos os métodos e propriedades em cima desse objeto. Percebam que iniciei 3 variáveis estáticas lá no início do código, que vão ser usadas nos métodos que criaremos na view e para que sejam visíveis à todos os métodos, precisam ser estáticas.

  • list : Vai carregar o elemento dom ul. Que será a lista de imagens trazidas do flickr.
  • form : O elemento formulário do html, que possui o campo de texto da pesquisa.
  • load : Um div para mostrar um gif de loading enquanto o ajax está sendo processado.

Existe também uma propriedade na view chamada “loader” que possui o nome da classe a ser colocada no div para exibir o gif animado, e outra propriedade “templates” que possui o template do html que será gerado dinamicamente pelo javascript quando possuirmos o json do flickr. Reparem que há duas variáveis dentro desse template, image e title. Eles serão substituídos pelos valores do json do flickr.

No Jails toda view possui um método chamado .initialize() que é chamado automaticamente pelo framework. Lá, nós setamos as variáveis estáticas com os elementos do dom, e adicionamos o evento .submit do formulário. Este método da view vai pegar o valor do campo e redirecionar a url para o valor buscado, através do método url.redirect().

Ainda temos mais dois métodos na nossa view o .wait() e o loaded() que criei para adicionar ou remover a classe de loading do div de classe “load”.

Percebam que na View, apenas mexemos com os elementos html, guardamos em variáveis, setamos eventos e etc. Este é o papel da View na minha modesta opinião.

Certo, vamos agora pegar os dados do flickr via controller. O resultado final será passado para a view que se encarregará de printar as imagens na tela.

/**
 * @class Controller
 */
;(function(Model, View){

	Jails.extend('Controller', Home.Controller = {

		index :function(){
			get_flickr();
		}
	});	

	function get_flickr(){

		var cache = Jails.get_cache('flickr');

		if(cache)
			View.display(cache);
		else{
			View.wait();
			$.getJSON( Model.flickr.url, Model.flickr.params, ajax );
        	}
	};

	function ajax(response){

		var
			html = '', item = response.items; 

		for(var i = 0; i < response.items.length) ; i++){
			html += View.inject({
				image :item[i].media.m,
				title :item[i].title,
			}, View.templates.flickr);
		}

		View.display( html );
		Jails.set_cache('flickr', html);
	};

})(Home.Model, Home.View)

Novamente nós estendemos nosso objeto Home com a classe ‘Controller’ do Jails, pegamos também como parâmetro no closure nossa View e nossa Model para facilitar. Ao carregar a aplicação pela primeira vez, nós cairemos na action “index” através das rotas (Mostrarei mais pra frente).

Para não poluir muito o objeto Controller, separei em duas funções diferentes para fazer o ajax.

get_flickr() :
Pega o conteúdo do cache com flag “flickr” se existir esse conteúdo ele o manda pra view mostrar.
Caso contrário, ele faz uma requisição ajax para o flickr passando os valores da model contendo os parâmetros e a url do ajax. Passa também a função de nome “ajax” como callback.

ajax() :
É o callback do ajax, ele pega o array de objetos retornados do flickr e itera sobre ele para montar o html final que iremos mostrar na tela. No for uso um método muito útil da View que é o .inject().

E quando eu falo que ele é útil, ele é útil mesmo, significa que raramente vai deixar de usar numa aplicação, especialmente se for uma aplicação ajax. Ele recebe dois parâmetros, um json e uma string. Ele irá retornar a string que passou substituindo wildcards como “@{nome}” pelo valor reespectivo no json.
e.g {nome :’Eduardo’}.

Vejam que no for, não passei uma string enorme e feiosa, passei a propriedade da nossa view que carrega essa string feiosa. Fica muito mais limpo o código, mais organizado, uma vez que a parte de “mostrar” deve ser centralizada na view. A variável “html” concatena a cada passo a string com o novo item do array através do operador “+=”.

Ao final, mandamos essa string com o html final para a nossa view mostrar na tela. Depois ainda salvamos no cache com uma flag chamada “flickr”. Então, quando cairmos nessa rota novamente, o método .get_flickr() anteriormente citado, vai verificar se há algo no cache e enviar direto para a view sem requisitar novamente o ajax. Isso é MUITO útil quando se quer fazer aqueles esquemas de carregamento ajax de sessões no conteúdo central do seu site.

E ARROTA? BUUUUUUUURG! Nossa…foi péssimo isso…

Bom, como a action “index” da controller é chamada? Definimos isso nas rotas:

/**
 * @singleton Routes
 */
;(function( namespace ){

	namespace.routes = [
		{ '#!/' :Home.Controller.index }
	] 

})(Jails)

Bem simples e intuitivo, quando cair no root do site, será executado o método .index() da Home.Controller.
Coisa fina né ? =)

Então, a aplicação ainda é inútil, uma vez que só carrega o ajax no momento que visita o site pela primeira vez, não existe a mudança do parâmetro da model para buscarmos novas imagens no ajax.

Lembram que na view, fazemos um redirect ao postar o formulário?
Esse redirect cai numa rota assim : ‘#!/minha busca’. Esta string “minha busca” precisa ser usada como parâmetro na requisição ajax para trazer as novas imagens.

Então precisamos definir uma rota nova:

/**
 * @singleton Routes
 */
;(function( namespace ){

	namespace.routes = [

		{ '#!/:tag' :Home.Controller.tag },
		{ '#!/' :Home.Controller.index }
	] 

})(Jails)

Só é executada uma rota apenas, e sua ordem de prioridade é maior olhando de cima para baixo. Então a nova rota precisa estar antes da rota ‘#!/’ senão ela nunca será executada. Eu poderia ter usado a mesma action index na rota e pegado o parametro de busca, mas preferi criar outra action chamada “tag” para ser mais didático =).

Criada a rota para o parâmetro, criamos a action .tag() que vai receber um parâmetro “:tag” como está ali na rota.

Jails.extend('Controller', Home.Controller = {

	index :function(){
		get_flickr();
	},

	tag :function(tag){
		Model.flickr.params.tags = tag;
	        this.index();
	}
});

Pronto! Pegamos o parametro “tag” e alteramos o valor do parâmetro do ajax contido na nossa model.
Pela natureza da organização MVC, é apenas isso que a action .tag() deve fazer, o resto é exatamente o que a index faz. Então, basta chamar a index para chamarmos o método get_flickr() para trazermos as novas imagens de acordo com a busca.

Vejam que há bastante organização e com isso reutilização de código. Além das vantagem de métodos DRY como o .inject e de otimização como o set_cache e o get_cache.

Ahh… como fica o método .display() da View?
Ela só precisa dizer que não está mais carregando e mostrar o seu conteúdo novo:


display :function(html){

	var html = $(html);
	this.loaded();
	list.html(html);
}

O método .display() pode ser escrito com mais purpurina, poderia fazer algum efeito para mostrar as novas imagens etc.

Queria mostrar meio na prática como usar o Jails na mesma aplicação que havia feito anteriormente. Acho que agora dá pra ter uma idéia de como ele funciona e qual a lógica que usei para montar o framework. Ele é simples, leve, e te direciona à um desenvolvimento mais organizado além de tentar evitar que reescreva código à toa.

Ainda vou montar um screencast mostrando alguns métodos que não foram mostrados aqui. Tive que enxugar um pouco a aplicação do flickr para que “coubesse” no post. Eu fiz ela com mais viadagens, considerando também o caso de não haver resultados e etc. Vou deixar disponível para download para quem quiser dar uma investigada no código pra aprender um pouco mais sobre o Jails.

Um abraço.

App Flickr – Jails