Błędy

9

procent przetłumaczone

W tym rozdziale dowiesz się:

  • Utworzenie lepszego mechanizmu wyświetlania błędów i komunikatów.
  • Jak używać `Template.rendered` aby rozpoznać, kiedy użytkownik zobaczył błąd.
  • Używanie filtra routera aby upewnić się, że błąd został wyświetlony tylko raz.
  • Używanie standardowego mechanizmu przeglądarki, czyli okna dialogowego alert() aby ostrzegać użytkownika o problemie z dodaniem danych wydaje się nieco niezadowalające i z pewnością nie tworzy świetnego interfejsu użytkownika. Możemy to udoskonalić.

    Zamiast tego rozwiązania, zbudujemy wszechstronny mechanizm raportowania błędów, który będzie wykonywał lepiej zadanie przekazywania błędów bez zatrzymywania pracy użytkownika.

    Wprowadzenie do lokalnych kolekcji

    Zaimplementujemy prosty system, który śledzi, które błędy użytkownik zobaczył i wyświetla nowe błędy w odpowienim miejscu “migoczących wiadomości” na stronie. Ten wzorzec UX jest użyteczny, gdy chcemy poinformować użytkownika, że coś się stało bez przerywania ich pracy.

    Utworzymy coś podobnego do migoczących powiadomień znanych z aplikakcji Ruby on Rails, ale jest bardziej subtelne, bo zaimplementowane po stronie klienta i rozpoznające kiedy użytkownik zobaczył wiadomość.

    Aby rozpocząć, utworzymy kolekcję, w której będziemy zapamiętywać błędy. Pod warunkiem, że błędy są przeznaczone tylko do bieżącej sesji i nie muszą być w żaden sposób zapisywane, zrobimy coś nowego i stworzymy lokalną kolekcję. Oznacza to, że kolekcja Errors będzie istniała tylko w przeglądarce po stronie klienta i nie będzie starała się synchronizować z serwerem.

    Aby tego dokonać, stworzymy komunikat błędu w pliku znajdującym się tylko po stronie klienta, z nazwą kolekcji ustawioną na null. Tworzymy funkcję throwError która po prostu wstawia błąd w nowo utworzoną lokalną kolekcję:

    // Local (client-only) collection
    Errors = new Meteor.Collection(null);
    
    client/helpers/errors.js

    Teraz gdy kolekcja została utworzona, możemy dodać funkcję throwError, którą będziemy wołali aby dodać do niej błędy. Nie musimy się martwić o allow czy deny czy cokolwiek podobnego, ponieważ jest to kolekcja lokalna, która nie będzie zapisywana w bazie danych Mongo.

    throwError = function(message) {
      Errors.insert({message: message})
    }
    
    client/helpers/errors.js

    Zaletą używania lokalnej kolekcji do przechowywania błędów jest to, że jak wszystkie kolekcje, jest reaktywna – oznacza to, że możemy wyświetlać błędy w ten sam sposób, co jakiekolwiek inne dane z kolekcji.

    Wyświetlanie błędów

    Zamierzamy wyświetlać błędy na samej górze strony:

    <template name="layout">
      <div class="container">
        {{> header}}
        {{> errors}}
        <div id="main" class="row-fluid">
          {{yield}}
        </div>
      </div>
    </template>
    
    client/views/application/layout.html

    Utwórzmy teraz szablony errors i error w errors.html:

    <template name="errors">
      <div class="errors row-fluid">
        {{#each errors}}
          {{> error}}
        {{/each}}
      </div>
    </template>
    
    <template name="error">
      <div class="alert alert-error">
        <button type="button" class="close" data-dismiss="alert">&times;</button>
        {{message}}
      </div>
    </template>
    
    client/views/includes/errors.html

    Bliźniacze szablony

    Zauważysz, że wrzucamy dwa szablony do jednego pliku. Do tej pory staraliśmy się stosować zasadę “jeden plik, jeden szablon”, ale jeżeli chodzi o Meteora, wrzucenie wszystkich szablonów do jednego pliku działa równie dobrze (chociaż stworzyłoby to bardzo zagmatwany main.html !)

    W tym przypadku, ponieważ oba szablony błędów są relatywnie krótkie, zrobimy wyjątek i wrzucimy je do tego samego pliku aby uporządkować repozytorium.

    Potrzebujemy tylko zintegrować nagłówek szablony i można zaczynać!

    Template.errors.helpers({
      errors: function() {
        return Errors.find();
      }
    });
    
    client/views/includes/errors.js

    Zatwierdź 9-1

    Podstawowe raportowanie błędów.

    Tworzenie błędów

    wiemy jak wyświetlać błędy, ale potrzebujemy utworzyć kilka zanim będzie można cokolwiek zobaczyć. Błędy są najczęściej uaktywniane przez użytkowników wproadzających nowe dane, zatem będziemy sprawdzali błędy w callbacku tworzenia postów i wyświetlać wiadomość dla jakichkolwiek błędów, które powstają.

    Dodatkowo, jeżeli otrzymamy błąd 302 (który oznacza, że post o tym samym URL już istnieje), przekierujemy użytkownika to bieżącego posta. Otrzymamy _id bieżącego posta z error.details (pamiętaj, że przekazaliśmy _id posta jako trzeci parametr do details naszej klasy Errors w rozdziale 7).

    Template.postSubmit.events({
      'submit form': function(e) {
        e.preventDefault();
    
        var post = {
          url: $(e.target).find('[name=url]').val(),
          title: $(e.target).find('[name=title]').val(),
          message: $(e.target).find('[name=message]').val()
        }
    
        Meteor.call('post', post, function(error, id) {
          if (error) {
            // display the error to the user
            throwError(error.reason);
    
            if (error.error === 302)
              Router.go('postPage', {_id: error.details})
          } else {
            Router.go('postPage', {_id: id});
          }
        });
      }
    });
    
    client/views/posts/post_submit.js

    Zatwierdź 9-2

    Używanie raportowania błędów.

    Sprawdź to: spróbuj stworzyć posta i przejść do URL http://meteor.com. Ponieważ ten URL jest już dołączony do posta w danych początkowych (fixtures), powinieneś zobaczyć:

    Wyzwalanie błędu
    Wyzwalanie błędu

    Usuwanie błędów

    Możliwe, że kliknąłeś przycisk ‘zamknij’ po zobaczeniu błędu. Jeżeli to zrobiłeś, błąd powinien zniknąć i pojawić się zaraz po tym, jak wczytasz kolejną stronę do przeglądarki. O co chodzi?

    Przycisk 'zamknij’ wyzwala kod JavaScript w Boostrap Twittera: nie ma nic wspólnego z Meteorem! Co się dzieje: Bootstrap usuwa element <div> z DOM zawierający błąd, ale nie z kolekcji Meteora. Oznacza to, że błąd pojawi się ponownie po ponownym przerenderowaniu strony.

    Z tego względu jeżeli nie chcesz powstrzymać nieustającego pojawiania się błędu za każdym razem i wprawić użytkownika w obłęd, zaimplementujmy rozwiązanie umożliwiające usuwanie błędów także z kolekcji.

    Na początek zmienimy funkcję throwError aby wziąć pod uwagę właściwość seen (ang. widziany). Będzie to przydatne później do śledzenia faktu, czy błąd został już widziany przez użytkownika.

    Gdy jest to gotowe, możemy zaimplementować funkcję clearErrors, która czyści te widziane błędy:

    // Local (client-only) collection
    Errors = new Meteor.Collection(null);
    
    throwError = function(message) {
      Errors.insert({message: message, seen: false})
    }
    
    clearErrors = function() {
      Errors.remove({seen: true});
    }
    
    client/helpers/errors.js

    Następnie, wyczyścimy błędy w routerze aby nawigacja do następnej strony spowoduje usunięcie tych błędów na zawsze:

    // ...
    
    Router.before(requireLogin, {only: 'postSubmit'})
    Router.before(function() { clearErrors() });
    
    lib/router.js

    Aby funkcja clearErrors() poprawnie wykonała swoją pracę, błędy muszą być zaznaczane jako widziane. Aby przebiegało to prawidłowo, musimy zatroszczyć się o obsługę jednego przypadku wyjątkowego: gdy wyrzucamy błąd i przekierowujemy użytkownika do innego miejsca (tak jak przy próbie utworzenia posta ze zduplikowanym linkiem), przekierowanie jest natychmiastowe. Oznacza to, że użytkownik nigdy nie dostanie szansy na zobaczenie błędu zanim zostanie on usunięty.

    Jest to miejsce, w którym przyda się zmienna seen (widziany). Musimy się upewnić, że jest ustawiana na true wyłącznie wtedy, gdy użytkownik zobaczył błąd.

    Aby tego dokonać, użyjemy funkcji Meteor.defer(). Funkcja ta przekazuje Meteorowi, aby uruchomić callback “zaraz po tym” co dzieje się w tej chwili. Jeżeli pomoże to zrozumieć, możesz pomyśleć, że defer() to jak przekazanie przeglądarce aby poczekać 1 milisekundę przed kontynuowaniem pracy.

    Osiągneliśmy to, że Meteor ustawia seen na true 1 milisekundę po renderowaniu szablonu template. Ale czy pamiętasz jak wspomnieliśmy, że przekierowanie dzieje się natychmiastowo? Oznacza to, że mechanizm przekierowania zostanie uruchomiony przed callbackiem defer, który nigdy nie będzie miał możliwości na wykonanie.

    Jest to dokładnie to, co chcieliśmy osiągnąć: jeżeli nie został wykonany, nasz błąd nie zostanie zaznaczony jako widziany, co oznacza, że nie będzie usunięty, co również oznacza, że pojawi się na stronie na którą użytkownik został przekierowany, dokładnie tak, jak chcieliśmy!

    Template.errors.helpers({
      errors: function() {
        return Errors.find();
      }
    });
    
    Template.error.rendered = function() {
      var error = this.data;
      Meteor.defer(function() {
        Errors.update(error._id, {$set: {seen: true}});
      });
    };
    
    client/views/includes/errors.js

    Zatwierdź 9-3

    Monitorowanie które błędy były wyświetlone i czyszczenie …

    Callback rendered jest wywołany ponownie po renderowaniu szablonu w przeglądarce. W środku callback'a this odnosi się do bieżącej instancji szablonu, a this.data pozwala na dostęp do danych aktualnie renderowanego obiektu (w naszym przypadku, komunikatu błędu).

    Świetnie! Dużo pracy było wykonane, aby zaimplementować coś, czego użytkownicy miejmy nadzieję nigdy nie zobaczą!

    Callback rendered

    Callback rendered szablonu jest wywoływany za każdym razem po renderowaniu szablonu w przeglądarce. Zawiera to oczywiście pierwsze renderowanie, gdy pojawia się na ekranie, ale warto zapamiętać, że callback będzie także wywoływany za każdym razem, gdy szablon jest renderowany ponownie tj. za każdym razem, gdy jego dane ulegną zmianie.

    Callback rendered jest ogolnie wywoływany przynajmniej dwukrotnie: za pierwszym razem, gdy aplikakcja zostaje załadowana i za drugim razem gdy dane kolekcji zostają wczytane. Powinieneś być zatem ostrożny przy wstawianiu kodu, który nie powinien zostać uruchomiony dwukrotnie (np. alert, czy kod śledzący zdarzenia na stronie).