chevron-left chevron-right

[JS] Tworzymy aplikację JS z wykorzystaniem Backbone.js, część 4 – wysyłanie wiadomości

W dzisiejszym odcinku serii, skupimy się na wysyłaniu wiadomości do sieci społecznościowych Twittera i Facebooka. Ze względu na to, że w jednym miejscu będziemy wykorzystywać dwa różne API, to taka sytuacja będzie wymagała odpowiedniego podejścia do sprawy czyli odpowiedniego przygotowania struktury wiadomości, którą wysyłamy do sieci.

Uwaga z 2026 roku. Ten wpis powstał w listopadzie 2013 roku, gdy obowiązującym standardem języka był ECMAScript 5 — bez klas, modułów, funkcji strzałkowych i obietnic, które dziś uchodzą za oczywistość. Kod celowo pozostaje w konwencji swoich czasów. Opisane wersje API Twittera i Facebooka od dawna nie istnieją, więc traktuj ten tekst jako zapis ówczesnego podejścia do budowy aplikacji, a nie jako instrukcję do wdrożenia. Poprawione zostały wyłącznie błędy rzeczowe oraz fragmenty kodu uszkodzone przez wtyczkę działającą na blogu.

Aplikacja społecznościowa - formularz wiadomości

Zaczniemy od utworzenia kodu HTML dla formularza wysyłkowego wiadomości. Dla uproszczenia, zostaną tam wykorzystane identyfikatory pól. Kod wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// js/app/template/messageForm.js
<form action="#" class="message form">
  <ul>
    <li class="message">
      <textarea name="social-message" id="social-message" cols="30" rows="10" placeholder="Treść wiadomości"></textarea>
    </li>
    <li class="link url">
      <input type="url" name="social-link" id="social-link" placeholder="Adres linka">
    </li>
    <li class="link title">
      <input type="text" name="social-title" id="social-title" placeholder="Tytuł linka">
    </li>
    <li class="link description">
      <input type="text" name="social-description" id="social-description" placeholder="Opis linka">
    </li>
    <li class="word-counter">
      <p><span>Wykorzystano znaków: </span><strong></strong></p>
    </li>
    <li class="row-fluid checkboxes">
      <label for="twitter" class="span2">Twitter</label><input type="checkbox" name="twitter" class="span1" id="checkbox-twitter"/>
      <label for="facebook" class="span2">Facebook</label><input type="checkbox" name="facebook" class="span1" id="checkbox-facebook"/>
    </li>
    <li class="buttons">
      <button class="submit btn positive">Wyślij wiadomość</button>
      <button class="clear btn tentative">Wyczyść formularz</button>
      <button class="cancel btn negative">Zaniechaj</button>
    </li>
  </ul>
</form>

Jak widać zdefiniowano 4 pola tekstowe, 2 checkboxy oraz 3 przyciski. Dla naszych potrzeb jest to zdecydowanie wystarczające. Oczywiście, można taki formularz rozszerzyć poprzez dodanie do pól tekstowych reguł walidacyjnych w HTML5 czy też opisów WAI-ARIA.

Licznik nieprzypadkowo zlicza znaki, a nie słowa. Twitter ogranicza pojedynczy status do 140 znaków i wlicza w to również adres odnośnika, który doklejamy do treści wiadomości. Po przekroczeniu limitu API po prostu odrzuci żądanie, dlatego w wersji produkcyjnej długość trzeba sprawdzić przed wysłaniem, a nie dopiero reagować na błąd. Facebook takiego ograniczenia nie ma, więc ten sam formularz zachowuje się inaczej w zależności od zaznaczonej sieci.

Backbone.js - Wysyłanie wiadomości do sieci społecznościowej

Mając przygotowany szablon HTML dla formularza wysyłkowego, możemy przystąpić do tworzenia kodu JS dla widoku odpowiedzialnego za obsługę formularza. Dla ułatwienia, kod został skomentowany przeze mnie za pomocą komentarzy w kodzie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// js/app/view/messageForm.js
(function () {
  'use strict';
 
  APP.view = APP.view || {};
 
  APP.view.MessageForm = Backbone.View.extend({
    el            : $('body').find('main'),
    container     : $('<div/>').prop({ id : 'message-form' }),
    events        : {
      'click.APP .submit' : 'submit',
      'click.APP .cancel' : 'cancel',
      'click.APP .clear'  : 'clear'
    },
    initialize : function () {
      this.render();
    },
    render : function () {
      console.info('Loading MessageForm view');
      // ładujemy szablon HTML dla widoku formularza
      var form          = APP.loadTemplate('messageForm', {});
      // usuwamy odwołanie do starego, aktywnego widoku
      APP.App.destroyActiveView(this.container);
      // wstawiamy wygenerowany kod HTML formularza do kontenera
      this.container.empty().append(form);
      // ustawiamy odwołanie do nowego, aktywnego widoku
      APP.App.setActiveView(this);
      // wstawiamy kontener z formularzem do widoku
      this.$el.append(this.container);
    },
    submit : function (event) {
      event.preventDefault();
 
      var that          = this;
      // znajdujemy wszystkie zaznaczone checkboxy
      var checkboxes    = this.$el.find('.checkboxes').find('input[type=checkbox]:checked');
      // funkcja, która wyświetli wiadomość w oknie aplikacji
      // o tym czy wysyłka wiadomości do sieci społecznościowych
      // zakończyła się sukcesem czy nie
      var createMessage = function (text, type) {
        console.info('Creating message. Type: ', type);
        var alertMessage = $('<div/>').prop({
          className : type === 'success' ? 'alert alert-success' : 'alert alert-error'
        }).html(text);
        that.container.prepend(alertMessage);
        // po 5 sekundach zakończ wyświetlanie wiadomości
        setTimeout(function () {
          that.container.find('.alert').remove();
        }, 5000);
      };
 
      // jeśli jest jakikolwiek checkbox zaznaczony
      if (checkboxes.length) {
        // dla każdego z nich
        _.each(checkboxes, function (checkbox) {
          // pobierz nazwę sieci społecznościowej
          var socialName = $(checkbox).prop('name');
          // jeśli jest to Twitter
          if (socialName === 'twitter') {
            // przygotuj treść statusu na Twittera
            // pobierz wiadomość oraz zawartość pola z linkiem
            var params = {
              status : that.container.find('#social-message').val() + ' ' + that.container.find('#social-link').val()
            };
            // wyślij status do Twittera
            APP.twitter.__call('statuses_update', params, function (reply) {
              // jeśli zakńczony sukcesem
              if (reply.httpstatus === 200) {
                createMessage('Twitter: Message was sent successfully', 'success');
              } else { // jeśli jest błąd to go wyświetl w konsoli
                createMessage('Twitter: Sending message failed. HTTP Status: ' + reply.httpstatus, 'error');
              }
            });
          } else if (socialName === 'facebook') { // jeśli jest to Facebook
            // id profilu lub fanpage'a na Facebooku
            var profilID    = '15XXXXXXXXXXXXX';     
            // link do grafu wybranego profilu     
            var graphURI    = '/' + profilID + '/feed'; 
            // przygotowujemy parametry bazowe z treścią wiadomości
            var parameters  = {
              message : that.container.find('#social-message').val()
            };
 
            // jeśli wszystkie pola związane z linkiem (adres URL, tytuł i opis linka) nie są puste
            if (!_.isEmpty(that.container.find('#social-title').val()) && !_.isEmpty(that.container.find('#social-link').val()) && !_.isEmpty(that.container.find('#social-description').val())) {
              parameters.name         = that.container.find('#social-title').val();
              parameters.link         = that.container.find('#social-link').val();
              parameters.description  = that.container.find('#social-description').val();
              // Uwaga: pola "type" i "status_type" Graph API zwraca wyłącznie
              // do odczytu - wysłanie ich przy publikacji niczego nie zmienia.
              // Aby post pokazał podgląd odnośnika, w zupełności wystarczy
              // parametr "link" przekazany do /feed. Osobny endpoint /links
              // nie jest do tego potrzebny.
            }
            // wysyłamy wiadomość do Facebooka
            APP.facebook.api(graphURI, 'post', parameters, function (response) {
              console.info('Facebook submit response:', response);
              // jeśli w odpowiedzi jest informacja o błędzie
              if (response.error) {
                createMessage('Facebook: Sending messsage failed', 'error');
              } else { // jeśli nie
                createMessage('Facebook: Message was sent successfully', 'success');
                that.$el.find(':input').val('');
              }
            });
          }
        });
      }
    },
    // funkcja obsługująca zdarzenie zaniechania wysłania wiadomości
    // wyświetli wtedy widok z Twitterem
    cancel : function (event) {
      event.preventDefault();
      APP.App.navigation.showTwitter();
    },
    // funkcja czyszcząca wszystkie pola tekstowe
    clear  : function (event) {
      event.preventDefault();
      this.$el.find(':input').val('');
    }
  });
})();

W powyższym kawałku kodu zdefiniowaliśmy obsługę kliknięć dla 3-ch różnych typów zdarzeń: submit, cancel i clear. Aplikacja rozpoznaje do której sieci społecznościowej ma wysłać nową informację poprzez wykrywanie zaznaczenia checkboxa wraz z przypisaną do niego siecią społecznościową, a następnie wykorzystanie odpowiedniego API do wysyłki wiadomości.

Należy pamiętać o tym, że Twitter nie obsługuje aż tak dużej liczby parametrów wiadomości jak Facebook ze względu na to, że zupełnie odmiennie są przedstawiane wiadomości w obydwu sieciach. Facebook stawia na pełną dokumentację linków, a Twitter stawia na prostotę.

Podsumowanie

To w zasadzie jest wszystko. Należy jednak pamiętać, aby kod w wersji produkcyjnej posiadał odpowiednią walidację danych wpisywanych przez użytkownika, dzięki czemu zabezpieczycie aplikację przed wysyłaniem szkodliwego kodu gdziekolwiek (do bazy danych, do innego użytkownika, itp.).

Planowałem jeszcze osobne części o routingu wewnątrz aplikacji i o jej optymalizacji — ostatecznie nie powstały, więc seria kończy się w tym miejscu. Odwołania do APP.App.navigation w powyższym kodzie pochodzą właśnie z tej niezrealizowanej części; w działającej aplikacji odpowiada za nie router Backbone.js.

Inne części z serii

  1. Część 1 – Tworzymy aplikację JS z wykorzystaniem Backbone.js - Zapoznanie z frameworkiem
  2. Część 2 - Tworzymy aplikację JS z wykorzystaniem Backbone.js – Integracja z Twitterem
  3. Część 3 - Tworzymy aplikację JS z wykorzystaniem Backbone.js – Integracja z Facebookiem