Archive for category New school

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

1 Comentário

[Vagas] F.biz – Front-End – Estágio

Tenho o prazer imenso de divulgar como o primeiro post sobre vagas no blog, duas vagas para trabalhar na F.biz, onde eu trabalho e considero uma das melhores empresas em que trabalhei, tanto profissionalmente como em ambiente de trabalho.

Na F.biz o ambiente é descontraído, você tem fácil acesso aos sócios e diretores, fica mais próximo dos processos da empresa e assim adquirindo conhecimento e experiência não só na parte técnica como na parte gerencial e de processos, fundamental para a carreira profissional.

Além de uma ótima área de convivência para descontração, refeições…

Descrição da vaga: http://www.facebook.com/notes/trampos/fbiz-sp-busca-2-estagi%C3%A1rios-em-desenvolvimento-front-end/10150750433620790

Deixe um comentário

O Mvc e o Javascript – Na prática

Fala galera,

Eu tinha prometido uma implementação do Mvc na prática, e resolvi fazer isso através de um screencast, porque era mais fácil de explicar, mais fácil de demonstrar…

Fiz uma aplicação simples usando a API do Flickr, e fiz já considerando que conhecem o esquema Mvc que comentei no post anterior, para não perder muito tempo com explicações.

Ainda quero fazer um post mostrando a aplicação da galeria de imagens, mas vou precisar de um pouco mais de tempo para formulá-lo.

Perdoem a voz de ganso morto, mas é a voz que Deus me deu. =)

Qualquer dúvida, ou crítica, postem aqui. =P

Abraço.

Screencast : O Mvc e o Javascript – Na prática
Aprox. 15 min

6 Comentários

Google Developer Day – Questão 5

E aí galera, tranquilo?

Eu queria postar sobre o DevQuiz do Google faz um tempinho, em particular a questão número 5 que era um pouco mais complexa, mas não achei correto publicar a minha solução enquanto as inscrições ainda rolavam, mas agora que já elvis, gostaria de publicá-la aqui.

A história na verdade começou quando na Agência, ao saber do questionário, a galera de Interface começou a resolver as questões, cada um usando a sua lógica. No final, alguns fizeram e compartilharam suas soluções.

Eu achei interessante isso, porque saiu um pouco da rotina meio “Fábrica de Pastel” e começamos a discutir as soluções propostas de cada um, percebemos que pensamos de forma muito diferente, não pior, não melhor, mas diferente.

Como o tempo contava na hora de responder as perguntas, não se pode criticar o código final seja lá qual for o problema, se de resolução, performance, design ou qualquer outro…

E como defensor ferrenho do Javascript, entusiasta, estudante e escritor do assunto ( já que tenho o blog ) acertei na mosca a resposta, certo?

BÉEEEEEEEEEE! FAIL!

Dog Fail

Meu script foi considerado o mais dificil de se entender ( o que é muito subjetivo ) e no final das contas não funcionou retornando a resposta certa =\.

DAMN!!!

Maaasssss felizmente acabei aprendendo coisas interessantes nos códigos alheios, o que valeu a brincadeira.

Claro, após revisto os erros, reescrevi o código envolvendo as funções soltas em um objeto e preservei TODA a lógica, removendo as redundâncias.

O primeiro código fail foi esse:

var double_n = function(string){
	    return !!string.match(/1{2}|2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|9{2}|0{2}/g)
	}

	var soma_n = function(string){
	    var cont = 0
	    string.replace(/\d/g, function(d){
	        cont += +d
	        return d
	    })
	    return !(!!(!cont % 2))
	}

	var last_first = function(string){
	    if (string.charAt(0) == string.charAt(string.length - 1)) 
	        return false
	    return true
	}

	//Telefone é o textarea com os números
	var array = $('#telefone').val().split(/\n/)
	//O último elemento vinha vazio por causa do último \n
		array.pop()
			
	var contador = 0
	$.each(array, function(i){
	    if (double_n(array[i]) && soma_n(array[i]) && last_first(array[i])) 
	        contador++
	})
	console.log(contador)

Meu erro foi a mania que tenho de querer me certificar de que o retorno de uma condição retorne um valor booleano de fato ao invés de um Truthy ou Falsy. Ou seja, num retorno de string vazia, tenho a mania de transformar antes em um boolean tipo: !!”"

