ddf-ng-timeline: Come trasformare una timeline in una direttiva angularjs

Qualche giorno fa avevo bisogno di implementare una timeline, che mi permettesse di tener traccia degli aggiornamenti di un utente. Volevo ottenere un effetto che permettesse di separare in modo chiaro i post inseriti dall’utente e le eventuali risposte ottenute. Dopo aver passato diverso tempo a googlare ho trovato questo splendido codice creato da Sergiors che vorrei ringraziare.

Sono partito dalla versione originale della timeline e l’ho trasformata nella sua versione Angular.

<ul class="timeline">
        <li>
          <div class="timeline-badge"><i class="glyphicon glyphicon-check"></i></div>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
              <p><small class="text-muted"><i class="glyphicon glyphicon-time"></i> 11 hours ago via Twitter</small></p>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
            </div>
          </div>
        </li>
        <li class="timeline-inverted">
          <div class="timeline-badge warning"><i class="glyphicon glyphicon-credit-card"></i></div>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
              <p>Suco de cevadiss, é um leite divinis, qui tem lupuliz, matis, aguis e fermentis. Interagi no mé, cursus quis, vehicula ac nisi. Aenean vel dui dui. Nullam leo erat, aliquet quis tempus a, posuere ut mi. Ut scelerisque neque et turpis posuere pulvinar pellentesque nibh ullamcorper. Pharetra in mattis molestie, volutpat elementum justo. Aenean ut ante turpis. Pellentesque laoreet mé vel lectus scelerisque interdum cursus velit auctor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ac mauris lectus, non scelerisque augue. Aenean justo massa.</p>
            </div>
          </div>
        </li>
        <li>
          <div class="timeline-badge danger"><i class="glyphicon glyphicon-credit-card"></i></div>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
            </div>
          </div>
        </li>
        <li class="timeline-inverted">
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
            </div>
          </div>
        </li>
        <li>
          <div class="timeline-badge info"><i class="glyphicon glyphicon-floppy-disk"></i></div>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
              <hr>
              <div class="btn-group">
                <button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-toggle="dropdown">
                  <i class="glyphicon glyphicon-cog"></i> <span class="caret"></span>
                </button>
                <ul class="dropdown-menu" role="menu">
                  <li><a href="#">Action</a></li>
                  <li><a href="#">Another action</a></li>
                  <li><a href="#">Something else here</a></li>
                  <li class="divider"></li>
                  <li><a href="#">Separated link</a></li>
                </ul>
              </div>
            </div>
          </div>
        </li>
        <li>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
            </div>
          </div>
        </li>
        <li class="timeline-inverted">
          <div class="timeline-badge success"><i class="glyphicon glyphicon-thumbs-up"></i></div>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">Mussum ipsum cacilds</h4>
            </div>
            <div class="timeline-body">
              <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
            </div>
          </div>
        </li>
    </ul>

Partiamo con l’analisi del problema.

  • Primo step: suddividere la timeline in piccoli pezzi di codice che si ripetono.
            <li>
              <div class="timeline-badge"><i class="glyphicon glyphicon-check"></i></div>
              <div class="timeline-panel">
                <div class="timeline-heading">
                  <h4 class="timeline-title">Mussum ipsum cacilds</h4>
                  <p><small class="text-muted"><i class="glyphicon glyphicon-time"></i> 11 hours ago via Twitter</small></p>
                </div>
                <div class="timeline-body">
                  <p>Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis.</p>
                </div>
              </div>
            </li>
    

    Il codice presentato poco sopra l’ho inserito, con le modifiche che vedremo più avanti, dentro un template di nome timelineEntry.html e l’ho utilizzato all’interno della direttiva (come vedremo più avanti).

  • Creazione di una direttiva che gestisca il pezzo di codice appena creato.
    ngTimelineDir.directive( 'ddfTimelineEntry', function ( $compile ) {
      return {
        restrict: 'EA',
        replace: 'true',
        templateUrl: 'partials/timeLineEntry.html',
        link: function ( scope, element, attrs ) {
            if(attrs.response=='true') {
                element.addClass('timeline-inverted');
            }
        }
      }
    });
    

    La direttiva si chiama ddfTimeLineEntry ed in questo modo, possiamo utilizzarla come elemento nella VIEW, scrivendo ddf-timeline-entry. Vincoliamo, poi, la direttiva all’utilizzo come elemento Element=E o attributo Attribute=A, il che significa che possiamo usare la direttiva come elemento HTML o all’interno di un altro elemento definendolo come attributo. Il parametro replace:true significa che nel codice dell’HTML risultante vedremo il codice inserito all’interno del template e non la definizione della direttiva stessa. Passiamo, quindi, al parametro templateUrl che, come dice il nome, rappresenta l’Url al codice Html che verrà utilizzato dalla nostra direttiva come template. La funzione link definisce la funzione che verrà chiamata da angular nella fase di link del suo ciclo di vita (esiste anche la fase di compile). In questo caso, nella funzione, che accetta tre parametri, il cui ordine è importante e dove, scope != $scope rappresenta lo scope definito nella direttiva (in questo caso vuoto). Facciamo un esempio:

    Controller:
    ...
    $scope.someObject = { name:'Danilo', id:1 };
    ...
    
    HTML:
    <my-directive my-attribute="someObject" /> 
    
    Direttiva:
    {
      scope: {myValue: "=myAtrribute" },
      link: function (scope, iElm, iAttrs) {
        var x = scope.myValue.name;
        // x == "Danilo";
        scope.myValue.name = "Marty";
        // in this case $scope.someObject.name == "Marty";
      }
    }
    

    Per capire meglio il significato del codice all’interno della funzione, analizziamo la versione Angularizzata del frammento di codice visto poco fa:

    <li response="{{feed.entry.response}}">
      <div class="timeline-badge success"><i class="{{feed.entry.glyphicon}}"></i></div>
          <div class="timeline-panel">
            <div class="timeline-heading">
              <h4 class="timeline-title">{{feed.entry.title}}</h4>
              <p><small class="text-muted"><i class="glyphicon glyphicon-time"></i> {{feed.entry.timeago}} ago</small></p>
            </div>
            <div class="timeline-body">
              <p>{{feed.entry.text}}.</p>
            </div>
          </div>
    </li>
    

    Ogni frammento rappresenta una entry nella nostra timeline, così, per parametrizzare il suo comportamento, ho modificato alcune parti con delle variabili del model di angular, in modo da intercettare i valori delle entry provenienti dal servizio creato e che vedremo più avanti.
    Un attributo particolare, definito sull’elemento li, si chiama response che viene usato dalla direttiva, ed in particolare dalla funzione di link, per decidere se la entry che si sta processando proviene dall’utente o rappresenta una risposta proveniente da qualche altro utente. In questo modo è stato possibile definire una classe nell’elemento li che mi permettesse di cambiare il layout della entry.

  • Creare la direttiva container
    ngTimelineDir.directive('ddfTimeline', function(){
          return {
            restrict: 'EA',
            scope: false,
            replace: 'true',
            template: '<ul class="timeline"><ddf-timeline-entry ng-repeat="feed in timelineEntries"></ddf-timeline-entry></ul>'
          }  
    });
    

    Questa direttiva è molto semplice e rispetto alla precedente definisce esplicitamente che non crea un nuovo scope scope: false; e indichiamo direttamente il codice HTML che verrà utilizzato come template.

  • Definire un servizio che permette di ottenere i feed della timeline
    ngTimelineServices.service('timelineService', function(){
      var timeline = [
        {"id": "0", "entry": {
                    "glyphicon"     :  "glyphicon glyphicon-bell",
                    "response"      :  "false",
                    "title"     	:  "Mussum ipsum cacilds",
                    "timeago"     	:  "1 hour",
                    "text"     	    :  "Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis."
                }},
        {"id": "1", "entry": {
                    "glyphicon"     :  "glyphicon glyphicon-eye-open",
                    "response"      :  "true",
                    "title"     	:  "Mussum ipsum cacilds",
                    "timeago"     	:  "2 hours",
                    "text"     	    :  "Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis."
                }},
        {"id": "2", "entry": {
                    "glyphicon"     :  "glyphicon glyphicon-eye-open",
                    "response"      :  "true",
                    "title"     	:  "Mussum ipsum cacilds",
                    "timeago"     	:  "3 days",
                    "text"     	    :  "Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis."
                }},
        {"id": "3", "entry": {
                    "glyphicon"     :  "glyphicon glyphicon-bell",
                    "response"      :  "false",
                    "title"     	:  "Mussum ipsum cacilds",
                    "timeago"     	:  "4 days",
                    "text"     	    :  "Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga. Sapien in monti palavris qui num significa nadis i pareci latim. Interessantiss quisso pudia ce receita de bolis, mais bolis eu num gostis."
                }}
      ];
      
      this.getTimeline = function(){
        return timeline;
      }
      
    });
    

Potete trovare il codice completo su un mio repository GitHub.

Enjoy

RSS AngularJob

ddelfio Written by: