Good Ideas

Creare un’App “News Reader” con Cordova, AngularJs e Twitter Bootstrap

Posted by:

|

On:

|

In questo periodo mi sto dilettando con la creazione di App per mobile, dopo aver scoperto Cordova (alias PhoneGap), un fantastico framework che consente di sviluppare un’App multipiattaforma (iOS, Android, Blackberry, Windows Phone, Palm WebOS, Bada e Symbian) utilizzando tecnologie web.

magapp

In questo articolo vi mostro quanto sia semplice creare un News reader, con lista di notizie, dettaglio e swipe tra le notizie (scorrimento orizzontale). Il codice completo dell’applicazione è stato messo a disposizione su questo repository GitHub.

Installazione Cordova e AngularJS

Inutile ripetere le solite informazioni, quindi vi rimando a questo utilissimo articolo di Will Vaughn “AngularJS, Phonegap, and angular-seed. Let’s Go!” che vi spiega passo dopo passo come installare Cordova e integrarlo con AngularJs.

Quando avrete pronta la cartella www con il vostro index.html potrete iniziare a creare il news reader.

Librerie necessarie

Per facilitare la creazione del front-end ho deciso di utilizzare Twitter Bootstrap. Per questo ci viene in aiuto una libreria di direttive per AngularJs che va installata e configurata nel progetto.

Dopo aver copiato i file necessari (ui-bootstrap-tpls-0.10.0.min.js) nella cartella lib/, modificate index.html aggiungendo quindi la libreria in questo modo:

<script src="lib/ui-bootstrap-tpls-0.10.0.min.js"></script>

Abilitate le direttive Bootstrap aggiungedo in js/app.js la dipendenza:

angular.module('myApp', ['ui.bootstrap']);

Fate la stessa cosa anche con Angular Carousel per poter utilizzare lo swipe tra gli articoli. In index.html aggiungiamo la libreria:

<script src="lib/angular-carousel.js"></script>

e la abilitiamo su AngularJs:

angular.module('myApp', ['ui.bootstrap', 'angular-carousel']);

La pagina principale

Utilizziamo questo frammento di codice per creare l’home page

<div ng-controller="MainCntl" >
  <div data-ng-include="'partials/nav.html'"></div>
  <div class="container">
    <div class="view-animate-container">
      <div ng-view ></div>
    </div>

  </div>
</div>

Facendo uso dei partial possiamo creare l’intera applicazione. In particolare sono stati definiti 3 file in partials/ che definiscono:

  • nav.html per la barra di navigazione
  • articles.html per la lista degli articoli
  • post.html per il dettaglio dell’articolo

Articles.html viene visualizzato nell’home page (partials/articles.html). La parte principale è composta da un ng-repeat che cicla sugli articoli a disposizione.

<div ng-init="init()">

<div class="row" id="articles">
<div class="col-xs-12 col-md-4" ng-repeat="item in items">
<div class="post" ng-click="go('/articles/'+item.id)" style="background-image: url({{ item.info1.fullimage.url }})">

<div class="caption">
<h3>{{item.title}}</h3>
<p ng-bind-html="item.description"></p>
<p>
<a class="btn btn-primary" role="button" ng-click="go('/articles/'+item.id)">Leggi</a>
</p>
</div>

</div>
</div>
</div>

</div>

Il singolo articolo è invece visualizzato utilizzando il file partials/post.html :

<ul rn-carousel rn-carousel-buffered rn-carousel-indicator rn-carousel-index="slideIndex">
<li ng-repeat="post in items">
<div class="page">
<h2>{{post.title}}</h2>

<a class="thumbnail" ng-click="go('/articles/'+item.$$hashKey)" ng-if="post.info1.fullimage.url">
<img ng-src="{{ post.info1.fullimage.url }}" alt="{{post.title}}" class="img-responsive">
</a>
<p ng-bind-html="post.description"></p>
carouselIndex: {{ carouselIndex }}
<br>carouselBufferIndex: {{ carouselBufferIndex }}
<br>
</div>
</li>
</ul>

<div class="controls ng-cloak text-center">
<a class="btn btn-primary" ng-disabled="slideIndex==0" ng-click="prev()">prev</a>
<span>{{ slideIndex + 1 }} / {{ items.length }}</span>
<a class="btn btn-primary" ng-click="next()" ng-disabled="slideIndex==items.length-1">next</a>
<a class="btn btn-primary" ng-click="pushSlide()">add 3 slides</a>
</div>

Si possono notare le classi rn-carousel che attivano il carousel tra le notizie e quindi la possibilità di scorrerle orizzatalmente attraverso il display touch tipico degli smartphone.

I controller

Sono presenti 2 controller per gestire le due parti dell’applicazione: la lista degli articoli (ArticlesCntl) e il dettaglio (PostCnts). Sono davvero semplici:

angular.module('App.controller.articles', [ ])

    .controller('ArticlesCntl', ['$rootScope', '$routeParams', 'Repository', function ( $scope, $routeParams, Repository ) {
            this.params = $routeParams;

            $scope.init = function () {
                $scope.items = Repository.data;
            }

    }])

angular.module('App.controller.post', [])

.controller('PostCntl', ['$scope', '$routeParams', '$location', 'Repository',
    function ($scope, $routeParams, $location, Repository) {

        $scope.params = $routeParams;
        $scope.items = Repository.data;
        $scope.post = Repository.getPost($routeParams.articleId);
        $scope.slideIndex = Repository.getPostIndex($routeParams.articleId);

        $scope.$watch('slideIndex', function(newValue, oldValue) {
            console.log(newValue, oldValue, $scope.items.length);
            if (newValue >= $scope.items.length -1 ) {
                console.log("siamo alla fine!!!!");
            }
           });

        $scope.pushSlide = function() {
        //    addSlides($scope.slides4, 'people', 3);
        }

        $scope.prev = function() {
            console.log("prev");
            $scope.slideIndex-=1;
        }

        $scope.next = function() {
            console.log("next");
            $scope.slideIndex+=1;
        }

    }]);

Nel controller PostCntl sono stati sviluppati i metodi per gestire un pannello di controllo per saltare da un post all’altro. Inoltre il metodo $watch sullo slideIndex è stato predisposto per il caricamento di ulteriori post (non ancora peraltro sviluppato).

Il servizio di recupero delle News

Il cuore dell’applicazione risiede nel servizio chiamato “repository” che si occupa di scaricare dalla rete le news e di metterle a disposizione dell’app. In questo esempio si fa uso delle Yahoo pipes per trasformare il feed RSS del Corriere.it in un formato Json.
Questo un estratto del servizio

          data: [], 
                status: '',
                method: 'GET',
                url: 'http://pipes.yahoo.com/pipes/pipe.run?_id=2FV68p9G3BGVbc7IdLq02Q&_render=json&feedcount=10&feedurl=http%3A%2F%2Fxml.corriereobjects.it%2Frss%2Fhomepage.xml',

                emptyData: function () {
                    this.data = [];
                },
                fetchData: function() {
                    code = null;
                    response = null;
                    self = this;
                    $http({method: this.method, url: this.url}).
                    success(function(data, status) {
                        self.status = status;
                        //angular.copy( data.value.items, self.data );
                        parser = document.createElement('a');
                        for (var i = 0; i < data.value.items.length; i++) {
                            self.data.push(data.value.items[i]);
                            parser.href = self.data[i].guid.content;
                            self.data[i].id = md5.createHash( data.value.items[i].title || '') //data.value.items[i].$hashKey;// parser.pathname;
                        }
                    }).
                    error(function(data, status) {
                        self.data = data || "Request failed";
                        self.status = status;
                        alert("fail");
                    });
                }, ...

Il metodo fetchData si occupa di fare una richiesta http all’indirizzo indicato dalla varibile url e di parserizzare le informazioni ottenute aggiungendo un ID per poter avere un riferimento interno delle notizie.

Codice e conclusioni

Ho messo a disposizione un repository GitHub con tutto il codice dell’app pronto all’uso. Si tratta di un piccolo progetto, non finito ma utile per poterlo utilizzare come punto di partenza per ulteriori sviluppi.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *