Sei sulla pagina 1di 52

CSS HTML NODEJS JAVASCRIPT PHP DESIGN MAIS CATEGORIAS

CRIANDO UMA APLICAÇÃO MÓVEL COM IONIC 2 E ANGULAR 2


EM DEZ PASSOS

por Carlos Cabral 04/11/2016 32 comentários

Introdução
À esta altura do campeonato é provável que você já tenha ouvido falar da nova versão deste famoso
framework para criação de aplicações móveis híbridas. O Ionic 2 acaba de chegar em seu Release Candidate
e, com ele, trás uma série de recursos e otimizações de código, além de um considerável ganho de
performance! Muito desse mérito se deve ao Angular (como é chamada a nova versão do framework, que
deixa para trás o “JS” ao nal do nome) que chega – nalmente – na sua versão estável, provando que não
está para brincadeiras.

Depois de passar por várias mudanças e quebras de código à cada novo release, o Ionic 2 agora atinge a
maturidade e se torna um competidor ainda mais forte do modelo de desenvolvimento tradicional (nativo). No
entanto, se você já está familiarizado com o Ionic 1, a mudança nos conceitos pode lhe soar um tanto quanto
desagradáveis à primeira vista. Mas uma vez que você entende como as peças se encaixam, vai perceber que Feedback
criar aplicações móveis com o framework tornou-se uma atividade ainda mais simples e recompensadora.





O que tem de novo?

O Ionic foi desenvolvido com base no AngularJS, um framework voltado para a criação de aplicações web
modernas, construídas com base em uma página HTML5 que atualiza seu conteúdo de maneira dinâmica
(as famosas Single Page Applications ou SPAs). Ao tirar proveito dessa arquitetura – e adicionar uma série de
estilos que emulam o visual de aplicações nativas – o Ionic facilitou, em muito, a tarefa de construir um app
híbrido, ou seja, aquele que executa tanto em smartphones iOS quando Android, otimizando o seu Tempo de
Mercado.
Mesmo ainda sendo executado em uma WebView (browser interno dos smartphones), uma aplicação
baseada no Ionic 2 é muito mais rápida, modular e escalável, se comparada com a primeira versão.
Principalmente porque o framework segue os padrões web mais recentes, como a nova especi cação ES6
(ou ES2015), trazendo para o javaScript conceitos como classes, módulos e arrow functions. Além disso,
temos também a presença do polêmico TypeScript (opcional), que trás o poder da tipagem para o seu
código, com o intuito de minimizar erros, simpli car a injeção de dependências, facilitar testes, e etc.

Mas embora tudo isso pareça um verdadeiro balaio de gato que funciona mais como repelente do que
atrativo, não se deixe enganar: A versão 2 do Ionic dá um considerável salto de inovação em relação à sua
versão original e abre caminho para novas e interessantes tendências que valem a pena serem exploradas! Feedback

Mão na massa! 


Para entender melhor como se constrói uma aplicação com o Ionic 2, vamos criar uma do zero

A aplicação que iremos construir é um simples leitor de feeds baseado na API do Reddit, o poderoso canal
agregador de notícias, onde membros da comunidade podem submeter conteúdos como links, textos,
imagens, etc. O app será 100% funcional e poderá ser instalado no seu smartphone e, quem sabe, até mesmo
evoluir com a inclusão de novas funcionalidades.
Instalando o framework

Se você já tem o Ionic 1 instalado na sua máquina, basta digitar o seguinte comando no terminal:

npm install -g ionic

Esse comando atualiza o framework para trabalhar com o Ionic 2 sem afetar a instalação da versão 1.

Mas caso você seja marinheiro de primeira viagem, certi que-se que tenha o Node.js instalado na sua
máquina e, em seguida, digite no terminal:

''
npm install -g ionic cordova

Lembre-se de que você também deve ter o SDK do Android


e o Java instalados para fazer build para Android e/ou o
Xcode para o build no iPhone:

Guia de instalação para Mac

Guia de instalação para Windows

Depois que a instalação for concluída, você pode veri car a versão do framework no terminal, digitando:

ionic -v
Feedback





Criando um novo projeto

O CLI (Command Line Interface) do Ionic vem com um monte de comandos úteis que nos ajudam na criação
e na manutenção dos projetos. Para conferir a lista de comandos disponíveis, digite:

ionic help

Por enquanto o que nos interessa é o comando start. Digite o seguinte no terminal:

ionic start MyReader blank --v2 --appname "Best Reader Ever" --id "com.tableless.myreader"
O comando start oferece três tipos de templates com código boilerplate. São eles:

sidemenu – adiciona um menu lateral à aplicação (estilo de navegação padrão no


Android);

tabs – cria uma navegação baseada em guias (modelo de organização de conteúdo


incentivado pelo iOS);

blank – cria um projeto com boilerplate básico, sem nenhum template especí co.

O comando que digitamos no terminal vai utilizar o template blank. Também passamos mais três parâmetros
adicionais: v2 que informa que queremos trabalhar com a versão 2 do Ionic, appname, que de ne um nome
de projeto menos formal e id, que nos possibilita de nir o package da aplicação.

Vamos agora acessar a pasta do nosso projeto, digitando:

cd MyReader

Passo 1 – Conhecendo a arquitetura

Depois de tantas con gurações e explicações iremos, en m, para a parte divertida do processo!

Se você visitar a pasta do projeto dentro de src/pages/, vai notar a presença de uma outra pasta chamada
home. Dentro dela há três arquivos:

home.html;

home.scss;

home.ts. Feedback

Essas pastas e arquivos foram criados como resultado do comando start. O Ionic é baseado no Angular que,




por sua vez, considera que os principais componentes de uma aplicação devem ter escopos isolados.
Portanto, cada “página” de um projeto tem seu próprio template visual (html), estilo (scss) e classe (ts).
Perceba também que, por padrão, o Ionic utiliza Sass para a escrita de CSS e TypeScript para as classes, ao
invés de JavaScript puro. Fique à vontade para vasculhar as pastas do projeto e entender como as
informações são organizadas, uma vez que este tutorial não tem o propósito de explorar isso com detalhes.

Antes de modi car algo no projeto, vamos veri car o que já foi gerado de graça. Digite no terminal:

ionic serve
Este comando inicia um servidor local na nossa máquina e abre uma nova aba no browser com a aplicação
no ar. Como o LiveReload já vem habilitado por padrão, modi cações que zermos no código serão re etidas
automaticamente no browser:

Vamos ver isso acontecendo em tempo real. Abra o arquivo home.html e remova o código desnecessário até
que ele que assim:

<ion-header>
<ion-navbar>
<ion-title>My Feed Reader</ion-title>
</ion-navbar>
</ion-header>

<ion-content>

</ion-content>

Con ra a mudança ocorrendo automaticamente no browser:


Feedback




Agora vamos dar uma olhada no componente responsável por controlar nosso template. Abra o arquivo
home.ts:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController) {}
}

Perceba que o arquivo é composto por três blocos distintos, que eu chamo carinhosamente de os 3D:
Declaration, Decorator e De nition. A primeira parte é onde declaramos componentes externos ou bibliotecas
que iremos utilizar em nosso projeto:

import { Component } from '@angular/core';


import { NavController } from 'ionic-angular';

O segundo bloco é composto por um Decorator. No Angular, todo componente tem um “decorador”, que é
responsável por fornecer metadados ou informações sobre a classe. No nosso caso, o decorador está
dizendo que as modi cações no html serão feitas apenas no componente page-home e que este arquivo, ou
seja, o template html que iremos utilizar, se chama home.html, veja:

@Component({
selector: 'page-home',
templateUrl: 'home.html'
})

Feedback

Lembre-se que, por padrão, os Decorators ficam sempre 




em cima do bloco de definição da classe.

O seletor page-home será útil quando for necessário criar regras de estilo em CSS aplicadas apenas à ele.

E, por m, temos nosso escopo de classe. Classes em qualquer linguagem de programação orientada à
objeto servem para de nir a estrutura e o comportamento de objetos. Por enquanto o que você precisa saber
é que nossa classe tem apenas um construtor que recebe um objeto do tipo NavController por parâmetro.

''
Nosso próximo passo será inserir novos atributos e métodos para de nir melhor o seu comportamento:

export class HomePage {


constructor(public navCtrl: NavController) {}

Passo 2 – Consumindo dados de uma API pública

Agora que você já sabe mais ou menos como as coisas funcionam, vamos fazer rapidamente uma requisição
à uma API externa (Reddit) para exibir seu resultado em uma lista no nosso template.

2.1 – Trabalhando com Observables

Inclua o seguinte código em home.ts:

import { Component } from '@angular/core';


import { NavController } from 'ionic-angular';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {

public feeds: Array<string>;


private url: string = "https://www.reddit.com/new.json";

constructor(public navCtrl: NavController, public http: Http) {


Feedback

this.http.get(this.url).map(res => res.json())


.subscribe(data => {
this.feeds = data.data.children;
}); 



}

_Caso queira entender melhor sobre os endpoints da API, dê uma olhada nesse link._

O que zemos acima foi importar o componente Http e injetá-lo no método construtor. Isso nos possibilita
acessar sua instância através do objeto this. Note que também estamos importando o operador map da
biblioteca rxjs. O rxjs é uma das extensões que compõe a reactiveX (Reactive Extensions), uma biblioteca
assíncrona que trabalha com o stream de dados no padrão Observable.

No objeto http estamos fazendo uma requisição do tipo GET à um endpoint que de nimos na variável url,
acima do método construtor. Note que, com o uso do TypeScript, podemos de nir seu escopo (pública ou
privada) e ainda de nir o seu tipo (string, number, array…). Ponto para o TypeScript!

Em seguida, transformamos o resultado dessa requisição utilizando o operador map e o convertemos para

''
JSON através do método subscribe (“similar” ao método then de uma Promise).

É importante salientar que o map da biblioteca rxjs é


utilizado exclusivamente para mapear um array do tipo
Observable e não é o mesmo map que utilizamos em um
array comum no JavaScript. Aprenda mais sobre
requisições remotas com Observables nesse link.

Por m, incluímos o resultado da requisição (agora um objeto do tipo JSON) dentro da variável pública feeds,
que aqui representa um array de strings. Seu escopo precisa ser público pois iremos acessar seu conteúdo no
template.

2.2 – Exibindo resultado para o usuário Feedback

Como você percebeu, dentro do nosso arquivo home.ts há uma referência ao template home.html dentro do
bloco @Component. Esse template, na verdade, é aquilo que o usuário realmente vê na tela do seu 



smartphone, com base no que de nimos dentro da nossa classe. Por enquanto ele não está exibindo nada.
Modi que o conteúdo de home.html conforme abaixo:

<ion-header>
<ion-navbar>
<ion-title>My Feed Reader</ion-title>
</ion-navbar>
</ion-header>

<ion-content>
<ion-list>
<ion-item *ngFor="let feed of feeds">
{{feed.data.title}}
</ion-item>
</ion-list>
</ion-content>

O Ionic fornece uma grande variedade de componentes visuais out of the box que nos permite construir uma
interface praticamente idêntica à de uma aplicação nativa. Não só isso como também é capaz de adaptar o
seu estilo visual de acordo com cada plataforma (algo que veremos em breve).

A tag representa a barra de navegação que ca no topo da tela. Essa barra geralmente comporta o título da
aplicação (como visto na tag ) mas também pode conter botões de ação e demais itens, caso necessário.

Já as informações dinâmicas sempre são inseridas dentro da tag , como acabamos de fazer ao inserir o

''
componente .

Não iremos nos aprofundar nos detalhes dos templates


visuais fornecidos pelo Ionic. Você pode encontrar
exemplos do markup de cada componente aqui. O
componente que estamos utilizando no exemplo acima é
este. Eu apenas copiei o markut e inseri aqui, alterando
apenas aquilo que é necessário. Esta é, sem dúvida, uma
das features mais importantes do framework, uma vez que
ela acelera o processo de prototipação de um aplicativo. Feedback





 

Observe o seguinte bloco de código:

<ion-list>
<ion-item *ngFor="let feed of feeds">
{{feed.data.title}}
</ion-item>
</ion-list>
Note o loop que estamos executando com a instrução *ngFor. Estamos acessando o conteúdo do array
feeds e iterando sobre ele com uma variável local (feed) para popular nossa lista. Esta é uma conveniência
fornecida pelo Angular conhecida como Embedded templates ou diretivas html, que nos ajuda na
renderização dos atributos disponíveis na classe associada. Observe agora o seguinte trecho:

{{feed.data.title}}

Ele representa o valor que será exibido em cada célula da lista, que, neste caso, representa o título do feed.
Esta sintaxe entre chaves duplas é chamada de Interpolação.

Agora salve o arquivo e veri que o resultado no browser. É provável que você esteja vendo algo assim:

Se você entendeu tudo que foi explicado até aqui, signi ca que você já domina boa parte dos principais
Feedback

conceitos não só do Ionic 2 como também do Angular 2. Parabéns!





Agora é o momento em que nos despedimos das explicações mais detalhadas e partimos para a ação.
Vamos dar um tapinha no visual desse app e inserir alguns recursos extras que irão torná-lo ainda mais sexy

Passo 3 – Customizando o template

Nosso próximo passo será incluir mais informações nas células dessa lista, uma vez que apenas o título não
é o su ciente para capturar a atenção do usuário.
3.1 – Adicionando informações extras

Ainda em home.html, altere o conteúdo atual de dentro da tag para:

<ion-list>
<ion-item *ngFor="let feed of feeds">
<ion-thumbnail item-left>
<img [src]="feed.data.thumbnail">
</ion-thumbnail>
<h2>{{feed.data.title}}</h2>
<p>{{feed.data.domain}}</p>
</ion-item>
</ion-list>

Salve o arquivo e visualize o resultado no browser:

Feedback




Agora estamos utilizando um novo template de lista, que comporta imagens. O Ionic já faz o serviço de
ajustar os itens pra você contanto que indiquemos isso através dos atributos e classes que o framework nos
oferece. Perceba, por exemplo, o atributo item-left presente dentro da tag . Altere seu nome para item-right e
você verá que as imagens serão posicionadas à direita da célula. Tente também alterar a tag para e verá que
as imagens carão menores e com bordas arredondadas. Muito conveniente!

Note que o atributo src da tag de imagem está envolto por colchetes. Essa sintaxe se chama Property binding
e é utilizada para atribuir uma propriedade da view ao valor de uma expressão. No entanto, a mesma sintaxe
pode ser substituída por esta:

<img src="{{ feed.data.thumbnail }}">

Para ns didáticos iremos deixar a expressão com colchetes neste exemplo.

Perceba também que o título do feed agora aparece dentro da tag h2 e um novo item foi inserido dentro de
uma tag p. Você pode utilizar o Chrome Developer Tools para inspecionar a conteúdo da listagem disponível
no array inserindo a instrução console.log(this.feed); ao m da requisição, dessa forma:

this.http.get(this.url).map(res => res.json())


.subscribe(data => {
this.feeds = data.data.children;
// Exibindo conteúdo do array no console do browser
console.log(this.feeds);
});

Passo 4 – Fornecendo feedback ao cliente e capturando


eventos

Embora nossa aplicação consiga requisitar dados externos com sucesso, não há nada que informe ao
usuário sobre o status dessa ação. Ele pode aguardar poucos segundos como também pode esperar uma
eternidade até que alguma coisa apareça na tela do celular, dependendo do tipo de conexão que esteja
enfrentando.

4.1 – Adicionando um Loading


Feedback

Insira o seguinte conteúdo no arquivo home.ts:





import { Component } from '@angular/core';


import { NavController, LoadingController } from 'ionic-angular';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {

public feeds: Array<string>;


private url: string = "https://www.reddit.com/new.json";
constructor(public navCtrl: NavController, public http: Http, public loadingCtrl:
LoadingController) {

this.fetchContent();

fetchContent ():void {
let loading = this.loadingCtrl.create({
content: 'Fetching content...'
});

loading.present();

this.http.get(this.url).map(res => res.json())


.subscribe(data => {
this.feeds = data.data.children;
loading.dismiss();
});
}

Salve o arquivo e veri que imediatamente o resultado no browser:

Feedback




O Loading é um ótimo componente para fornecer feedback visual para o usuário, indicando que alguma
atividade está sendo executada em background. Nada mais é que uma caixa de diálogo que bloqueia
qualquer atividade do usuário até que determinada ação seja concluída. A nossa caixa de diálogo inclui um
spinner e um texto indicativo por padrão, mas todas essas opções podem ser customizadas para atender
melhor a necessidade do seu app.

Incluir um Loading é extremamente simples: Primeiro nós importamos o componente LoadingController da


biblioteca ionic-angular e injetamos o objeto no método construtor. Em seguida, inicializamos o Loading com
uma mensagem de feedback e depois apresentamos ele através do método present. Depois nós retiramos o
componente da tela caso tenhamos sucesso na requisição através do método dismiss. Simples!

Perceba também que, como boa prática, movemos a requisição da API para um método chamado
fetchContent que é então chamado imediatamente no construtor. Outra novidade é a inclusão do tipo de
retorno do método, tipado como void. Se você vem de linguagens como Java, sabe que esta é uma maneira
de dizer que o método não retorna nada, apenas executa uma ação.

4.2 – Eventos html

Antes de passarmos para a próxima etapa, vamos incluir um evento nas células. Faça a seguinte modi cação
em home.html:

<ion-item *ngFor="let feed of feeds" (click)="itemSelected(feed)">

Queremos executar alguma ação sempre que o usuário clicar/tocar em uma das células. Conseguimos isso
fazendo o binding do método itemSelected no evento html click e passando o feed como argumento. Essa
sintaxe de incluir eventos html dentro de parênteses é chamado de Event Binding no Angular.

Agora basta incluir o método dentro da classe:

itemSelected (feed):void { Feedback


alert(feed.data.url);
}




Salve o arquivo e clique em cima de alguma célula. A url do post será exibida em um alert!

Passo 5 – Exibindo o conteúdo de uma url no browser

Agora que você já entendeu como capturar uma ação do usuário, vamos prosseguir com as funcionalidades
do nosso app e fazer com que o post seja exibido no browser.

5.1 – Instalando plugin InAppBrowser


Em uma nova aba do terminal, entre na pasta do projeto e digite o seguinte:

ionic plugin add cordova-plugin-inappbrowser

Este plugin nos possibilita abrir sites externos em um browser diretamente do app. Mas só será possível
testar essa funcionalidade se você zer o build para testar no emulador ou no seu próprio dispositivo. Para
isso, digite a seguinte instrução no terminal caso você possua um iPhone:

ionic platform add ios

Ou, caso tenha um dispositivo Android:

ionic platform add android

Agora altere o parâmetro do método no arquivo home.html para enviar apenas a url como argumento:

<ion-item *ngFor="let feed of feeds" (click)="itemSelected(feed.data.url)">

E agora basta fazer as seguintes alterações em home.ts. Primeiro, importar a classe do plugin:

import { InAppBrowser } from 'ionic-native';

Em seguida, faça a seguinte alteração no método:

itemSelected (url: string):void { Feedback


let browser = new InAppBrowser(url, '_system');
}




Pronto! Agora só resta testar se a funcionalidade está sendo executada conforme desejado.

5.2 – Executando testes nas plataformas

Para instalar o emulador do iOS e preparar o ambiente para testes no seu próprio iPhone, basta digitar no
terminal:

npm -g install ios-sim ios-deploy .


Agora digite a instrução abaixo e, caso tudo tenha dado certo, é provável que você veja o aplicativo abrindo no
seu emulador:

ionic run ios

Caso esteja com o celular conectado ao computador através da porta USB, o deploy será automaticamente
executado no seu iPhone. Se mesmo assim você encontrar di culdades, tente digitar:

ionic run ios --device

Para testar no Android, apenas digite:

ionic run android

Maiores detalhes sobre deploy e testes em ambas plataformas você encontra aqui.

Caso você tenha conseguido testar com sucesso, deve ter percebido que, ao clicar em uma das células, há
um certo delay entre o momento do clique e o carregamento da página. Para corrigir isso, apenas insira o
conteúdo da célula dentro de um botão (button) com o atributo ion-item, dessa forma:

<button ion-item *ngFor="let feed of feeds" (click)="itemSelected(feed.data.url)">


<ion-thumbnail item-left>
<img [src]="feed.data.thumbnail">
</ion-thumbnail>
<h2>{{feed.data.title}}</h2>
<p>{{feed.data.domain}}</p>
</button>

Agora o delay não só é removido como é adicionado um overlay em tom mais escuro na célula quando a Feedback

mesma é pressionada.

Tem mais uma coisa que está incomodando: Perceba que os posts sem imagens estão quebrando nosso 



layout e deixando a nossa lista com aspecto pouco pro ssional. Vamos mudar isso incluindo o seguinte
trecho de código dentro do método subscribe de fecthContent:

this.feeds.forEach((e, i, a) => {
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1 ) {
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}
})
Utilizamos o método forEach do JavaScript para iterar pelo array de feeds e veri car quais itens estão sem
imagem. Em seguida, para estes itens, incluímos uma imagem padrão do próprio reddit, que está disponível
em um link público e irá servir de placeholder.

Veja o resultado de todas estas modi cações rodando em um device iOS:

Feedback





Passo 6 – Adicionando scroll in nito na célula

Nosso app está cando bem legal mas ainda necessita de algumas modi cações para car realmente
atrativo. Uma delas é viabilizar alguma maneira de acessar os posts mais antigos, já que nosso app tem uma
restrição de apenas 25 itens por request. Isto é muito ruim, pois o usuário ca limitado à visitar apenas estes
itens.

Se você explorar a API do reddit vai perceber que ela nos fornece vários parâmetros do tipo GET para
controlar ltros e paginações. Um deles é chamado after, que utiliza o o atributo fullName (junção do tipo do
post mais o seu ID) como identi cador único e funciona como âncora para os demais posts.

Em outras palavras, uma requisição como esta:

https://www.reddit.com/new.json?after=t3_57ct5z

''
Pode ser lida como: “_Busque os novos posts que vem depois do post de nome t357ct5z”

Fique atento com a forma como você lê a instrução pois há


uma pegadinha: Depois aqui se refere ao array de posts,
ou seja, os posts mais velhos e não os mais recentes.
Veremos como buscar os mais recentes na próxima etapa
do app

Feedback





Agora que você já entendeu a mecânica, vamos começar inserindo o componente responsável por acionar o
scroll in nito na nossa página. Insira a seguinte instrução em home.html imediatamente após o m da tag :

<ion-infinite-scroll (ionInfinite)="doInfinite($event)">
<ion-infinite-scroll-content
loadingText="Loading more data...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>

E criamos o método correspondente em nossa classe:


doInfinite(infiniteScroll) {

let paramsUrl = (this.feeds.length > 0) ? this.feeds[this.feeds.length - 1].data.name : "";

this.http.get(this.olderPosts + paramsUrl).map(res => res.json())


.subscribe(data => {

this.feeds = this.feeds.concat(data.data.children);

this.feeds.forEach((e, i, a) => {
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1
) {
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}
})
infiniteScroll.complete();
});
}

Por m, inserimos a url da requisição:

private olderPosts: string = "https://www.reddit.com/new.json?after=";

O novo método é bem parecido com o fetchContent, com a diferença de que criamos uma variável local que
guarda o valor do atributo nome do último item do array de feeds e insere este valor no m da url. Em seguida,
pegamos o array resultante da requisição e adicionamos no m do array original através do método concat
do JavaScript. Note também que utilizamos o método complete do componente, indicando que o mesmo
deve ser removido da view.

O resultado você confere abaixo: Feedback





Feedback




E, com isto, incluímos uma funcionalidade extremamente importante em aplicações móveis: A habilidade de
adicionar itens em uma lista por demanda, algo que enriquece em muito a experiência do usuário. Se você
estava esperando instruções muito complexas, sinto lhe desapontar!

Passo 7 – Atualizando a lista com pull-to-refresh


Da mesma forma que adicionamos uma funcionalidade para carregar posts mais antigos sempre que
chegarmos ao m da nossa lista, precisamos agora viabilizar uma maneira de atualiza-la com os posts mais
recentes. Uma excelente maneira de fazer isso é incluindo o componente Refresher na nossa aplicação.

O Refresher é um componente que adiciona o recurso de pull-to-refresh à nossa lista. O pull-to-refresh


consiste em manter o dedo pressionado no topo de uma lista e arrastá-la até uma determinada posição até
que um evento seja disparado. No nosso caso, utilizaremos esse evento para inserir os posts mais recentes
no início do array, ao contrário do que zemos com o componente In niteScroll.

Sem mais delongas, vamos começar inserindo o markup do componente antes da tag no arquivo home.html:

<ion-refresher (ionRefresh)="doRefresh($event)">
<ion-refresher-content
pullingIcon="arrow-dropdown"
pullingText="Pull to refresh"
refreshingSpinner="circles"
refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>

Diferentemente do In niteScroll, desta vez eu incluí alguns parâmetros adicionais, como os textos de início e
m do evento, o formato padrão do spinner, o ícone da seta, etc.

A url da requisição também precisa ser diferente, uma vez que iremos buscar os itens mais novos.
Utilizaremos então o parâmetro before oferecido pelo Reddit, fazendo com que a nossa nova url que assim:

private newerPosts: string = "https://www.reddit.com/new.json?before=";

Feedback

Por m, inserimos o método na classe:





doRefresh(refresher) {

let paramsUrl = this.feeds[0].data.name;

this.http.get(this.newerPosts + paramsUrl).map(res => res.json())


.subscribe(data => {

this.feeds = data.data.children.concat(this.feeds);

this.feeds.forEach((e, i, a) => {
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1 )
{
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}
})
refresher.complete();
});
}

Perceba como o método é similar àquele que escrevemos para o scroll in nito. A única diferença está na
variável de parâmetro (que agora guarda o nome do primeiro item da lista como referência) e a maneira como
concatenamos o array de feeds, inserindo os novos dados no início da lista e não no m. Observe também a
instrução refresher.complete, que informa que operação foi concluída e que o componente pode ser
removido da view.

Nossa aplicação agora utiliza o refresher para atualizar a lista com os novos posts, veja:

Feedback





Feedback




Passo 8 – Filtrando a lista com uma Action Sheet

Agora que a nossa lista tem potencial para aumentar cada vez mais de tamanho, seria interessante termos
uma opção de ltrar posts pertencentes à determinadas categorias. Podemos fazer isso facilmente com uma
Action Sheet.
No arquivo home.html vamos incluir um botão do lado direito da nossa AppBar/NavBar que será responsável
por disparar o método:

<ion-header>
<ion-navbar>
<ion-title>My Feed Reader</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="showFilters()">
<ion-icon name="funnel"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>

Observe o atributo end na tag , indicando que o botão deve ser posicionado à direita, ou seja, no m da barra
de navegação. O atributo start posicionaria o botão à esquerda. O funciona como um container de botões. No
nosso caso, só precisamos de um que será representado por um ícone (por isso o atributo icon-only).
Fizemos o binding do método showFilters no evento click e escolhemos o ícone de nome funnel para

''
representar o ltro.

Os ícones no Ionic são uma implementação própria do que


chamamos de icon fonts, ou seja, fontes que contém
símbolos ao invés de texto ou números e que podem ser
estilizados utilizando CSS. Utilizar esse tipo de fonte é
conveniente pois reduz a necessidade de imagens, o que Feedback

torna nosso aplicativo ligeiramente mais rápido e menos


pesado. Para ter acesso à lista de ícones do Ionic 2, dê uma




olhada aqui.

Antes de incluir nosso método, precisamos de mais duas variáveis. Uma que será responsável por guardar a
versão íntegra do nosso array de feeds (sem nenhum ltro) e uma outra que será um booleano, com a função
de indicar se há ou não um ltro ativo:
public noFilter: Array<any>;
public hasFilter: boolean = false;

Com isso podemos incluir as seguintes instruções no nal do método subscribe das funções doRefresh e
doIn nite, com a nalidade de remover qualquer ltro ativo:

this.noFilter = this.feeds;
this.hasFilter = false;

Por m, vamos agora incluir o método showFilters na classe:

showFilters() :void {

let actionSheet = this.actionSheetCtrl.create({


title: 'Filter options:',
buttons: [
{
text: 'Music',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() ===
"music");
this.hasFilter = true;
}
},
{
text: 'Movies',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() ===
"movies");
this.hasFilter = true;
} Feedback
},
{
text: 'Cancel',
role: 'cancel', 



handler: () => {
this.feeds = this.noFilter;
this.hasFilter = false;
}
}
]
});

actionSheet.present();

}
Primeiramente inicializamos o componente com a função create em uma variável local. Este componente
recebe um título e um array de botões onde cada botão tem, obrigatoriamente, um texto indicativo e um
handler que dispara o evento correspondente. Estes botões representam as opções que serão apresentadas
para o usuário na tela. O código do ltro é autoexplicativo.

O último botão tem a função de cancelar a operação e remover qualquer ltro que esteja ativo. Perceba que
este botão tem uma propriedade role com o valor de cancel, indicando que adota o comportamento padrão
da plataforma e sempre estará posicionado como última opção da lista. Vale ressaltar que se o usuário clicar
fora da Action Sheet, ou seja, no overlay da camada de fundo, a ação será interpretada como um
cancelamento (o mesmo comportamento do botão com a role “cancel”).

Em seguida adicionamos o método actionSheet.present para que o componente seja apresentado na tela.

Por enquanto só estamos ltrando os subreddits com as categorias música ou lmes, mas nada nos impede
de inserir mais opções de ltro no componente. O código nal da nossa classe ca assim:

import { Component } from '@angular/core';


import { NavController, LoadingController, ActionSheetController } from 'ionic-angular';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import { InAppBrowser } from 'ionic-native';

@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {

public feeds: Array<any>;


private url: string = "https://www.reddit.com/new.json";
private newerPosts: string = "https://www.reddit.com/new.json?before=";
private olderPosts: string = "https://www.reddit.com/new.json?after=";
Feedback
public hasFilter: boolean = false;
public noFilter: Array<any>;

constructor(public navCtrl: NavController, public http: Http, 





public loadingCtrl: LoadingController, public actionSheetCtrl: ActionSheetController) {

this.fetchContent();

fetchContent ():void {
let loading = this.loadingCtrl.create({
content: 'Fetching content...'
});

loading.present();

this.http.get(this.url).map(res => res.json())


.subscribe(data => {
this.feeds = data.data.children;

this.feeds.forEach((e, i, a) => {
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1 )
{
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}
})

this.noFilter = this.feeds;

loading.dismiss();
});
}

doRefresh(refresher) {

let paramsUrl = this.feeds[0].data.name;

this.http.get(this.newerPosts + paramsUrl).map(res => res.json())


.subscribe(data => {

this.feeds = data.data.children.concat(this.feeds);

this.feeds.forEach((e, i, a) => {
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1 )
{
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}
})

this.noFilter = this.feeds;
this.hasFilter = false;

refresher.complete();
});
}

doInfinite(infiniteScroll) { Feedback

let paramsUrl = (this.feeds.length > 0) ? this.feeds[this.feeds.length - 1].data.name : "";

this.http.get(this.olderPosts + paramsUrl).map(res => res.json())






.subscribe(data => {

this.feeds = this.feeds.concat(data.data.children);

this.feeds.forEach((e, i, a) => {
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1
) {
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}
})

this.noFilter = this.feeds;
this.hasFilter = false;

infiniteScroll.complete();
});
}

itemSelected (url: string):void {


let browser = new InAppBrowser(url, '_system');
}

showFilters() :void {

let actionSheet = this.actionSheetCtrl.create({


title: 'Filter options:',
buttons: [
{
text: 'Music',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() ===
"music");
this.hasFilter = true;
}
},
{
text: 'Movies',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() ===
"movies");
this.hasFilter = true;
}
},
{
text: 'Games',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() ===
"gaming");
this.hasFilter = true;
}
},
{
text: 'Pictures',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() === Feedback
"pics");
this.hasFilter = true;
}
},




{
text: 'Ask Reddit',
handler: () => {
this.feeds = this.noFilter.filter((item) => item.data.subreddit.toLowerCase() ===
"askreddit");
this.hasFilter = true;
}
},
{
text: 'Cancel',
role: 'cancel',
handler: () => {
this.feeds = this.noFilter;
this.hasFilter = false;
}
}
]
});

actionSheet.present();

Por questões de bom senso, seria interessante indicar ao usuário quando um ltro está ou não ativo alterando
a cor do ícone do funil. Podemos fazer isso utilizando o conceito de Property binding explicado mais acima,
com a diferença de que agora a propriedade será atribuída baseada em uma condição.

insira o seguinte código na tag em home.html:

<ion-icon name="funnel" [style.color]="hasFilter ? 'orange' : 'inherit'"></ion-icon>

O resultado pode ser visto abaixo:

Feedback





Feedback




Passo 9 – Adicionando um provider e uma barra de busca

Apesar de termos avançado com sucesso até aqui, tenho certeza de que a quantidade de código repetitivo
presente em nossa classe deve ter te causado um certo incômodo. Podemos muito bem mover a
responsabilidade de conexão com a API para um outro serviço externo, no intuito de evitar o DRY e a
propagação de code smell.

9.1 – Criando um Injectable

O Angular nos permite criar uma classe com a anotação @Injectable para estes cenários. Esse tipo de classe
também são conhecidos como Providers e podem tanto ser criados “na mão” quanto com a ajuda do CLI.
Digite no terminal:

ionic g provider RedditService

Esse código cria uma pasta providers no nosso projeto com um arquivo de nome reddit-service.ts, onde o
Ionic insere alguns códigos de boilerplate para facilitar nossa vida. Altere seu conteúdo conforme abaixo:

import { Injectable } from '@angular/core';


import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class RedditService {

private feeds: Array<any>;

constructor(private http: Http) {}

fetchData(url: string): Promise<any> {

return new Promise(resolve => {

this.http.get(url).map(res => res.json())


.subscribe(data => {
this.feeds = data.data.children;

this.feeds.forEach((e, i, a) => { Feedback


if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1
) {
e.data.thumbnail = 'http://www.redditstatic.com/icon.png';
}




})
resolve(this.feeds);
}, err => console.log(err));
});
}
}

Replicamos boa parte do código presente no método fetchContent da classe home.ts aqui no nosso método
fetchData, com algumas diferenças. A primeira delas é a já citada anotação @Injectable() presente antes do
nome da classe, o que nos permite mover a de nição do serviço para o construtor de home.ts dessa forma:
constructor(public redditService: RedditService) {}

Isso evita que tenhamos de instanciar o serviço utilizando new. Clique aqui para saber mais sobre Injeção de
Dependência.

Outra mudança importante é que, por conveniência, a assinatura do método retorna uma Promise do tipo any
(para evitar que tenhamos qualquer erro em tempo de compilação) ao invés de um Observable.

Por m, para utilizar este serviço em nossa classe home.ts precisamos incluí-lo no arquivo app.module.ts,
dentro da pasta src/app. Este arquivo faz uso da anotação @NgModule, onde todas as dependências da
aplicação devem ser declaradas previamente:

import { NgModule } from '@angular/core';


import { IonicApp, IonicModule } from 'ionic-angular';
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
//indicamos o source path do arquivo:
import { RedditService } from '../providers/reddit-service';

@NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
], Feedback
//declaramos o nome do nosso provider:
providers: [RedditService]
})
export class AppModule {} 


Com isso é possível escrever os métodos da nossa classe home.ts da seguinte maneira:

this.redditService.fetchData(this.url).then(data => {
this.feeds = data;
this.noFilter = this.feeds;
loading.dismiss();
})
Repare que além de muito mais simples, agora utilizamos o método then ao invés do subscribe para
recuperar os dados do serviço e preencher nosso array.

9.2 – Adicionando uma SearchBar

Para aplicativos que utilizam listas e exibem conteúdo sob demanda é uma boa prática adicionar algum
recurso de busca para que o usuário procure informações com base em uma palavra especí ca ou sequência
de caracteres. Para tal, o Ionic fornece um componente chamado SearchBar.

Para evitar con itos com as ações da nossa lista, escolhi inserir o componente diretamente na
AppBar/NavBar da aplicação. Para tal, insira o seguinte bloco de código dentro da tag em home.html:

<ion-searchbar
[(ngModel)]="searchTerm"
(ionInput)="filterItems()"
placeholder="Type here..." >
</ion-searchbar>

Perceba que a junção das sintaxes de Event binding e Input binding do ngModel nos permite replicar o tão
famoso recurso de Two-way data binding no Angular 2.

Agora inclua o seguinte método em home.ts:

filterItems() {
this.hasFilter = false;
this.feeds = this.noFilter.filter((item) => {
return item.data.title.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
});
}
Feedback

Veja o resultado rodando em um iPhone:






Feedback




Antes de concluir eu gostaria de mostrar um recurso fornecido pelo framework que nos permite testar o
comportamento e visual da nossa aplicação em diferentes plataformas chamado Ionic Lab. Caso ainda
esteja com o servidor ativo, basta inserir /ionic-lab após o número da porta na url ou digitar
ionic serve --lab no terminal. O resultado é o seguinte:
Na imagem acima você consegue visualizar o nosso aplicativo no iOS, Android e Windows Phone! O Ionic
não apenas executa o build da aplicação com uma única base de código para dispositivos diferentes como
também se adapta ao comportamento e estilo visual de cada um, numa tentativa de fazer com que o usuário
sempre tenha uma experiência condizente com a plataforma que utiliza. Perceba, por exemplo, como o
spinner, a barra de busca, os ícones e estilo da lista são diferentes entre as plataformas. No caso do iOS, são
incluídos até mesmo as setas na lateral direita da célula, o que é comum na plataforma. Além de tudo isso,
poder testar aplicativos dessa maneira e ainda tirar proveito do recurso de LiveReload enquanto você está
programando é algo realmente especial.

Estamos chegando ao m da criação do nosso aplicativo. Vimos que, apenas com pequenos ajustes, foi
possível obter um código mais modular e ainda incluir o componente SearchBar com o estilo visual
adequado para cada plataforma. Tudo isso de maneira simples e rápida, graças ao casamento perfeito entre
o Angular e os componentes estilizados fornecido pelo Ionic.
Feedback

Passo 10 – Melhorando a experiência do usuário





Mesmo com todos os recursos que o Ionic 2 nos oferece é sempre importante garantir a melhor experiência
possível para o usuário fazendo otimizações gerais, como customização de UI, ajustes no comportamento de
componentes, ganho de performance, etc. Essa última etapa será dedicada à este propósito.

10.1 – Controlando o scroll

Notei alguns problemas ao utilizar a Action Sheet para ltrar a lista quando o scroll está numa posição muito
abaixo, pois a ação de carregar posts antigos pode ser disparada indevidamente. Podemos evitar isso
fazendo a lista rolar para o topo antes de executar qualquer ltro. Mas como controlar isso
programaticamente?
O componente Content (que gere a tag do nosso template html) disponibiliza um método de controle do
scroll chamado scrollToTop. Podemos inserir o código no início do método showFilters da Action Sheet
dessa forma:

this.content.scrollToTop();

Antes precisamos obter uma referência à este componente utilizando a anotação @ViewChild da biblioteca
@angular/core (algo similar à maneira como protocolos funcionam no iOS):

@ViewChild(Content) content: Content;

Agora a lista vai rolar para o topo sempre que acionarmos a Action Sheet!

10.2 – Melhorando a busca com Observables

Apesar de termos nossa barra de buscas funcionando perfeitamente, a cada caractere digitado estamos
emitindo uma nova requisição, o que é desnecessário. Mas há uma forma elegante de lidar com isso
utilizando Observables, uma vez que o evento só será disparado quando uma requisição for considerada
válida.

O que queremos fazer é monitorar o componente de duas maneiras: A primeira é oferecendo um tempo
maior para que o usuário conclua a digitação da palavra que está buscando através do método
debounceTime e a segunda é utilizando o método distinctUntilChanged que irá comparar a palavra (ou a
sequência de caracteres) digitada com a última que foi procurada, evitando que uma nova requisição seja
emitida para um resultado que já se encontra na tela.
Feedback

Iremos utilizar o FormControl de @angular/forms que irá conectar uma variável da classe ao input presente
no nosso html (similar à maneira como o Two way binding funciona).



Inclua as seguintes instruções no componente em home.html:

<ion-searchbar
[(ngModel)]="searchTerm"
[formControl]="searchTermControl"
[showCancelButton]=true
(ionInput)="filterItems()"
placeholder="Type here..." >
</ion-searchbar>
Note que além do formControl também atribuímos o valor true à propriedade showCancelButton, que irá
apresentar um botão para cancelar a busca e retirar o teclado digital da tela.

E a seguinte instrução que irá controlar quando devemos disparar a busca:

this.searchTermControl = new FormControl();


this.searchTermControl.valueChanges.debounceTime(1000).distinctUntilChanged().subscribe(search
=> {
if (search !== '' && search) {
this.filterItems();

}
})

E com isso o componente se torna mais coerente com a expectativa do usuário, que irá perceber um ganho
de performance ao ltrar resultados em uma lista com muitas células.

10.3 – Ajustando o visual dos componentes com CSS

Nosso aplicativo agora depende de algumas mudanças visuais para corrigir alguns pequenos detalhes. O
primeiro deles é referente ao Android. O Ionic 2, ao rodar em um dispositivo Android, oferece
automaticamente suporte ao Material Design do Google (uma linguagem visual que sintetiza princípios
clássicos daquilo que considera o “bom design”). Em resumo, o Material Design se preocupa em criar uma
experiência uni cada de layout entre as plataformas que rodam o sistema operacional do Android. Aqui você
pode conhecer melhor sobre seus princípios fundamentais.

Um dos pontos de atenção é a maneira como os textos devem ser apresentados ao usuário. O Material
Design trabalha com a noção de hierarquia baseada em tons e opacidade. Em outras palavras, textos
primários (que representam títulos e informações de destaque) recebem 87% de opacidade enquanto Feedback
subtítulos recebem 54%. Veja abaixo:





Se você for inspecionar as cores presentes nos textos das células (utilize o Devtools para tal) vai notar que
elas não seguem este ponto da especi cação da linguagem. O texto principal, por exemplo, utiliza preto puro
e é sempre bom ( ca aqui a dica) evitar preto puro em seus designs sempre que possível.

Diferentemente do Android, a preocupação do iOS está voltada para o conteúdo, por isso não existe nenhuma
linguagem tão restritiva quanto o Material Design na plataforma. No entanto, irei replicar a mudança visual
que faremos para o Android também no iOS, tornando nossos textos ainda mais agradáveis para leitura.

Em home.scss inclua o seguinte código:

// iOS & Android only


.item-md, .item-ios {
h2 {
color: rgba($color: #000, $alpha: .87);
}
p {
color: rgba($color: #000, $alpha: .54);
}
}

Repare que podemos fazer o nesting dos elementos por estar utilizando Sass. Também perceba que
aplicamos a alteração apenas para as plataformas Android e iOS mas não para Windows Phone. As classes
você pode obter facilmente ao inspecionar o DOM no console do browser.

Outro problema aparente são os títulos dos posts que somem ao atingir a borda da célula. Precisamos incluir
uma quebra de linha para que eles sejam apresentados por completo. Dessa vez iremos aplicar a alteração às
três plataformas:

// iOS, Android & WP Feedback


.item-md, .item-ios, .item-wp {
h2, p {
white-space: normal;
} 



}

Por m, gostaria de melhorar a maneira como a barra de busca se apresenta na versão iOS. Ela está pequena
e diminui ainda mais de tamanho quando o botão de cancelar está ativo. Também seria interessante
escurecer um pouco mais a opacidade do background para lhe conferir maior destaque:

Inclua o seguinte código (desta vez aplicado apenas para o iOS):


// iOS only
.toolbar-ios {
ion-title {
padding: 0 90px 0 1px;
}
.searchbar-ios .searchbar-input {
background: rgba($color: #000, $alpha: 0.12);
}
}

''
Caso prefira, você também pode alterar o valor das
variáveis Sass do Ionic. Neste link há uma lista de todas
elas.

10.4 – Ajustes nais no html

Vamos iniciar modi cando a cor da NavBar. Inclua o seguinte atributo na tag em home.html:

<ion-navbar color="secondary">

Como estamos utilizando Sass, fazemos uma referência à variável secondary do array colors que está listado
no arquivo src/theme/variable.scss. Isso signi ca que você pode alterar o valor da cor na variável ao invés de
escrever diretamente no template html.
Feedback

Agora eu gostaria de inserir um ícone na frente do meu endereço de domínio (que representa meu subtítulo
na lista) sempre que a categoria estiver relacionada com alguma das listadas na nossa Action Sheet. Eu




posso controlar esse comportamento utilizando a diretiva de html do Angular chamada ngSwitch.

Substitua esta linha:

<p>{{feed.data.domain}}</p>

Por esta instrução:


<div [ngSwitch]=feed.data.subreddit.toLowerCase()>
<p *ngSwitchCase="'askreddit'"><ion-icon name="help-circle"></ion-icon> {{feed.data.domain}}
</p>
<p *ngSwitchCase="'gaming'"><ion-icon name="logo-playstation"></ion-
icon> {{feed.data.domain}}</p>
<p *ngSwitchCase="'music'"><ion-icon name="musical-notes"></ion-icon> {{feed.data.domain}}
</p>
<p *ngSwitchCase="'movies'"><ion-icon name="film"></ion-icon> {{feed.data.domain}}</p>
<p *ngSwitchCase="'pics'"><ion-icon name="image"></ion-icon> {{feed.data.domain}}</p>
<p *ngSwitchDefault>{{feed.data.domain}}</p>
</div>

Observe que os ícones apenas serão aplicados no caso de coincidirem com os argumentos. Em caso
contrário, será exibido apenas o texto sem nenhum ícone, conforme descrito na cláusula ngSwitchDefault.

Outra coisa que me incomoda é o componente Refresher ser acionado com muito pouco esforço. Eu sinto
que o usuário poderia puxar um pouco mais a lista para evitar que o evento seja disparado com muita
facilidade. Podemos modi car isso alterando a propriedade pullMin, veja:

<ion-refresher (ionRefresh)="doRefresh($event)" [pullMin]=90>

Alteramos para 90 dpi a distância mínima que o usuário deve alcançar para disparar o evento. A distância
padrão é 60.

Seria também interessante alterar a cor da barra de status da aplicação para a cor branca, já que o fundo da
NavBar agora está colorido. Como a barra de status é um componente nativo, para modi cá-lo precisaremos
instalar um plugin do Cordova. Veri que se ele já está instalado procurando no arquivo package.json por
“cordova-plugin-statusbar“. Caso contrário, digite no terminal: Feedback

ionic plugin add cordova-plugin-statusbar





E insira a seguinte instrução dentro do método construtor do arquivo src/app/app.component.ts:

StatusBar.backgroundColorByHexString('#ffffff');

E agora veja como cou o visual nal da nossa aplicação rodando em um iPhone 6:
Feedback




10.5 – Aumentando o desempenho

Se você pensa em evoluir de um protótipo para um aplicativo real, se preocupar com o seu desempenho é
tarefa essencial. Abaixo eu listo algumas sugestões que podem ajudar:

WKWebView – Recentemente o time do Ionic tornou possível rodar os aplicativos iOS


utilizando o browser WKWebView (evolução do antigo browser UIWebView). Este novo
engine oferece aos aplicativos iOS um ganho de performance muito superior ao antigo
browser, principalmente na experiência com listas. Para instalar o plugin, digite: ionic
plugin add https://github.com/driftyco/cordova-plugin-wkwebview-engine.git --save

Crosswalk – Como o Android tem algumas limitações de desempenho que podem ser
encontradas em alguns devices (principalmente nos antigos devido às várias versões de
sistema existentes), ca difícil garantir que o aplicativo irá rodar exatamente da maneira
como queremos. O Crosswalk é um browser moderno que é empacotado junto com o seu
app no momento que você faz o build para Android. Isso signi ca que, independente do
device do usuário, ele estará executando o app no Crosswalk. O ganho de performance é
visível mas ele pode aumentar o tamanho nal da sua aplicação. Para instalar, digite: ionic
plugin add cordova-plugin-crosswalk-webview

Virtual Scroll – O nosso aplicativo pode adicionar novos itens à lista de várias maneiras.
Isso signi ca que, quanto mais a lista aumenta de tamanho, mais itens precisarão ser
renderizados, o que irá consumir muita memória e impactar o desempenho geral. O Virtual
Scroll foi criado com o intuito de minimizar este impacto, uma vez que ele apenas
renderiza uma quantidade “x” de células na tela, su cientes para preenche-la. Dessa forma
elas podem ser reutilizadas, o que evita uma sobrecarga de memória (comportamento
muito similar ao de uma ListView no iOS). Para entender melhor sobre como utilizar o
Virtual Scroll, visite este link.

Considerações nais
Sim, é um post gigantesco. Mas a minha meta ao escrevê-lo era gerar o máximo de valor para pro ssionais
que ainda não tiveram contato com o Ionic 2 ou aqueles que desejam entender melhor como ele funciona,
uma vez que somos carentes de tutoriais mais densos escritos sobre o assunto em português.

Seja você um desenvolvedor, gerente de produto ou CIO de uma empresa, é muito importante compreender Feedback
que ainda é difícil nos dias de hoje suportar a grande diversidade de aparelhos e plataformas existentes em
um ecossistema que vive em constante mudança. Os custos para manter uma equipe multidisciplinar
sempre atualizada (e com boa sinergia) é altíssimo e isso se re ete no orçamento repassado para o cliente. 



Optar pelo desenvolvimento de aplicativos híbridos é, antes de mais nada, uma opção estratégica que deve
ser avaliada de acordo com o contexto de cada projeto. Muitas das vezes os argumentos à favor do
desenvolvimento nativo não se justi cam, principalmente se o projeto não demanda um frame rate muito alto
(como aplicativos com muitas animações, transições customizadas ou jogos).

Outro ponto que precisa ser esclarecido é que o Ionic tem um papel importantíssimo no que se refere à uma
fase que é tão ou mais importante que o desenvolvimento em si: A prototipação. Poder validar o produto com
o cliente ainda em fase inicial é um grande diferencial. Algo que lhe confere uma posição de destaque em um
mercado extremamente competitivo.
Conclusão
Há muito espaço para melhorias e recursos adicionais que podem ser implementados no aplicativo que
criamos. Caso tope desa os, você pode tentar os seguintes:

Incluir data de publicação do post na lista;

Incluir recurso de navegação entre telas (deixei o NavController lá de propósito);

Oferecer opção de alterar url para exibir resultados de um determinado subreddit;

Considerar cenários onde o usuário pode perder a conexão com a internet;

Opção de utilizar algum recurso nativo do smartphone (ex: Câmera).

Para facilitar o seu aprendizado, o projeto está disponível no GitHub separado por branches. Por exemplo,
caso você queira ter acesso ao passo 4 do tutorial, baixa digitar no terminal git checkout step4 e o código
fonte referente à este passo estará disponível.

''
Bons estudos e até a próxima!

Se você ficou curioso sobre a criação de aplicativos


multiplataforma que utilizam tecnologia da web, saiba que
o Ionic não é a única opção existente. Leia meu post sobre
React Native e descubra como já é possível criar uma
aplicação 100% nativa utilizando JavaScript. Feedback





Leia mais aqui no Tableless:
Introdução ao Ionic Framework

React Native: Construa aplicações móveis nativas com JavaScript

Tutorial Ionic – Meu primeiro app


32 Comentários Tableless 
1 Entrar

Ordenar por Mais recentes


 Recomendar 1 ⤤ Compartilhar

Participe da discussão...

FAZER LOGIN COM


OU REGISTRE-SE NO DISQUS ?

Nome

PH chaves • 5 meses atrás


Estou tentando resolver a parte do filtro, mas só aparece a seguinte mensagem:
TypeError: Unable to get property 'filter' of undefined or null reference
Alguém sabe o que poderia estar ocorrendo?
△ ▽ • Responder • Compartilhar ›

Leandro Rodrigues > PH chaves • 5 meses atrás


adicione:
this.noFilter = this.feeds;

ao método fetchContent() antes de loading.dismiss();

O autor esqueceu de mencionar essa etapa, mas está no código.


△ ▽ • Responder • Compartilhar ›
Feedback
Ueslei Ramos • 5 meses atrás
Não estou conseguindo seguir com o passo: 5.2 – Executando testes nas plataformas
Alguém conseguiu simular no iOS?
△ ▽ • Responder • Compartilhar › 


PH chaves > Ueslei Ramos • 5 meses atrás


Eu nem tentei, pelo que entendi pra poder usar o iOS, é necessário ter um iphone, caso não
tenha, passa pro passo seguinte e toca a vida.
△ ▽ • Responder • Compartilhar ›

Francisco Vieira • 6 meses atrás


O meu método para abrir o link no navegador está assim, mas no Android tá parando a aplicação,
mesmo assim ele abre o navegador com o link:

itemSelected(url: string): void {


let browser = this.inAppBrowser.create(url, '_system');
browser.show();
};

Por que será que ele pára a aplicação?


△ ▽ • Responder • Compartilhar ›

Francisco Vieira • 6 meses atrás


Cara, muito bom seu tutorial, eu sempre fiz tudo no Ionic 1, sempre tive meio "preguiça" de começar
com o 2, mas achei muito bom o tutorial, deu praticamente tudo certo, exceto os filtros.
Não funcionou filtrar por nenhuma opção, mas depois vejo isso, no geral deu tudo certo.
△ ▽ • Responder • Compartilhar ›

Rogério Ferreira • 7 meses atrás


Hoje consegui terminar o tutorial, foi muito enriquecedor, agradeço ao autor do post...
△ ▽ • Responder • Compartilhar ›

Gadiel Kaleb • 7 meses atrás


resolvi estava dando erro na linha
<ion-item *ngfor="let feed of feeds" (click)="itemSelected(feed.data.url)">

no tutorial esta com aspas dupla e troquei para aspas simples e funcionou tudo ok
△ ▽ • Responder • Compartilhar ›

Gadiel Kaleb • 7 meses atrás


dentro do passo 2 ele carrega o serve mas a pagina fica toda em branco
△ ▽ • Responder • Compartilhar ›

Iohan Gomes Pierdoná • 7 meses atrás


Estou recebendo o erro: Cannot find module "ionic-native".
O plugin foi instalado corretamente e a importação está correta.
Alguem teve o mesmo problema?
4△ ▽ • Responder • Compartilhar ›

Allan Alcantara • 7 meses atrás


Pessoal, no passo 5 ao adicionar o código que conserta os posts sem imagem, eu recebo um erro,
dizendo que a propriedade não existe no tipo string, alguém conseguiu passar dessa parte? Não
entendi muito bem esse erro. Antes de adicionar esse trecho, não havia erro nenhum. Feedback




△ ▽ • Responder • Compartilhar ›

Yuri Silva > Allan Alcantara • 7 meses atrás


Tive esse mesmo problema. Pra resolver basta alterar a declaração do array para o tipo any.
Onde tem: "public feeds: Array<string>;"
Altere para: "public feeds: Array<any>;"
△ ▽ • Responder • Compartilhar ›
Allan Alcantara > Yuri Silva • 7 meses atrás
Muito obrigado Yuri, realmente funciona. Como eu não pensei nisso antes? xD
△ ▽ • Responder • Compartilhar ›

Rogério Ferreira • 8 meses atrás


Esta linha não está funcionando:

let paramsUrl = (this.feeds.length > 0) ? this.feeds[this.feeds.length - 1].data.name : "";

Não encontra o objeto data no array de feeds. Alguém sabe o que é ou conseguiu retornar o atributo
name de outra forma?
△ ▽ • Responder • Compartilhar ›

Francisco Vieira > Rogério Ferreira • 6 meses atrás


Onde tem:
public feeds: Array<string>;

Altere para:
public feeds: Array<any>;
△ ▽ • Responder • Compartilhar ›

Rogério Ferreira • 8 meses atrás


No passo 5 o forEach não funcionou, fiz assim:

fetchContent ():void {
let loading = this.loadingCtrl.create({
content: 'Fetching content...'
});

loading.present();

this.http.get(this.url).map(res => res.json())


.subscribe(data => {
for(let e of data.data.children){
if (!e.data.thumbnail || e.data.thumbnail.indexOf('b.thumbs.redditmedia.com') === -1 ) {
e.data.thumbnail = 'http://www.redditstatic.com...
} Feedback
}
this.feeds = data.data.children;
loading.dismiss();
}); 



}
△ ▽ • Responder • Compartilhar ›

Flavio Renato • 8 meses atrás


Demais. Demais!!! Adorei esse tutorial. mais explicadinho doq ue isso é IMPOSSÍVEL!
Adorei a sua didática. Vou fazer ele para pegas as manhas de Angilar!
△ ▽ • Responder • Compartilhar ›

Lucimar Leandro Bezerra • 8 meses atrás


Parabéns Carlos pelo tutorial, é extenso porém muito bem explicado. Obrigado por ceder o seu
tempo para transmitir conhecimentos para outras pessoas.
△ ▽ • Responder • Compartilhar ›
Pablo Fior • 8 meses atrás
Tutorial copiado de:
https://www.joshmorony.com/...

para q o servico http funcione voce precisa incluir o httpmodule em app.module.ts, assin como
descrito no tutorial original
acredito q na hora de copiar e traduzir o autor do tableless pulou essa parte
1△ ▽ • Responder • Compartilhar ›

Flavio Renato > Pablo Fior • 8 meses atrás


Sacanagem dizer isso. Eu li o tutorial gringo e não tem a ver em nada com este. Só porque
cita o Fetch Remote Data? A vantagem é que ao querer menosprezar o artigo do Carlos. eu
acabei vendo pór lá a parte de "Pulling in Data From Your Own Server" onde ensina a
conectar e extrair dados de sua própria base de dados, via PHP e MySQL
△ ▽ • Responder • Compartilhar ›

Carlos > Pablo Fior • 8 meses atrás


"Tutorial copiado", Pablo? Escrevi este post na época com o cuidado de introduzir de
maneira simples o Ionic 2 para a comunidade, antes até mesmo do lançamento da versão
final. Várias modificações ocorreram na plataforma desde então, principalmente devido às
evoluções no Angular 2. Foram dias de trabalho onde tentei documentar um passo à passo
do processo em um repositório no Github. Peço que tenha um pouco mais de bom senso na
hora de fazer esses julgamentos porque, além de ferir o trabalho do profissional que
escreveu, atinge também a credibilidade do site, que até hoje é visto como referência para a
comunidade web no Brasil. Abraços.
2△ ▽ • Responder • Compartilhar ›

José Antônio Pires > Carlos • 8 meses atrás


Carlos, você está de parabéns pelo belo trabalho. Pessoas como você são preciosas,
pois dedicam seu tempo e esforço para ajudar os outros. Parabéns!
2△ ▽ • Responder • Compartilhar ›

Carlos > José Antônio Pires • 8 meses atrás


Obrigado, José. Infelizmente algumas pessoas da comunidade fazem questão
de menosprezar o trabalho alheio. Abraços.
△ ▽ • Responder • Compartilhar ›
Feedback

Vitor Figueredo • 8 meses atrás


Olá, estou recebendo este erro. Alguém poderia me ajudar? 



Agradeço.
2△ ▽ • Responder • Compartilhar ›

Renan Viana Marcelino > Vitor Figueredo • 8 meses atrás


No app.module.ts precisa adicionar:

import { HttpModule } from '@angular/http';

e depois

imports: [
BrowserModule,
HttpModule,
IonicModule.forRoot(MyApp)
]
4△ ▽ • Responder • Compartilhar ›

Danrley Scharf > Renan Viana Marcelino • 7 meses atrás


Tbm tive esse problema, e com sua solução consegui... Obrigado Renan! abraço!
△ ▽ • Responder • Compartilhar ›

Nelson Oliveira > Renan Viana Marcelino • 8 meses atrás


Runtime Error
Cannot find module "ionic-native"

tenho esse erro .. alguma ajuda com o InAppBrowser


△ ▽ • Responder • Compartilhar ›

Rogério Ferreira > Nelson Oliveira • 8 meses atrás


Fiz as seguintes alterações e funcionou:

Dentro da pasta do projeto abre o cmd e digita:

ionic plugin add --save cordova-plugin-inappbrowser


Feedback

npm install --save @ionic-native/in-app-browser

No home.ts coloca: 


import { InAppBrowser } from '@ionic-native/in-app-browser';

e altera o método itemSelected para:

itemSelected (url: string):void {


let browser = new InAppBrowser();
browser.create(url, '_system');
}
3△ ▽ • Responder • Compartilhar ›

Ueslei Ramos > Rogério Ferreira • 5 meses atrás


Muito obrigado!
Seguindo os passos, funcionou
1△ ▽ • Responder • Compartilhar ›

Nelson Oliveira > Rogério Ferreira • 7 meses atrás


valeu pela dica cara .. funcionou no browser .. ainda nao testei nos devices..

Mais uma vez tanks.


1△ ▽ • Responder • Compartilhar ›

Luiz Antonio Leão > Vitor Figueredo • 8 meses atrás


Tive o mesmo problema. A partir desse ponto não consegui mais acompanhar. Uma pena.
△ ▽ • Responder • Compartilhar ›

Marcelo Silva Silva > Luiz Antonio Leão • 3 meses atrás


Também tive o mesmo problema. Mas não desisti não! Pulei a etapa do plugin e estou
seguindo em frente. É bem normal acontecer isto, até porque, tanto o Ionic quanto o
Angular, recebem atualizações periódicas. Parabéns e muito obrigado, Carlos!
△ ▽ • Responder • Compartilhar ›

Sponsored Links

Dad Donates Kidney to Save Daughter's Life, Then Doctors Warn About Son
LifeDaily.com

Novo drone portátil assusta…


SelfieDrone

Father and Son Take the Same Photo For 27 Years! Don't Cry When You See The Last One!
Womens24x7

15 Foods to NEVER Keep In The Fridge


HealtyLeo.com

Feedback

They Took The Same Picture For 40 Years. Don't Cry When You See The Last!
TopGentlemen.com




The Most Beautiful Places To Relax In Panama


Rest Period
Entre na nossa TinyLetter:
Qual o seu e-mail?
Feedback

Inscrever-se




Últimos posts
Dicas de front-end para designers

Salesforce Lightning Design System

Utilizando Fragment no React

Como implementar o Blockchain em


JavaScript

Ember.js: Deploy no Heroku


Categorias
tecnologia-e-tendências (329)

geral (322)

artigos (319)

técnicas-e-práticas (288)

código (244)

javascript (212)

browsers (206)

Feedback





SOBRE
SOBRE O TABLELESS

CONTATO

ANUNCIE

SEJA UM AUTOR

FAZEMOS CÓDIGO FRONT-END

ACOMPANHE
WEBINARS

FÓRUM

CANAL NO MEDIUM

CANAL NO TELEGRAM

COMUNIDADE
FEMUG

MEETUPCSS

PODCAST ZOFE

BRAZILJS Feedback

DEVNAESTRADA

FRONT-END BRASIL



Escrito pela e para a comunidade web brasileira. Ajude

Potrebbero piacerti anche