Com isso, errei ali no retorno da soma_n, na precedência dos operadores unários.

Mas acabei aprendendo com esse erro e além disso aprendi a melhorar minha RegExp verbosa, por uma bem mais simples na solução do líder técnico da área.

Aí vai o código reescrito com as modificações necessárias, removi algumas redundâncias, mas a lógica se mantém a mesma:

var Resolution = {

	of :function(array, cont){
		var el = null
		var i = 0
			with( this ) while( el = array[i++] )
				cont += if_hasnt_dup(el) && 
					if_sum_isnt_mod2(el) && 
					if_last_isnt_first(el) ? 1:0
		return cont
	},

	if_sum_isnt_mod2 :function(string){
		var cont = 0
		string.replace(/\d/g, function(d){ cont += +d })
		return !(cont % 2)
	},

	if_hasnt_dup :function(string){
		return !(!!string.match(/(\d)\1/g))
	},

	if_last_isnt_first :function(string){
		return !(string.charAt(0) == string.charAt(string.length - 1))
	}
}

alert( Resolution.of(arr, 0) )

Eu usei um with ali, vocês sabem que é horrível e que jamais devemos usar o with, mas eu coloquei ali para dificultar ainda mais o código rsrsrs.

Para quem programa em Javascript, a resolução do problema foi bem rápida, uma porque a própria linguagem permite isso a outra porque era só botar o código no firebug e executar! Que foi o que eu fiz e imagino que muitos outros fizeram também.

Também fiz em Ruby, usando a mesma lógica que usei em Javascript:

require 'jcode'
		def sum_isnt_mod2?(string) 
			cont = 0
			string.each_char { |c| cont+= c.to_i }
			(cont%2).zero?
		end
		
		def hasnt_dup?(string)
			!(string =~ /(\d)\1/) 
		end
		
		def last_isnt_first?(string)
			!(string[0,1] == string[-1,1])
		end
		
		def answer (arr,count)
			arr.each do |v|
				count += 1 if sum_isnt_mod2?(v) && hasnt_dup?(v) && last_isnt_first?(v) 			
			end
			count
		end	
	
	puts answer(array, 0)

Em ambas as linguagens, resolvi considerando os números de telefones como strings ao invés de números, porque achei muito mais fácil.

Você que está lendo o blog agora, resolveu a questão? Acertou ? Errou ?
Posta sua solução nos comentários, acertando ou não tenho certeza que há algo para aprender no código, sempre há…

Se resolveu usando outra linguagem de programação, MELHOR AINDA, poste para mostrarmos as diferenças das linguagens, seria muito interessante. ;)

Obrigado mais uma vez pela atenção galera, um grande abraço!

[Update]
A Carmen Sachetto que faz o Curso de Ciência da Computação comigo no Mackenzie, fez esta solução em Scheme!!

Muito foda, olha só:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 		No peculiar condado de Compiladorshire, todos os telefones têm 6 dígitos.            ; 
; 		A companhia telefônica estabelece as seguintes regras sobre os números:              ;
;		Não pode haver dois dígitos consecutivos idênticos, porque isso é chato;             ;
;		A soma dos dígitos tem que ser par, porque isso é legal;                             ;
;		O último dígito não pode ser igual ao primeiro, porque isso dá azar.                 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   	função principal        ;
; entrada: uma lista com listas ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (Principal lista)
  (if (null? lista) '()
  (if (= (Verifica (car lista)) 0) (cons (car lista) (Principal (cdr lista)))
  (Principal (cdr lista)))))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;    Verifica se um numero de telefone é valido   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (Verifica lista)
  (if (= (Iguais (cdr lista) (car lista)) 
         (Epar (somaElem lista 0)) 
         (PrimUlt (cdr lista) (car lista) (car lista)) 
         0) 0
  (if (not (= (Iguais (cdr lista) (car lista)) 
         (Epar (somaElem lista 0)) 
         (PrimUlt (cdr lista) (car lista) (car lista)) 
         0) ) 1)))    

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   verifica se ultimo elemento é igual a primeiro                         ;
;   se ultimo elemento for igual ao primeiro, retorna 1, senão retorna 0   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (PrimUlt lista primeiro anterior)
  (if (null? lista) (comparaElem primeiro anterior)
  (PrimUlt (cdr lista) primeiro (car lista))))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     compara dois elementos    ;
;   se forem iguais, retorna 1  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (comparaElem a b)
(if ( = a b) 1
(if (not ( = a b)) 0)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  soma elementos de uma lista  ;
;       retorna soma            ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (somaElem lista soma)
(if (null? lista) soma
(somaElem (cdr lista) (+ soma (car lista)))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     verifica se um número é par ou não     ;
;    se for par retorna 0, senão retorna 1   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (EPar numero)
  (if (= (remainder numero 2) 0) 0
  (if (not (= (remainder numero 2) 0)) 1)))

  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   verifica se há dois dígitos consecutivos idênticos  ;
;                 se houver, retorna 1                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define (Iguais lista anterior)
  (if (null? lista) 0
  (if (= anterior (car lista)) 1
  (Iguais (cdr lista) (car lista)))))

Bem legal né ?!?!?!

Deixe um comentário

Paginação em Javascript – Parte 2 Final

Olá galera, esse post é a segunda parte sobre paginação em Javascript, dessa vez vou mostrar a segunda forma que havia comentado no post anterior sobre como fazer a paginação.

Na forma anterior, lembram, a paginação tinha como função pegar os elementos e manipulá-los por conta própria, sendo que você como desenvolvedor bastaria passar alguns parâmetros para a classe, ela se encarregaria de fazer tudo sozinha.

É claro que não é a melhor classe do mundo, nem a mais eficiente, para pequenos propósitos ela funciona muito bem, mas não é um bom código por exemplo para um grande número de páginas. Não sei se perceberam, mas o script toda hora, ou melhor, cada vez que é clicado em uma nova página, ele remove todos os nodos das páginas, e remove também todos os nodos da página atual e depois reescreve-os, criando tudo de novo e fazendo o append no documento.

Isso é extremamente dispendioso, extremamente lento no que diz respeito ao dom. Vocês vão perceber isso se passarem um “size” de tamanho muito grande, gerando muitas páginas. Ao clicar numa página que não a atual, vão perceber que o script demora muito tempo pra ser executado, porque precisa apagar todos aqueles nodos e gerá-los de novo. O DOM é muito lento, e da forma como é feita na jsPagination, o torna mais lento ainda.

Há ainda um outro problema, que tenho percebido de uns tempos pra cá no desenvolvimento dos meus scripts. Existe um erro grande ali no design do script. Se é um script que faz paginação, ele só deveria fazer a lógica da paginação. O que você vai fazer com as páginas e como vai montá-las é um outro problema.

A medida que o script cresce ele se torna mais complexo e quanto mais complexo ele fica, mais difícil de dar manutenção e maiores são as chances de surgirem bugs, é inevitável.

Então comecei a “atomizar” mais minhas “classes” fazendo com que elas apenas resolvam o problema que elas precisam resolver e deixar o resto na mão de outra classe. Menos é mais, quanto mais modularizado o código, melhor.

Neste post vou resolver apenas o segundo problema da classe, ela ainda vai adicionar e remover os elementos como fazia anterioremente, mas agora ela fará parte de uma outra classe de implementação, podendo ser melhorada ou estendida independentemente da classe de paginação em questão.

Então a classe atomizada, que apenas contém a lógica da paginação ficou assim:

;(function(namespace){
	
	var Interface = {
		actions : function(){			
			this . pages = function(){
				throw new Error('Implemente o método pages em sua classe/objeto')	
			}
			this . list = function(){
				throw new Error('Implemente o método list em sua classe/objeto')
			}
		}
	}
	
	var Paginator = {
		
		Class : function( target ){
			var json
			Interface.actions.apply( this )

			this.ready = null
			
			this.open = function(options){
					
					if(!json) json = options					
					else for(var i in options) json[i] = options[i]
				
				var pages = 
					Math.floor( json.size / json.show ) + 
					( ( json.size % json.show ) ? 1 : 0 )
									
				json.page = json.page ? json.page : 1	
				
				this.begin? this.begin() :null
				
					for( var i = 1; i <= pages; i++ )
						this.list.call? this.list.call(this, i, json) :null
					
					for( 
						var i = ( (json.page-1 ) * json.show ); 
						i< ( ( json.page-1 ) * json.show ) + 1 + ( json.show - 1 ); 
						i++
					){											
							if( i > json.size - 1 )
								break						
						this.pages.call? this.pages.call(this, i, json) :null						
					}	
					
				this.ready? this.ready() :null
			}			
			
		}
		
	}	
	
	namespace['Paginator'] = Paginator.Class
		
})(window)

Então essa classe só faria chamadas aos métodos que implementam a população de elementos html. O método open ao ser chamado configuraria as opções passadas, e chamaria os métodos públicos :

this.begin() : Método que é chamado logo antes dos outros métodos da paginação.
this.list() : Método que será chamado n vezes, sendo n o número de páginas.
this.pages() :Método que é chamado n vezes, sendo n o número de elementos à serem mostrados.
this.ready(): Quando tudo estiver sido chamado, ou seja, o ready é um callback.

Os métodos list e pages são chamados no escopo da classe de paginação e com dois argumentos, o primeiro um inteiro contendo o índice da paginação e o segundo contendo as opções passadas na chamada do método open, mantendo as opções privadas.

Então agora, como e onde a paginação vai colocar os elementos, não é mais o cargo do Paginator, é cargo de alguma outra classe que vai herdar Paginator ou compor a mesma.

A classe estando atomizada, ela fica bem mais fácil de ser entendida, mais fácil de dar manutenção, a lógica não fica misturada e código menos emaranhado.

A otimização, implementação de plugins e a forma como serão adicionados os elementos fica a cargo de uma outra classe como eu havia comentado anteriormente, através de herança ou composição.

Eu fiz um código para provar essa parte toda teórica, preferi usar composição para reproduzir o funcionamento da classe antiga que postei na primeira parte do post. Fiz um sigleton que será responsável por instanciar o Paginator, e fazer todo o trabalho com os nodos de inserção e remoção. É claro que o código ficou bem menor do que o antigo código do jsPaginator, mostrado no post anterior, isso não se deve apenas pela atomização da classe, mas porque eu não implementei os plugins que haviam na classe antiga e porque agora estou usando jQuery =), não tenho tanto tempo livre como tinha antigamente então preciso ser mais produtivo, caso contrário postaria um conteúdo novo a cada 5 meses =/.

Então, para finalizar o post e deixá-los em paz, aqui vai o singleton que havia comentado:

;(function(){
	
	var Paginate = {
		
		pages		:null,
		content		:null,
		temp_pages	:null,
		instance	:null,
		callback	:null,
		
		initialize : function( paginacao, content ){
			
			this.pages = $(paginacao)
			this.content = $(content)	
			this.temp_pages = this.pages.eq(0).clone()
					
			this.start()
			
			return this
		},
		
		start : function(){
			
			var self = this
			var paginator = new Paginator			
			
			this.instance = paginator
			
				paginator . begin = function(){
					self.content.empty()
					self.temp_pages.empty()	
				}
				
				paginator . pages = function(i, json){
					self.content.append( $('<p />').html(i+1) )										
				}
				
				paginator . list = function(i, json){
						
					var self_paginator = this
					var el = $('<a />').attr('href', '#').html(i)
						.click(function(){							
							json.page = i
							self_paginator.open()
						})
						
						if( json.page == i )
							el.addClass('selected')
					
					self.temp_pages.append( $('<li />').append(el) )
					
				}
				
				paginator . ready = function(){
					
					self.pages.each(function(){						
						$(this)
							.empty()
							.append( self.temp_pages.children().clone( true ) )
					})
					
					self.callback? self.callback(self) :null
								
				}				
		},
		
		open : function(options){
			this.callback = options.callback? options.callback :null
			this.instance.open(options)			
		}		
	
	}
	// Fim do Singleton
	
	Paginate	
		.initialize('.paginacao', '#pagina')		
		.open({ 
			size : 50, 
			show : 10,
			callback : function(){} 
		})
			
	
})()

Eu acho que o Singleton Paginate está bem simples, não vou perder muito tempo explicando como ele funciona, acho que dá pra entender. Se alguém tiver alguma dúvida ou não entendeu alguma parte, poste no comentário, que eu explico =). Ah, críticas também são muito bem vindas.

Abraço galera, obrigado pela atenção e até o próximo post. o/

Deixe um comentário

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.