Categorias
PWA - Progressive web apps

Media queries para Progressive Web Apps

Se você me segue no Twitter já viu que estou criando uma série sobre Progressive Web Apps, neste mini curso estou abordando os principais recursos na construção de um PWA. Para esse post vamos falar sobre um conteúdo complementar como adicionar um estilo específico através media queries para identificar uma PWA.

Se você ainda não ouviu falar sobre Progressive web apps ou aplicações web progressivas, PWA não é uma biblioteca ou framework, mas uma filosofia. Uma definição rápida seria o desenvolvimento de uma aplicação com uso de técnicas e recursos para proporcionar progressivamente uma melhor experiência para o usuário com foco em três pontos:

  • Confiabilidade
  • Velocidade
  • Engajamento

Quer saber mais acesse acompanhe a playlist do mini curso:

Também me siga no youtube para acompanhar mais vídeos

Voltando ao tema principal, quando construímos nossa PWA utilizamos o manifest.json, arquivo responsável por configurar nossa PWA, nome da aplicação(name e short_name), URL de inicialização(start_url), ícones(icons) e modo de exibição(display). O modo de exibição é o ponto chave deste tutorial e ele possui os seguintes modos:

  • fullscreen – como o nome sugere exibe em modo full screen sem nenhum elemento de interface do browser
  • standalone – Exibe como uma aplicação independente do browser, mas dependendo do sistema operacional podendo exibir alguns elementos de controle de navegação, por exemplo, iOS não exibe itens de navegação mas permite o controle de navegação através de gestos.
  • minimal-ui – Esse modo é exibido como uma aplicação independente mas forca a exibição de elementos de navegação
  • browser – Esse modo exibe como um browser convencional

Fullscreen e standalone

Agora que a gente teve uma introdução sobre os modos disponíveis, dois modos precisam de atenção especial fullscreen e standalone eles precisam prover uma navegação dentro da aplicação pois os elementos de navegação do browser são removidos, com isso o usuário pode ficar preso em sua aplicação e isso não é uma boa experiência.

Para isso podemos implementar media query para controlar a exibição controle de navegação ou apenas estilizar nossa aplicação. Atualmente temos um seletor para display-mode dentro de media queries, com isso podemos implementar media queries para diferentes modos, mas lembrando que a media query precisa seguir o parâmetro definido em seu manifest.json.

Media query para desktop e mobile

//media query para o modo de exibicao standalone presente tanto em mobile e desktop 
@media (display-mode: standalone) {
 
}

No código acima temos uma media query para o formato standalone, suportado por PWAs que rodam no desktop ou mobile, mas poderíamos utilizar um dos quatros padrões que vimos anteriores.

Um fato relevante neste momento até a versão 12.2 do iOS não temos suporte ao web app manifest em outras palavras iOS não utiliza o manifest.json, a definição de ícones e títulos para nossa PWA é feito através de meta tags dentro de nosso HTML, e elas apenas suportam o formato standalone.

Caso utilize um formato diferente de standalone precisamos ter duas media queries uma para android e outra para iOS

//media query para MacOS e iiOS
@media (display-mode: standalone) {
 
}

//media query para Android caso definido no manifest.json
@media (display-mode: fullscreen) {
 
}

Media query para iOS

Mas o código acima pode perder sua funcionalidade quando iOS começar a suportar web app manifest, mas caso queiramos ter uma estilização estritamente para iOS, por simples motivo cada sistema operacional tem seu guia de interface, por exemplo, Android geralmente exibe seus items de navegação na base da tela enquanto iOS exibe os elementos de navegação no topo da aplicação.

Com media query podemos incluir mais uma regra dentro de nosso media query o “@supports” com ele podemos realizar filtros através de features suportada pelo browser, por exemplo, suporte a display grid podemos utilizar o seguinte código:

@supports (display: grid) {
  div {
    display: grid;
  }
}

Agora que sabemos desse recurso como podemos implementar essa regra com PWA? Temos algumas features apenas suportada pelo iOS no caso temos “-webkit-overflow-scrolling” presente apenas no safari mobile. Com isso podemos da seguinte forma:

/* 
 - podemos utilizar display mode em: standalone, fullscreen, minimal-ui e browser
*/

@media (display-mode: standalone) {
  /*To das as pwas instaladas desktop e mobile */
}

@media (max-width: 576px) and (display-mode: standalone) {
  /* Filtro para pwas instalada apenas em dispositivos moveis */
  
  @supports (-webkit-overflow-scrolling: touch) {
    /* PWAs instaladas apenas no iOS */
  }
  
  @supports not (-webkit-overflow-scrolling: touch) {
    /* PWAs instaladas em dispositivos não iOS */
  }
}

Com o código acima cobrimos os principais casos para PWA, em breve estarei postando um vídeo sobre esse assunto para seguir o conteúdo dessa série confira os post sobre Progressive web apps. Caso tenha alguma dúvida só deixar um comentário.

Categorias
WordPress

Criando seu próprio bloco Gutenberg: Parte 2

No post anterior vimos como trabalhar com blocos customizados, conceitos básicos de como registrar nosso bloco como um plugin e criar um markup básico com JavaScript. Nesse post vamos evoluir o nosso bloco todo código está disponível no Github. Primeiro passo vamos ver como os blocos trabalham com estilo externo.

Carregando estilo externo

Primeiro passo será criar nossos arquivos CSS, um arquivo para o front-end de nossa aplicação e o segundo para nosso editor.

style.css(front-end e editor):

.wp-block-fellyph-tutorial-02 {
  background: linear-gradient(45deg, #12c2e9, #c471ed, #f64f59);
  padding: 2em;
  color: white;
  font-size: 20px;
  box-sizing: border-box;
  transform: rotate3d(0, 0, 0, 0);
  transition: all 200ms ease-out;
  box-shadow: 3px 3px 3px #666;
  text-align: center;
}

.wp-block-fellyph-tutorial-02:hover {
  transform: rotate3d(1, 1, 1, -4deg);
}

editor-style.css(somente editor)

.wp-block-fellyph-tutorial-02 {
  background: #12c2e9;
  border: 3px solid #666;
  box-shadow: 0 0 0 #666;
}

.wp-block-fellyph-tutorial-02:hover {
  transform: rotate3d(0, 0, 0, 0);
}

Temos acima os dois códigos que iremos carregar em nossa aplicação, dois pontos importantes, um o nome da classe ela sempre irá iniciar com ‘wp-block-‘ mais o nome que registramos o nosso bloco, nesse caso o ‘fellyph/tutorial-02‘ irá se transformar em fellyph-tutorial-02, assim temos a classe wp-block-fellyph-tutorial-02.

Segundo ponto, quando trabalhamos com a função register_block_type ela espera 4 parâmetros:

  • style
  • script
  • editor_style
  • editor_script

As propriedades script e style representam os arquivos que serão carregados no editor e front-end da aplicação, enquanto editor_style e editor_script serão carregados apenas no editor do WordPress. Nesse caso o estilo no arquivo editor-style irá sobrescrever o estilo do arquivo style.css. Para ficar mais claro vamos ver o print do resultado desses dois estilos:

Estilo aplicado em nosso front-end
Estilo aplicado em nosso editor

Temos um spoiler de como ficará esse nosso bloco, mas como podemos observar, o bloco no editor herda todas as propriedades do bloco do front-end, por exemplo, alinhamento, padding e a cor da fonte, essas características foram de no arquivo style.css.

index.php

Agora vamos dar uma olhada em nosso arquivo principal:

<?php

/**
 * Plugin Name: Gutenberg tutorial
 * Plugin URI: https://github.com/fellyph/Gutenberg-tutorials
 * Description: Este tutorial ensina como criar um bloco gutenberg https://blog.fellyph.com.br/wordpress-2/criando-seu-proprio-bloco-gutenberg/.
 * Version: 1.2
 * Author: fellyph
 */

defined( 'ABSPATH' ) || exit;

/**
 * 1 - Criando nosso primeiro bloco: Parte 02
 *    - Carregando arquivo externo para nosso estilo
 */
function meu_primeiro_bloco_gutenberg_parte_02 () {

	if ( ! function_exists( 'register_block_type' ) ) {
		// Checamos se temos suporte a função register_block_type antes de tudo.
		return;
	}

	wp_register_script(
		'tutorial-02',
		plugins_url( 'script.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'script.js' )
	);

	wp_register_style(
		'style-editor',
		plugins_url( 'editor-style.css', __FILE__ ),
		array('wp-edit-blocks'),
		filemtime( plugin_dir_path( __FILE__ ) . 'editor-style.css' )
	);

	wp_register_style(
		'style-frontend',
		plugins_url( 'style.css', __FILE__ ),
		filemtime( plugin_dir_path( __FILE__ ) . 'style.css' )
	);

	register_block_type( 'fellyph/tutorial-02', array(
		'style' => 'style-frontend',
		'editor_script' => 'tutorial-02',
		'editor_style' => 'style-editor'
	) );
}

add_action( 'init', 'meu_primeiro_bloco_gutenberg_parte_02' );

Não vamos abordar todas as partes desse código caso não tenha visto a primeira parte dessa séria aqui está o link: https://blog.fellyph.com.br/wordpress-2/criando-seu-proprio-bloco-gutenberg.

Comparado ao tutorial anterior, acrescentamos duas funções wp_register_style, uma para estilo geral e outra para o estilo do nosso editor e por fim passamos como parâmetro na função register_block_type.

script.js

Em nosso script agora temos o seguinte código:

( function( blocks, element ) {
	var el = element.createElement;


	blocks.registerBlockType( 'fellyph/tutorial-02', {
		title: 'Primeiro bloco Gutenberg: Parte 02',
		icon: 'smiley',
    category: 'layout',
  
		edit: function(props) {
			return el(
				'p',
				{ className: props.className },
				'Este conteúdo será exibido no editor.'
			);
		},
		save: function(props) {
			return el(
				'p',
				{ className: props.className },
				'Este conteúdo será exibido para o usuário final.'
			);
		},
	} );
}(
	window.wp.blocks,
	window.wp.element
) );

Comparado a versão anterior passamos nas funções dentro das propriedades edit e save uma propriedade chamada props, ela contém informações referente ao nosso bloco e uma delas é o nome dessa classe, não obrigatório passar o className dinamicamente poderíamos passar um valor estático, mas para facilitar a manutenção e evitar conflitos com outros blocos utilizamos o nome de classe dinamicamente.

Uma observação sobre a propriedade “icon” caso queira saber os ícones disponíveis dentro do WordPress, só acessar o link: https://developer.wordpress.org/resource/dashicons/#smiley lá você irá encontrar uma série de ícones separados por categoria.

Na página de dashicons você pode conferir o id para cada ícone

No meu caso eu selecionei o ícone smiley, como podemos ver na imagem abaixo, quando clico no ícone temos seu id e como utiliza-lo em nosso CSS ou HTML, mas eu só irei precisar do id ‘dashincons-smiley’ em nosso script usamos ‘dashicons-‘ apenas smiley.

A nível de curiosidade eu alterei a função edit para ver quais são os valores passados dentro de props(você não precisa implementar isso).

edit: function(props) {
	var test = 'Este conteúdo será exibido no editor. / ';
	for(var key in props) {
		test += key + " : " + props[key] + ' / ';
	}

	return el(
		'p',
		{ className: props.className },
		test
		);
	},

O resultado foi o seguinte:

Dentro do objeto props temos as seguintes propriedades e funções:

  • name
  • isSelected
  • attributes
  • setAttributes (function)
  • insertBlocksAfter (function)
  • onReplace (function)
  • mergeBlocks (function)
  • clientId
  • isSelectionEnabled
  • toggleSelection (function)
  • className

Para a estilização de nosso bloco poderíamos trabalhar com atributos dinâmicos, mas esse será assunto para um próximo post. Caso queira saber mais sobre como trabalhar com estilo em blocos customizados só deixar um comentário.

O código desse tutorial você irá encontrar na branch(tutorais/gutenberg-02) do repositório: https://github.com/fellyph/Gutenberg-tutorials/tree/tutoriais/gutenberg-02

Categorias
Mobile Tutoriais Web

Realizando consultas na base de dados do Parse

Essa semana eu iniciei uma série de posts sobre o Parse, serviço de cloud do Facebook. Neste segundo post vou abordar como realizar consultas em nossa base. Caso não conheça o Parse veja o post de introdução: https://www.fellyph.com.br/blog/tutoriais/parse/.

No post anterior abordamos um pouco sobre consultas, primeiro resgatamos todos os valores de uma base com a função “find”, em seguida resgatamos um único item com a função “get”. Criei um cadastro de carros com as propriedades: modelo, ano e valor para cadastrar os dados utilizei os mesmo padrão do post anterior:

INDEX.HTML




    
        
        
        
        Todo List App Parse
        
    
    
        

CAR LIST

Carregando...




APP.JS

Parse.initialize("YOUR_APP_ID", "YOUR_JS_KEY");

 var app = {
	 		Cars:null,
        	listOfCars:null,
        	inputModel:null,
			inputValue:null,
	 		inputYear:null,
	 
	 		//função para inicializar nosso app
        	initApp: function(){
        		app.Cars = Parse.Object.extend("Cars");
        		
				//showAllCars busca tarefas já cadastradas em nosso app
        		app.showAllCars();
        		
				//Adicionamos um lister para o nosso form
        		document.getElementById("form-car").addEventListener("submit",app.onSubmit);
        		
        		//Fazemos um cache dos itens que acessamos mais de uma vez em nosso app
        		app.listOfCars 	= document.getElementById("list-cars");
        		app.inputModel 	= document.getElementById("model-car");
				app.inputYear	= document.getElementById("year-car");
				app.inputValue	= document.getElementById("value-car")
        	},
	 
	 	//função responsável por consultar os dados na nuvem
        	showAllCars: function(){
				//chamamos a função Query do Parse parar varer a nossa base
        		var query = new Parse.Query("Cars");
        		
        		//a função trata a query para sucesso ou erro de nossa consulta
        		query.find({
					success:function(results){
						//esse bloco será executado se ocorrer tudo bem com nossa query
						app.updateOutputList(results)
					},
					error:function(error){
						//tratamento para caso de erro
						console.log("error",error)
					}
        		})
        	},
	 
	 		updateOutputList: function(results){
 				var markupList = "";
				if(results.length ModeloAnoValor";
						
						//os resultados vem em um array de objetos
						//varremos o nosso array e montamos um markup
						for(var id in results){
							markupList += "";
							markupList += ""+ results[id].attributes.model +"";
							markupList += ""+ results[id].attributes.year +"";
							markupList += ""+ results[id].attributes.value +"";
							markupList +=
							"";
						}
				}else{
					markupList = "Nenhum Resultado Encontrado";
				}
				app.listOfCars.innerHTML = markupList;
 			},

			//função para fazer o tratamento quando o usuário envia os dados do form
        onSubmit: function(e){
		var car = {};
				
		car.model = app.inputModel.value;
		//utilizamos parseInt para converter o nosso value que é uma string e inteiro
                car.year = parseInt(app.inputYear.value);
		car.value = parseInt(app.inputValue.value);
		//passamos o nosso objeto para ser salvo na cloud
		app.saveCar(car);
		// utiliza o preventDefault para evitar do form realizar o reload da página
		e.preventDefault();
	},

        	//função específica para salvar as cars na nuvem
        	saveCar: function (car){
        	 	var carCloud = new app.Cars();
        	 	carCloud.save(car).then(function(object) {
                	console.log("Carro salvo com sucesso!");
					//após a informação salva limpamos os dados dos inputs e atualizamos a lista
                	app.showAllCars();
                	app.inputModel.value =
					app.inputYear.value =
					app.inputValue.value = "";
            	});
        	}
 }
 			
window.addEventListener("load", app.initApp)

Cadastrei algumas informações na minha base para realizar a consulta:

Agora vamos dar uma olhada em algumas funções que podemos trabalhar com a classe Query. A primeira delas será a função equalTo:

equalTo

        searchByModel: function (modelCar) {
		/*
			Meu App Já instaciou a Class Cars
			caso contrario teria que instancia-la:
			var Cars = Parse.Object.extend("Cars");
		*/
		var query = new Parse.Query(app.Cars);

		query.equalTo("model", modelCar);
		query.find({
			success: function (results) {
				app.updateOutputList(results)
			},
			error: function (error) {
				alert("Error: " + error.code + " " + error.message);
			}
		});
	}

Na função equealTo passamos dois parâmetros, o primeiro o nome da propriedade e a segunda o valor que você deseja comparar, detalhe sobre a função que sua consulta é case sensitive.

notEqualTo

query.notEqualTo("modelo", "Uno");

A função notEqualTo é o inverso da função equalsTo ela retorna todos os valores diferentes do valor repassado. Também é possível realizar queries compostas:

query.notEqualTo("model",  "Uno");
query.equalTo("year",2000);

Na consulta acima o modelo do carro tem que ser diferente de "Uno" E o ano tem que ser igual a 2000, pensando em SQL seria um operador AND as duas condições tem que ser verdadeiras.

greaterThan & lessThan

Com essa consulta podemos receber valores maiores do que o valor informado para a função, por exemplo caso queira receber os carros com o ano superior a 2000 utilizamos a seguinte função:
query.greaterThan("year",2000);

O função oposta seria lessThan ela retorna apenas os valores menores:

query.lessThan("year",2000);

limit

Por padrão as consultas tem um limite 100 resultados por query, podemos modificar esse resultado de 1 até 1000. Para aplicar esse limite utilizamos a seguinte função:

query.limit(10);

first

Em nossa query podemos resgatar apenas o primeiro resultado isso é possível com a função first:

query.greaterThan("year",2000);
query.first({
  success: function(object) {
    // caso sucesso retorna o primeiro resultado
  },
  error: function(error) {
    alert("Error: " + error.code + " " + error.message);
  }
});

skip

Podemos definir o limite de resultados e também podemos definir o índice inicial do resultado com a função skip, com essas duas funções podemos criar um esquema de paginação.

query.skip(10);

ascending & descending

Além de criar filtros, busca por valores, limitar a busca... criar paginação... Pensa que acabou? Não ainda podemos ordenar nossa consulta(por ascendente ou descendente):

//Instanciamos a query sobre "Cars"
var query = new Parse.Query(app.Cars);

//resgatamos os valores maiores que 2000
query.greaterThan("year",2000);
//definimos a ordenação
query.ascending("year");
		
query.find({
	success: function (results) {
		app.updateOutputList(results)
	},
	error: function (error) {
		alert("Error: " + error.code + " " + error.message);
	}
}); 

O resultado será o seguinte:

Outra opção com listagem de valores maiores para menores:

query.descending("year");

containedIn & notContainedIn

Caso queiramos realizar a consulta com mais de um modelo de carro, por exemplo, podemos utilizar a função containedIn ela espera um Array como parâmetro:

query.containedIn("model",["Uno", "Civic", "Gol"]);

O inverso da função containedIn seguindo o padrão é a função notContainedIn, ela retorna o resultado das palavras diferente das que constam no Array:

query.notContainedIn("model",["Uno", "Civic", "Gol"]);

exists & doesNotExist

O Parse permite criar objetos com propriedades vazias ou modificar os objetos depois de criados adicionando novos atributos, assim os objetos antigos ficam sem a informação gravada, para isso podemos utilizar a função exists para verificar se o objeto possui informação gravada na propriedade requisitada.

Vamos a um exemplo:

query.exists("year");

No código acima verificamos os itens que tem a propriedade year cadastrada, o inverso seria:

query.doesNotexists("year");

matchesKeyInQuery & doesNotMatchKeyInQuery

A matchKeyInQuery serve para comparar dois objetos de classes diferentes que possuem duas propriedades iguais, por exemplo:

var Team = Parse.Object.extend("Team");
var teamQuery = new Parse.Query(Team);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesKeyInQuery("hometown", "city", teamQuery);
userQuery.find({ 
  success: function(results) {
    // results has the list of users with a hometown team with a winning record
  }
});

No código acima temos duas class Team e Users esta classe deriva do Parse Porque é uma classe especial, vamos falar dela em um próximo post. Mas voltando a query, comparamos duas propriedades "hometown" e "city" presentes na classe User e Team quando eles forem idênticos apareceram em nossa consulta.

select

Caso queira consultar propriedades específicas com a função select passamos as keys das propriedades que queremos:

query.greaterThan("year",2000);
query.select("model", "year");

No caso da imagem acima utilizei a mesma função de exibir os resultados, por conta de utilizar o select a coluna valor veio "undefined" porque eu só escolhi duas propriedades "model" e "year".

startsWith

Na função equalsTo ele retorna valores estritamente idênticos, com a função podemos resgatar valores que iniciam com a String repassada, ela tem um funcionamento parecido com o operador LIKE do MySQL. Esta consulta também é case sensitive.

query.startsWith("model","F")

count

A função count conta "Dan", isso mesmo ela serve para contar o número de itens do resultado ela funciona da seguinte forma:

query.startsWith("model","F")
query.count({
		  success: function(count) {
			// The count request succeeded. Show the count
			alert("Carros que começam com F: " + count);
		  },
		  error: function(error) {
			// The request failed
		  }
		});

No exemplo anterior eu realizei um filtro sobre a query e a função count tem o tratamento similar a função find, falando nela podemos aplicar as duas em conjunto sem problemas. Temos outras queries aplicadas em objetos relacionais mas isso vamos abordar em um futuro. Até o próximo post Pessoal.

Categorias
Mobile Tutoriais Web

Crie aplicações na nuvem com Parse

No ano passado tive oportunidade de conferir o Parse Developer Day em San Francisco, foi o primeiro e um dos melhores eventos que conferir lá na capital da tecnologia. O Parse para que não conhecer é um serviço de cloud para aplicações e games mobile. Ele tem suporte a IOS , Android, Windows Phone, Unity e JavaScript.

Dentre os serviços que o Parse oferece, temos:

  • Cloud Storage
  • Push Notification
  • Social Connection
  • Hosting
  • Analytics
  • Cloud Code

Neste tutorial vou dar uma introdução sobre o serviço de Cloud Storage com JavaScript esse recurso possibilita salvarmos informações em um server sem precisar de uma linguagem Back-end, também podemos utilizar o JavaScript SDK junto com Phonegap e exporta-ló para diversas plataformas. O JavaScript SDK é baseado no framework BackBone.js: Isso possibilita total compatibilidade com aplicações que usar tal framework. O JavaScript SDK não possui dependência por outras libs, você pode trabalhar com Parser + JS e mais nada, mas claro que você pode utilizar outras libs. Durante o tutorial eu vou falando sobre mais recursos. Agora vamos aos passos iniciais.

Primeiro passo é realizar o cadastro no site do Parse: https://parse.com/. Não se preocupe com questões de preço eles fornecem um pacote free bem “parrudo”. O plano free tem 1 milhão de requisições/mês e 1 milhão de Pushes Notifications/mês. Se sua aplicação realiza mais de um 1 milhão de requisições mês já está na hora de ganhar algo com seu app ou repensar no esquema de requisições que ele realiza.

Você pode criar uma conta passando os dados convencionais ou vincular sua conta a uma conta do Facebook/GitHub . Com sua conta devidamente criada ele vai liberar para você um painel para você administrar suas aplicações.

Crie sua aplicação clicando no botão “+ Create New App”, só adicionar o nome do seu app:

E você já tem uma aplicação na nuvem!

Agora vamos fazer um pequeno exemplo para ler e gravar informações na nuvem. Precisamos realizar o download do JavaScript SDK:

https://parse.com/downloads/javascript/parse-js-blank/latest

Ou utilizar o link da CDN :

  

Com o Parse devidamente importado temos vamos ao código de nossa aplicação:



    
        
        
        
        Hello World Parse
    
    
        

App com Parse

No código acima temos um markup simples em HTML, no final de nosso código temos a parte que interessa que é o nosso código JavaScript. após chamar o Parse via CDN no segundo bloco de código JavaScript inicializamos o Parse com a função “Parse.initialize(“APPLICATION_ID”,”JAVASCRIPT_KEY”)” esse dois parâmetros você deve substituir pelo application ID e o JavaScript Key de sua aplicação, você vai descobrir essa informação no settings de sua aplicação, na imagem abaixo temos marcados o application id e o JavaScript Key.

Copie as duas infos e substitua em sua inicialização. Na linha seguinte criamos um objeto da classe “MyObject” ela estende do Parse.Object, ele é responsável por toda regra de armazenamento de dados. Quando chamamos a função extend o Parse irá verificar em sua base se existe a classe MyObject, caso não exista ele irá criar uma nova base de dados com a classe. Também não é necessário definir o esquema padrão para armazenamento previamente, as propriedades do elemento podem mudar, não é uma ação recomendável, mas você pode adicionar uma propriedade nova em sua classe sem precisar criar uma nova classe.

Nesse ponto eles simplificaram ao máximo, você chega a não acreditar como isso funciona. Na linha 18 ele cria uma nova instancia(test) da Classe MyObject e por fim na linha 20 ele salva essa instancia passando um objeto com as informações que serão salvas nesse momento temos um códido “like jQuery” temos mais uma função atrelada a then ela será disparada caso os dados seja gravados com sucesso, essa função também recebe um objeto no callback se inspecionarmos esse objeto teremos os seguintes valores:

Como podemos ver na imagem acima recebemos um objeto com uma série de informações que foram gravadas em sua aplicação, algumas informações úteis como o ID do objeto esse atributo é um identificador único do objeto na aplicação, também podemos verificar os atributos do objeto salvo.

Se testarmos nossa aplicação e ela retornar um alert significa que as informações foram salvas. Para verificar isso basta acessar o painel database no dashboard de sua aplicação como na imagem a seguir:

Como podemos ver a informação está salva na nuvem, agora vamos para algo mais prático no próximo exemplo eu vou montar um cadastro de TODO-LIST. O código vai ficar um pouco extenso por isso vou separar o código em dois(HTML/CSS), primeiro vamos ver o nosso HTML:



    
        
        
        
        Hello World Parse
        
    
    
        

App com Parse - TODO LIST

    Carregando...


Em seguida o nosso JavaScript, comentei algumas linhas mas também vou comentar a seguir.

  		Parse.initialize("APPLICATION_ID","JAVASCRIPT_KEY")
        	var Tasks;
        	var listOfTasks;
        	var inputTask;
        	
        	//função para inicializar nosso app
        	function initApp(){
        		Tasks = Parse.Object.extend("Tasks");
        		//showTasks busca tarefas já cadastradas em nosso app
        		showTasks();
        		//Adicionamos um lister para o nosso form
        		document.getElementById("form-task").addEventListener("submit",onSubmit);
        		
        		//Fazemos um cache dos itens que acessamos mais de uma vez em nosso app
        		listOfTasks = document.getElementById("list-tasks");
        		inputTask = document.getElementById("tarefa")
        	}
        	
        	//função resposável por consultar os dados na nuvem
        	function showTasks(){
        		//chamamos a função Query do Parse parar varer a nossa base
        		var query = new Parse.Query("Tasks");
        		
        		//a função trata a query para sucesso ou erro de nossa consulta
        		query.find({
        		success:function(results){
        			//esse bloco será executado se ocorrer tudo bem com nossa query
        			var markupList = "";
        			//os resultados vem em um array de objetos
        			//varremos o nosso array e montamos um markup
        			for(var id in results){
        				console.log("success",results[id].attributes.descricao);
        				markupList += "
  • "+ results[id].attributes.descricao +"
  • " } listOfTasks.innerHTML = markupList; }, error:function(error){ //tratamento para caso de erro console.log("error",error) } }) console.log(query); } //função para fazer o trantamento quando o usuário envia os dados do form function onSubmit(e){ var task = {}; //pegamos o valor cadastrado em nosso input task.descricao = inputTask.value; task.done = "false"; //passamos o nosso objeto para ser salvo na cloud saveTask(task); // utiliza o preventDefault para evitar do form realizar o reload da página e.preventDefault(); } //função específica para salvar as tasks na nuvem function saveTask(task){ var taskCloud = new Tasks(); taskCloud.save(task).then(function(object) { alert("Task salva com sucesso!"); showTasks(); inputTask.value = ""; }); } window.addEventListener("load", initApp)

    Nas primeiras linhas inicializamos o Parse, em seguida criamos algumas variáveis para armazenar alguns itens importantes em nosso app. A função initApp será disparada por um listener para quando nossa página for totalmente carregada. Continuando na função initApp inicializamos nossa Class Tasks. Na linha 11 chamamos a função showTasks ela será responsável por exibir nossas tasks. Nas últimas 3 linhas adicionamos um listener para nosso form, guardamos o container das tasks a ul com id list-tasks e o input que iremos digitar nossas tasks.

    Na Função “showTasks” temos um item novo, para realizamos uma consulta em nossa base utilizamos a Classe Parse.Query(“Tasks”), armazenamos ela na variável “query” com a função find pedimos para resgatar a lista de valores cadastrados, também podemos atribuir callbacks para serem chamados em caso de sucesso ou erro. Em caso de sucesso recebemos um array de objetos, tratamos o array e o exibimos em nosso ul.

    A função “onSubmit” é disparada quando o usuário tenta salvar alguma informação, utilizei o submit para utilizar o recurso de required de nosso input, nesta função pegamos o dado cadastrado no input e passamos para função saveTask.

    A “saveTask” será responsável por salvar os dados na nuvem, não comentarei essa função porque já abordamos o assunto anteriormente sobre salvar objetos na nuvem.

    Até o momento nosso app só salva os itens agora vamos implementar um função de editar os itens, mas antes vamos adicionar um CSS para sinalizar nossos itens editados. vamos adicionar o seguinte CSS:

    .list-tasks{
    	list-style:none;
    	margin:0;
    	padding:0;
    }
    
    .item-task{
    	padding: 10px;
    	border-bottom: 1px solid #ccc;
    }
    
    .item-task[data-done="true"]{
    	text-decoration:line-through;
    }
    

    Detalhe para o código acima a regra aplicada para quando o elemento com class .item-task tiver sua propriedade setada para true ele receberá uma linha sobre o texto.

    Em nossa função initApp adicionamos o evento de click para lista com:

    listOfTasks.addEventListener("click",clickList);
    

    Adicionamos o tratamento no click:

    function clickList(e) {
    				if(e.target.localName == "li"){
    					e.target.dataset.done = (e.target.dataset.done === 'true')? false : true;
    					editTask(e.target.dataset.id,e.target.dataset.done);
    				}
    			}
    

    Atribui o click no “ul” pelo fato de não implementar o click em cada filho, observo o item pai e valido se o meu target foi o “li” e pego as informações contidas no elemento e passa para a função editTask o id do elemento e o novo valor da variável done. Agora vamos ao código da função editTask:

    function editTask(id,done){
    				var query = new Parse.Query(Tasks);
    				query.get(id, {
    				  success: function(task) {
    					 task.set("done", done);
    					 task.save()
    				  },
    				  error: function(object, error) {
    					console.log("erro ao salvar o objeto", object, error)
    				  }
    				});
    			}
    

    Na função editTask instanciamos uma nova query e realizamos uma busca pelo id da task com a função get, caso a função ache o item, executamos a função success modificamos a propriedade com a função set e salvamos novamente a nossa task. Esse esquema não possui nenhum esquema de tratamento para controle de requisições e em todo o clique iremos executar um nova requisição.

    Para finalizar vamos adicionar uma função para excluir a task, na função showTasks, em cada “li” vou adicionar um button com o id de cada item vou adicionar somente a linha modificada:

    markupList += "
  • "+ results[id].attributes.descricao +"
  • ";

    Agora vou adicionar na função clickList uma condicional para quando o usuário clicar no button:

     
    function clickList(e){
    	if(e.target.localName == "li"){
    		e.target.dataset.done = (e.target.dataset.done === 'true')? false : true;
    		editTask(e.target.dataset.id,e.target.dataset.done);
    	}else if(e.target.localName == "button"){
    		e.target.disabled = "disabled";
    		e.target.innerHTML = "Removendo..."
    		removeTask(e.target.dataset.id);
    	}
    }
    

    No código acima quando o usuário clicar no button eu passo o id da task para função removeTask que possui o seguinte código:

    function removeTask(id){
    	var query = new Parse.Query(app.Tasks);
    	query.get(id, {
    		success: function(task) {
    			task.destroy({
    				success:function(task){
    					app.showTasks();
    				}
    			})
    		}
    	});
    }
    

    Na função removeTask realizamos uma Query para resgatar o objeto pelo id, quando recebermos a task chamamos a função destroy como nome sugere ela é responsável por remover o dado.

    O objetivo desse tutorial era mostrar as funções básicas do Parse e entender o seu funcionamento, os dados estão abertos para todos os usuários, ou seja todo mundo que usasse esse app compartilharia a mesma lista de task. Nos próximos posts pretendo abordar questões de usuários, vincular os dados por usuário e criar seções. Coloque o código completo no gitHub para baixar o código basta acessar o link:

    https://github.com/fellyph/Tutorial-bbUI/tree/master/parse-tutorial/www

    Referência:
    https://parse.com/docs/js_guide#javascript_guide

    Segundo post da série:

    https://www.fellyph.com.br/blog/tutoriais/realizando-consultas-na-base-de-dados-parse/

    Categorias
    HTML5 JavaScript Tutoriais

    Observe a orientação do device com JavaScript

    Com Javascript podemos observar a orientação do device, também é possível realizar este tratamento com CSS, mas isso fica restrito a questões visuais, caso queria realizar algum tratamento lógico em nossa aplicação podemos utilizar JavaScript.

    Temos “N” possibilidades de acompanhar a orientação do device, nesse post vou mostrar duas maneiras bem simples, a primeira observando o evento ‘orientationchange’, para isso adicionamos um listener em em nossa janela com o seguinte código:

    window.addEventListener('orientationchange', function(){ // faça alguma coisa });
    

    O código acima acompanha quando a orientação do device muda, mas como sabemos que o usuário está em landscape ou portrait, para isso pegamos a orientação do device, com window.orientation essa propriedade retorna a rotação do device:

    window.addEventListener('orientationchange', function(){ switch(window.orientation) { case -90: case 90: alert('landscape'); break; default: alert('portrait'); break; } });
    

    Com as informações sobre a rotação do device sabemos que 90 ou -90 o device está em landscape e diferente disso portrait. No código acima foi disparado somente um alert, mas poderíamos chamar alguma outra função, editar uma classe de um elemento ou adicionar uma conteúdo específico para o usuário.

    A segunda opção seria com matchMedia ela faz parte da Web API Interface com ela podemos validar media queries por exemplo:

    if (window.matchMedia("(min-width: 360px)").matches) { /* O view port tem pelo menos 360px de largura */ } else { /* o view port tem menos de 360px de largura */ }
    

    Podemos validar qualquer media query no CSS3 temos uma regra para verificar a orientação vamos utilizar ela para pegar o orientação do nosso device:

    function handleOrientationChange(mediaQuerie) { if (mediaQuerie.matches) { alert("portrait") } else { alert("landscape") } } /* Criamos uma variável para armazenar a media querie */ var mediaQuerie = window.matchMedia("(orientation: portrait)"); /* Adicionamos um listenr para acompanhar a mudança de orientação */ mediaQuerie.addListener(handleOrientationChange);
    

    Temos ai duas opções para verificar a orientação do device.