https://www.pexels.com/photo/computer-content-control-data-270700/

Konfiguracja położenia sensorów cz. 1 (JS)

Poprzednim razem opisywałem bardziej to co chcę osiągnąć w kwestii wyboru lokalizacji sensora w mieszkaniu, niż jak planuję to zrobić. Dało mi to trochę czasu na poszerzenie wiedzy z zakresu pisania kodu w języku JavaScript. Jestem w trakcie lektury bardzo dobrej książki JavaScript. Wzorce. Co prawda nie jest to najświeższa pozycja (2012) i opisuje czasy, gdzie ES5 nie był jeszcze zaimplementowany we wszystkich przeglądarkach, jednak część wzorców i zaleceń pozostanie dalej aktualna. Biorąc pod uwagę, że jest to pierwsza książka o JS, po którą sięgnąłem, jestem całkiem zadowolony z tego wyboru. Czyta się lekko, treści są łatwe do zrozumienia, nawet dla początkującego, takiego jak ja. 🙂

W tym miejscu powinienem się wytłumaczyć z pewnych niedociągnięć, z których zdaję sobie sprawę, a które rasowych frontendowców mogą mocno razić w oczy. Aplikacja webowa przeze mnie tworzona nie jest uniwersalna na tyle, na ile być powinna. Przede wszystkim zastosowałem element <canvas>, który będzie obsługiwany za pomocą JS. Przy takim warunku, wyłączenie obsługi JS w przeglądarce uniemożliwi korzystanie z głównej funkcjonalności! Dodatkowo, całkowicie nie przejmuję się różnicami w przeglądarkach, a w szczególności pomiędzy tymi nowoczesnymi, a (pre)historycznymi. Na swoją obronę mogę przytoczyć jeden argument. Zakładam, że aplikacja ta nie jest przeznaczona dla masowego odbiorcy, a raczej do własnych zastosowań. W takiej sytuacji jestem w stanie zapanować nad środowiskiem, w którym będzie ona uruchamiana. W związku powyższym, świadomie nie będę się przejmował się tym problemem.

Pierwszym antywzorcem jaki znalazłem w moim kodzie podczas weekendowego przeglądu było pomieszanie elementów z różnych warstw. Niby oczywista sprawa, a jednak z lenistwa się trafiła w dwóch miejscach. Refaktoryzacja polegająca na oddzieleniu zawartości (HTML), prezentacji (CSS) i zachowania (JS).

<!--Było-->
<section class="container" style="text-align: center">
    <a href="/" onclick="HomeController.utilities.goBack()" class="btn btn-danger">
        <span class="glyphicon-hand-left glyphicon"></span> Powrót</a>
</section>
 
<!--Jest-->
<section class="container text-center">
    <a href="/" id="goBack" class="btn btn-danger">
        <span class="glyphicon-hand-left glyphicon"></span> Powrót</a>
</section>

Powyższa sekcja dodaje przycisk cofający do poprzedniego widoku. Poniżej krótki przykład obsługi z poziomu JS z zastosowaniem jQuery. Funkcja event.preventDefault() wyłącza domyślne zachowanie, w tym wypadku przeniesienie do strony określonej w atrybucie href znacznika <a>.

$(document).ready(function () {
   $('#goBack').click(function (event){
       event.preventDefault();
       HomeController.utilities.goBack();
   }); 
});

Dzięki lekturze książki w kolejnym kroku udało mi się wyłapać niepotrzebny atrybut type=”text/javascript” w znaczniku <script>, który w HTML5 nie jest już wymagany. Zmieniłem również miejsce podpinania skryptów, które teraz są wstawione tuż przed znacznikiem zamykającym </body>. Z rzeczy do zrobienia pozostaje dalsze uporządkowanie ładowania plików CSS i JS. Na produkcji wszystkie skrypty i style trafią do jednego pliku, który poddany będzie minifikacji. W tej chwili zasoby wspólne dla każdej podstrony ładowane są w pliku main.scala.html. Zasoby specyficzne dla danego widoku dołączane są na żądanie, co opisywałem w poprzednim wpisie.

To, na czym najbardziej mi zależało, to uporządkowana struktura kodu, aby nie było jednego wielkiego makaronu. Jednym rozwiązań jest podział aplikacji na moduły, które będę chciał wykorzystywać wielokrotnie. Wydzieliłem ogólne funkcjonalności, które mogą być wykorzystane w wielu miejscach i umieściłem w module HomeController.utilities, który został utworzony na bazie funkcji natychmiastowej z publicznymi metodami. Znalazła się tu metoda powrotu do poprzedniej podstrony (goBack), metoda pobierająca pozycję kursora na elemencie <canvas> (getMousePos()) oraz druga sprawdzająca czy kliknięcie wystąpiło w określonym, prostokątnym obszarze (isMouseInsideRectangle()). W tym samym module znalazła się również metoda rysująca okrąg o określonym stylu (drawCircle()) oraz przycisk (showButton()). Implementacje zostały wycięte, aby nie zaciemniały idei, natomiast zainteresowanych odsyłam do GitHub.

HomeController.namespace('HomeController.utilities');
HomeController.utilities = (function () {
    return {
        goBack: function (){
        // (...)
        },
        isMouseInsideRectangle: function (mousePosition, rectangle) {
        // (...)
        },
        getMousePos: function (canvas, event) {
        // (...)
        },
        drawCircle: function (context, x, y, radius, color) {
        // (...)
        },
        showButton: function (context, buttonRectangle, color, caption) {
        // (...)
        }
    };
}());

Przewidziałem również konieczność powtórnego wykonania kodu rysującego plan mieszkania oraz czujniki na elemencie <canvas>. Metody z modułu HomeController.home wykorzystywane są obecnie w widoku wyboru lokalizacji sensora, a w przyszłości chcę je wykorzystać również w planie mieszkania zawierającym podgląd graficzny wszystkich czujników. Moduł jest analogiczny do poprzedniego z tą różnicą, że występują w nim składowe prywatne, których nie chcę udostępniać na zewnątrz. Jest to dopiero wstępny zarys, który wystawia na świat jedynie metodę zwracającą obiekt z typami sensorów, metodę pobierającą kolor wybranego czujnika oraz inicjującą, która rysuje plan mieszkania z legendą objaśniającą znaczenie symboli czujników.

HomeController.namespace('HomeController.home');
HomeController.home = (function () {
 
    const utils = HomeController.utilities;
    let _canvas = {};
    const _sensorTypes = [
        {name: "Czujnik temperatury DS18B20", color: "red"},
            // (...)
        {name: "Przekaźnik", color: "teal"}
    ];
 
    const
            _prepareHomePlan = function () {
            // (...)               
            },
            _prepareAppartmentPlan = function (context) {
            // (...)              
            },
            _prepareLegend = function (context) {
            // (...)              
            },
            _addLegendPosition = function (context, position, text, color) {
            // (...)  
            };
 
    return {
        init: function () {
            _canvas = document.getElementById("appartmentPlan");
            _prepareHomePlan();
            return _canvas;
        },
        getSensorTypes: function () {
            return _sensorTypes;
        },
        getColorOfSensor: function (sensorType) {
            let color = "black";
            for (let i = 0, max = _sensorTypes.length; i < max; i++) {
                if (sensorType === _sensorTypes[i].id) {
                    color = _sensorTypes[i].color;
                }
            }
            return color;
        }
    };
}());

Temat ten będę kontynuował w następnych wpisach. W związku z tym, że lada dzień kończy się mój 10 tydzień aktywności w konkursie „Daj się poznać 2017” i przekroczyłem już graniczną liczbę 20 postów (od 1 marca), kolejne wpisy chcę publikować z mniejszą częstotliwością, tj. 1 post / tydzień. Wbrew pozorom ubranie myśli w słowa kosztuje trochę czasu i sądzę, że lepiej jest go wykorzystać na pisanie kodu, zamiast pamiętnika. Pomimo dotychczasowego braku zainteresowania i szerokiej rzeszy czytelników, chcę dalej kontynuować prowadzenie bloga i rozwijać projekt Sterownika Domowego. Tym bardziej, że postęp prac nie jest wcale tak zaawansowany, jakbym oczekiwał. Serdecznie zapraszam pod koniec przyszłego tygodnia, na pewno pojawi się jakaś nowa notka! 🙂

Literatura obowiązkowa

Jak schudnąć w przeglądarce
JavaScript – var, let, const – różnice

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *