Статьи

Создание батареи, а именно с использованием Node.js: клиент

В первой части этого мини-сериала мы обсуждали детали создаваемого сервиса и то, что вы узнаете. Затем мы рассмотрели, почему нам нужен сервер и почему я решил создать службу RESTful. Обсуждая, как разрабатывать сервер, я воспользовался возможностью обсудить, как вы можете определить текущую операционную систему, а также как использовать Node.js для запуска команд на нем.

Во второй и последней части этой серии вы узнаете, как создать клиентскую часть, чтобы она была приятной для пользователей. Для достижения этой цели мы должны обновлять состояние батареи каждые X минут (или секунд), не перезагружая страницу. Более того, мы должны иметь возможность приостанавливать / возобновлять обновления, чтобы избежать затопления нашей системы, когда нам не нужна информация или даже когда мы не смотрим на страницу. Для этого мы будем:

  • Расписание звонков Ajax в нашу бэкэнд-службу через регулярные промежутки времени
  • Используйте декларативную структуру, которая автоматически и эффективно обновляет DOM в ответ на изменения данных;
  • Используйте некоторую служебную функцию jQuery, чтобы сделать нашу жизнь проще;
  • Используйте несколько красивых изображений и CSS, чтобы сделать панель визуально привлекательной (в качестве бонуса!).

Реактивный дизайн

Обсуждение Ajax и асинхронных вызовов, безусловно, выходит за рамки этой статьи (в конце статьи я приведу несколько полезных ссылок). Для наших целей мы можем даже рассматривать их как черные ящики, которые позволяют нам запрашивать данные у сервера и выполнять некоторые действия после их отправки.

Вместо этого давайте уделим минуту обсуждению реактивного дизайна и декларативных структур.

HTML-страница по умолчанию является статической сущностью. Это означает, что для чистой HTML-страницы содержимое, отображаемое на странице, остается неизменным каждый раз, когда оно отображается в браузере. Однако мы знаем, что с помощью JavaScript и, возможно, некоторых шаблонных библиотек, таких как Mustache, мы можем динамически обновлять их.

Существует множество библиотек, которые помогают разработчикам связывать данные с узлами DOM. Большинство из них использует JavaScript для описания элементов DOM, в которые должны быть переведены данные, и требует, чтобы обновления страницы запускались вручную (через JavaScript). Таким образом, мы в конечном итоге полагаемся на логику приложения для решения, когда визуализация должна быть обновлена ​​и какие изменения должны быть сделаны в ответ на изменения данных.

Декларативные структуры связывают данные с элементами DOM и автоматически обновляют DOM при каждом изменении данных. Эта привязка также предоставляется с использованием шаблонов в презентации (разметка HTML), а не в JavaScript.

Дополнительную ценность этих структур можно определить в нескольких ключевых моментах:

  • Они обеспечивают большую степень разделения между содержанием и представлением. Это достигается за счет того, что вы можете определить в уровне представления привязку для данных, обработчиков событий и даже структуры представлений (например, для итеративных и составных объектов, например таблиц);
  • Они обеспечивают простой способ синхронизации вашей модели данных и презентации;
  • Как правило, они делают это чрезвычайно эффективным способом, следя за тем, чтобы перекомпоновывать только минимально возможное подмножество вашего дерева DOM. В связи с этим имейте в виду, что перекомпоновка и перекрашивание обычно являются узкими местами для клиентских браузерных приложений.

Ractive.js

Для библиотеки Ractive.js , которую мы будем использовать, синхронизация между данными и DOM осуществляется через объекты-контейнеры . Библиотека создает объекты, которые оборачиваются вокруг данных. Эти объекты имеют доступ к данным, поэтому каждый раз, когда вы устанавливаете или получаете какое-либо свойство, библиотека может записать ваше действие и внутренне передать его всем подписчикам.

Руки вверх

Теперь, когда мы увидели, для чего нужен Ractive.js, пришло время добавить наш первый шаблон Ractive на нашу страницу. Для этого вы можете добавить тег сценария с выбранным вами идентификатором в любом месте внутри <body> Я предлагаю вам правильно выбрать удостоверение личности, так как оно понадобится нам позже. Нам также нужно добавить атрибут type='text/ractive'

 <script id='meterVizTemplate' type='text/ractive'></script>

type='text/ractive'

 <script src='http://cdn.ractivejs.org/latest/ractive.js'></script>

Теперь внутри скрипта Ractive вы можете добавлять теги HTML и переменные шаблона и условные выражения / циклы. Ractive.js позаботится о том, чтобы оценить все внутри групп {{}}

 <script id='meterVizTemplate' type='text/ractive'>
    {{#batteryState}}
      <br>
      <div class='battery-div'>
        <div class='battery-shell'>
          <div class='battery-percent-text'>{{batteryPercent.toFixed(1) + '%'}}</div>
        </div>
        <div class='battery-level'>
          <div class='battery-mask' style="width:{{(100 - batteryPercent) + '%'}};">
          </div>                
        </div>
        {{#batteryCharging}}
          <div class='battery-plug' intro-outro='fade:1000'></div>
        {{/batteryCharging}}
        {{#batteryPercent <= batteryRedThreshold}}
          <div class='battery-warning' intro-outro='fade:1000'></div>
        {{/batteryLife}}                
      </div>
      <br>
      <br>
        <span class='key'>Battery state:</span> <span class='value {{batteryStateClass(batteryState)}}'>{{batteryState}}</span>
        <br>
        {{#batteryLife}}
          <span class='key'>Time to empty:</span> <span class='value {{batteryLifeClass(batteryPercent)}}'>{{batteryLife}}</span>
        {{/batteryLife}}                  
    {{/batteryState}}
    {{^batteryState}}
      <br>
      LOADING...
    {{/batteryState}}
</script>

В приведенном выше примере вы можете увидеть:

  • Переменные: {{batteryState}}
  • Условия: {{#batteryState}}
  • Вызов функции: {{batteryStateClass(batteryState)}}

Чтобы все это работало, мы должны добавить некоторые привязки в JavaScript. Для этого нам нужно создать новый объект Ractive.js:

 ractive = new Ractive({
    el: 'panels',
    template: '#meterVizTemplate',
    data: {
        // Percentage at which the battery goes to 'red' zone (export for Ractive templates)
        batteryRedThreshold: BATTERY_RED_THRESHOLD,
        // Percentage at which the battery enters 'yellow' zone (export for Ractive templates)
        batteryYellowThreshold: BATTERY_YELLOW_THRESHOLD,
        // The capacity of the battery, in percentage. Initially empty
        batteryPercent: NaN,
        // How much more time can the battery last?
        batteryLife: "",
        // True <=> the update daemon for the battery has been paused
        batteryPaused: false,
        // True <=> the update daemon for the battery has reported an error at its last try
        batteryUpdateError: false,
        // Is the battery connected to power?
        batteryCharging: false,
        batteryStateClass: function (state) {
            return state === 'discharging' ? BATTERY_RED_CLASS : BATTERY_GREEN_CLASS;
        },
        batteryLifeClass: function (percent) {
            return percent <= BATTERY_RED_THRESHOLD ? BATTERY_RED_CLASS : (percent <= BATTERY_YELLOW_THRESHOLD ? BATTERY_YELLOW_CLASS : BATTERY_GREEN_CLASS);
        }
    }
});

Опции, которые мы передаем конструктору, очень важны. Во-первых, el В этом случае нам нужно добавить div

 <div id='panels'></div>

Точка, где вы вставляете этот тег, имеет значение. Это будет родительский элемент для всех элементов, представленных системой шаблонов Ractive.js. Вторым важным параметром, с которым вам следует быть осторожным, является template Его значение должно соответствовать идентификатору text/ractive Наконец, мы присваиваем data

С Ractive.js мы можем даже определить пользовательские события, на которые будет реагировать библиотека:

 ractive.on({
    "battery-pause": function () {
        clearInterval(batteryUpdateTimerId);
        ractive.set('batteryPaused', true);
    },
        "battery-play": function () {
        updateBatteryStatus(); //Checks the status immediately, then starts the daemon
        batteryUpdateTimerId = setInterval(updateBatteryStatus, BATTERY_CHECK_INTERVAL);
        ractive.set('batteryPaused', false);
    }
});

В нескольких строках мы создали механизм для приостановки / возобновления наших обновлений. Однако нам все еще нужно определить updateBatteryStatus()

Асинхронное получение данных

Как и было обещано, здесь есть функция, которая заботится о получении данных из нашего сервиса REST. Используя объект jQuery Deferred , мы настроили обратный вызов, который будет вызван, как только некоторые данные будут получены с сервера. Поскольку мы также используем Ractive.js внутри этого обратного вызова, нам не нужно будет проходить логику того, как мы обновили уровень представления. Фактически, мы просто обновляем значение переменных, используемых в шаблонном скрипте, и Ractive.js позаботится обо всем.

То, что я только что описал, реализовано кодом, приведенным ниже:

 function updateBatteryStatus() {
    $.getJSON(BATTERY_SERVICE_URL)
        .then(function (battery) {
        ractive.set('batteryUpdateError', false);
        var batteryLife = battery.timeToEmpty,
            batteryState = battery.state;
        ractive.animate('batteryPercent', parseInt(battery.percentage, 10), {
            easing: 'easeOut'
        });
        ractive.set('batteryLife', batteryLife);
        ractive.set('batteryState', batteryState);
        ractive.set('batteryCharging', batteryState !== BATTERY_STATE_DISCHARGING);

    }).fail(function () {
        ractive.set('batteryUpdateError', true);
    });
}

//Start the daemons that will check the battery and networks status...
batteryUpdateTimerId = setInterval(updateBatteryStatus, BATTERY_CHECK_INTERVAL);

Собираем все вместе

Конечно, есть еще какая-то проводка, чтобы все это работало вместе. Мы полностью пропустили дизайн приборной панели UX. В конечном счете, это зависит от вас, как только вы поймете, как заставить его работать с системой шаблонов! Например, насколько круто было бы, если бы мы могли отображать процент заряда как текст, так и визуально с помощью какого-нибудь крутого индикатора питания, используя изображения и анимацию? С Ractive.js это не так сложно! Посмотрите на конечный результат:

приборная доска

Если вы хотите проверить код, вы можете снова найти его на GitHub .

Выводы

Наша многоплатформенная приборная панель батареи должна быть готова к работе сейчас. Но это должно быть отправной точкой, а не конечным результатом, и я надеюсь, что вы поняли важные вещи на этом пути:

  • Как настроить HTTP-сервер с помощью Node.js
  • RESTful API
  • Как запускать команды терминала ОС на сервере Node.js.
  • Основы декларативных фреймворков и Ractive.js в частности

Если вы хотите поднять его на следующий уровень, я советую начать экспериментировать с этими инструментами и копать сеть, чтобы углубить знания в этих областях. Если вы хотите углубиться в темы, рассматриваемые в этой статье, я настоятельно рекомендую вам взглянуть на эти полезные ресурсы: