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/

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s