Статьи

Создание виджета пожертвований с компонентами рейса

Эта статья была рецензирована Томом Греко . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

В этом уроке я научу вас основам Flight.js в Twitter, сделав виджет для пожертвований, который также использует Materialize для внешнего интерфейса и Stripe для обработки платежей. Мы рассмотрим основные концепции и методы полета.

Flight — это событийный фреймворк от Twitter. Основываясь на компонентах, Flight независимо отображает поведение узлов DOM. В отличие от других популярных фреймворков, Flight не предписывает особый подход к отображению или извлечению ваших данных, однако он зависит от jQuery. По своей сути, Flight это все о событиях. Они могут быть вызваны DOM или искусственными триггерами внутри других компонентов пользовательского интерфейса. Полет — это в основном рамки для создания рамок. Это может показаться сложным, но хотя Flight и не так 1-2-3, как jQuery, его кривая обучения преувеличена, и изучение Flight определенно улучшит ваши навыки JavaScript.

Зачем использовать полет?

  • Напишите более читабельный jQuery.
  • Напишите повторно используемые компоненты.
  • Используйте столько библиотек, сколько захотите.
  • Лучшая структура для вашего кода в целом.
  • Поддерживает и даже предпочитает модульные JS.

Читаемость: JQuery против полета

Допустим, мы слушаем щелчок и наведение на кнопку. Используя jQuery, вы, вероятно, сделаете что-то вроде этого:

$('#button').on('click', function() {
  confirm('You clicked?');
});
$('#button').on('mouseover', function() {
  console.log('Oops');
});

Но использование Flight это все входит в один компонент.

 var Button = flight.component(function () {
  this.log = function () {
    console.log('Oops!');
  }
  this.confirm = function () {
    confirm('You clicked?');
  }
  this.after('initialize', function(){
    this.on('mouseover', this.log);
    this.on('click', this.confirm)
  })
});

Button.attachTo('#button');

Конечно, jQuery требует меньше кода, но с Flight наш код структурирован намного яснее. События — это два разных потока в jQuery, но в Flight они оба содержатся в одном и том же компоненте. Мы можем легко увидеть, что этот компонент и элемент, к которому он присоединен, прослушивают два события. Но, скажем, мы хотели добавить этот слушатель события hover Используя jQuery, вы, вероятно, добавите его в этот момент в своем файле. Однако, используя Flight, мы почти вынуждены добавить его в существующий компонент.

Я сделал шаблон, который включает в себя все, что вам нужно для начала, чтобы вы могли сразу перейти к кодированию. Вы можете раскошелиться / скачать его с CodePen .

Хотите сделать свой собственный шаблон? Просто скопируйте в эти CDN:

 <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="http://flightjs.github.io/release/latest/flight.min.js"></script>
<script src="https://checkout.stripe.com/checkout.js"></script>    

Создание вашего первого компонента

Полет состоит из «компонентов». Компоненты — это многократно используемые блоки кода, которые находятся внутри вашего приложения. Создание компонента во многом похоже на создание конструктора объекта, за исключением того, что компоненты не могут быть изменены или доступны после их инициализации. Компоненты могут общаться с вашим приложением только через события. Либо вызывая их, либо слушая их.

Компоненты полета могут быть простыми или сложными, как вам нравится. В приведенном ниже примере мы только слушаем событие hover (mouseover) на кнопке и показываем предупреждение, когда это происходит.

 var Button = flight.component(function () {
  this.alert = function () {
    alert('Oops!');
  }
  this.after('initialize', function(){
    this.on('mouseover', this.alert);
  })
});

Button.attachTo('#button');

Очевидно, что jQuery ничего не мог сделать, и структурирование вашего кода, как это, может показаться большим усилием сейчас, но как только вы начнете использовать Flight для чего-то другого, кроме событий hover, вы поймете его преимущества.
Пока вы понимаете, что слушаете событие mouseover Я объясню, что значит остальное позже в этом уроке.

Начиная

Если вы используете шаблон, форма в вашем файле index.html

 <form action="#">
    <input type="checkbox" id="accept" />
    <label for="accept">By proceeding your agree to our terms, which are completely unfair and well, not very real.</label>
    <br/>
    <button class="waves-effect btn" style="margin-top: 15px;" id="launch" disabled>Let's Go</button>
</form>

Мы хотим включить кнопку, когда флажок установлен, и отключить ее в противном случае. Давайте перейдем к нашему JavaScript-коду и создадим наш первый компонент.

 var checkToEnable = flight.component(function () {
  // Magic
});

Каждый компонент Flight состоит из нескольких различных методов. Некоторые из них обязательны, другие нет. Первый метод, который мы добавим, это attribute () . Метод attributes() Атрибуты являются переменными и / или аргументами. Пустые атрибуты (объявленные со значением null Другие атрибуты будут использовать свои значения по умолчанию, если не указано иное. Атрибуты обычно используются для хранения ссылок на элементы. Вставьте следующий код в ваш компонент:

 this.attributes({
    button: null 
});

attribute Следующий метод, который мы хотим добавить, это метод initialize () .

 this.after('initialize', function () {
    this.on('change', this.enableButton); 
});

Компоненты Flight уже определяют реализацию по умолчанию initialize()after () . Внутри функции обратного вызова мы добавили прослушиватель событий . Это будет прослушивать изменение элемента, к которому будет присоединен компонент, и, следовательно, запускать enableButton()

 this.enableButton = function (e) {
    var buttonEl = document.getElementById(this.attr.button);
    switch (e.target.checked) {
        case true:
            buttonEl.disabled = false;
            break;
        case false: 
            buttonEl.disabled = true;
            break;
    }
};

Это не делает ничего особенного. Это просто простая функция, которая включает кнопку, когда флажок, к которому этот компонент будет прикреплен, установлен и наоборот. Возможно, вы заметили, что мы получили доступ к attribute Это плохая практика, и я покажу вам лучшее решение позже.

Наш компонент еще не работает. Чтобы завершить это, нам нужно прикрепить его к DOM. Это происходит «снаружи» компонента. Компонент может быть присоединен к любому количеству элементов, а также к документу, но компонент должен стоять на первом месте. Мы передадим ссылку на элемент кнопки в качестве аргумента.

 checkToEnable.attachTo('#accept', {
    button: 'launch'
});

Большой! Вы создали свой первый компонент Flight. Напомним, что это должно выглядеть так:

 var checkToEnable = flight.component(function () {
    this.attributes({
        button: null 
    });

    this.enableButton = function (e) {
        var buttonEl = document.getElementById(this.attr.button);
        switch (e.target.checked) {
            case true:
                document.getElementById(this.attr.button).disabled = false;
                break;
            case false: 
                document.getElementById(this.attr.button).disabled = true;
                break;
        }
    };

    this.after('initialize', function () {
        this.on('change', this.enableButton); 
    });
});

Дайвинг в

К настоящему времени вы должны понимать три наиболее важных понятия: атрибуты, функции обратного вызова и initialize() Большинство компонентов Flight состоят только из этих частей. Наш следующий будет использовать основную концепцию полета: пузыри событий. Звучание событий кажется немного сложным, но на самом деле это не так сложно понять. Например, допустим, у меня есть buttondiv Когда buttondivdiv

Именно так будет работать наш следующий компонент. Он будет прикреплен к виджету пожертвований в виде модальности Materialize, но мы будем прислушиваться к событиям его дочерних элементов. Перво-наперво, нам нужно добавить разметку для модального в наш файл index.html Вставьте его перед закрывающим тегом body:

 <div id="stripe-widget" class="modal">
    <div class="modal-content">
    <h4>Give us your money.</h4>
    <p>We'll use it well, we promise.</p>
    <form action="#">
        <p class="range-field">
        <input type="range" id="stripe-amount" value="10" min="0" max="100" />
    </p>
    </form>
    </div>
    <div class="modal-footer">
    <button class="btn blue waves-effect waves-blue" id="checkout" disabled>Donate <span data-amount=""></span> <i class="fa fa-cc-stripe"></i></button>
    <a href="#!" class=" modal-action modal-close waves-effect waves-red btn-flat">Close</a>
    </div>
</div>

Теперь давайте создадим наш компонент.

 var getAmount = flight.component(function () {
    // Magic
});

Чтобы лучше понять наш компонент, мы будем добавлять его методы в обратном порядке. Прежде всего добавьте метод initialize

 this.after('initialize', function () {
    this.on(this.attr.range,'change', this.onChange); 
    this.on(this.attr.checkout, 'click', this.onClick);
    });

Выглядит иначе, не так ли? Поскольку наш компонент привязан к виджету пожертвования, мы передаем двух его дочерних элементов нашим слушателям событий. Это может не иметь смысла сейчас, но это скоро будет. Давайте добавим атрибуты.

 this.attributes({
    checkout: '#checkout',
    range: '#stripe-amount',
    display_amount: '[data-amount]'
}); 

Эти атрибуты работают с текущей разметкой. Вы можете добавить этот компонент в другую разметку в будущем, и в этом случае вы можете передать разные селекторы. Далее мы добавим onChange()

 this.onChange = function (event) {
    var amount = this.select('range').val();
    if (amount == 0) {
      alert('please enter an amount');
       this.select('checkout').prop('disabled', true);
    } else {
     this.select('checkout').prop('disabled', false);
      this.select('display_amount').text('$' + amount);
      this.select('checkout').attr('data-stripe-amount', amount);
     }
    };

Единственный выделяющийся метод — select () , который во многом похож на метод find в jQuery, но его область действия включает только дочерние элементы присоединенного элемента (виджет пожертвования). Важно понимать, что мы называем имена наших атрибутов как строки. Сначала это смутило меня, так что просто помните об этом, потому что это на самом деле один из ярлыков, созданных Flight.

Поскольку кнопка была включена, теперь она может прослушивать события. Давайте добавим onClick()

 this.onClick = function (event) {
    var stripeAmount = this.select('checkout').attr('data-stripe-amount');
    stripeAmount = stripeAmount + 0 + 0;
    this.trigger('callStripe', {
        amount: stripeAmount
    });
};

Сначала мы выбираем сумму из атрибута кнопки, затем делаем ее действительной для Stripe, добавляя два нуля. В этом нет ничего нового. Настоящее волшебство происходит в методе триггера , который запускает пользовательское событие и передает сумму в виде данных (также называемых полезной нагрузкой). Мы собираемся создать компонент, который будет прослушивать это событие в следующем. Готовый компонент должен выглядеть так:

 var getAmount = flight.component(function () {
    this.attributes({
        checkout: '#checkout',
        range: '#stripe-amount',
        display_amount: '[data-amount]'
    });

    this.onChange = function (event) {
        var amount = this.select('range').val();
        if (amount == 0) {
            alert('please enter an amount');
            this.select('checkout').prop('disabled', true);
        } else {
            this.select('checkout').prop('disabled', false);
            this.select('display_amount').text('$' + amount);
            this.select('checkout').attr('data-stripe-amount', amount);
        }
    };

    this.onClick = function (event) {
        var stripeAmount = this.select('checkout').attr('data-stripe-amount');
        stripeAmount = stripeAmount + 0 + 0;
        this.trigger('callStripe', {
            amount: stripeAmount
        });
    };

    this.after('initialize', function () {
        this.on(this.attr.range,'change', this.onChange); 
        this.on(this.attr.checkout, 'click', this.onClick);
    });
});

Прежде чем мы создадим последний компонент, мы должны initialize Поскольку сам модал инициализируется динамически, мы прикрепим его после того, как он был инициализирован. Приведенный ниже код является простым прослушивателем события нажатия кнопки, которую мы включили в первом компоненте, после чего мы присоединяем наш новый компонент. Метод openModal()

 $('#launch').on('click', function (event) {
    event.preventDefault();
    $('#stripe-widget').openModal();
    getAmount.attachTo('#stripe-widget'); 
});

Встретить полосу

В двух словах, Stripe — это PayPal, который вы всегда представляли, созданный для разработчиков. Stripe используется во многих веб-сайтах и ​​приложениях для обработки платежей (например, Twitter и Kickstarter). Они предлагают ряд услуг или API (как бы вы их ни называли), но мы будем использовать Checkout .

После того, как Stripe проверит чью-либо кредитную карту, ваш веб-сайт получит токен обратно, затем этот токен необходимо отправить в Stripe вместе с вашим секретным ключом. Поскольку ваш ключ является секретным, это не может произойти на внешнем интерфейсе, поскольку JavaScript не был и не должен быть, по крайней мере в его первоначальном виде, безопасным. Для этого вы можете использовать одну из библиотек Stripe для PHP , Sinatra , Python (Flask) , Rails или Node.js.

Я упомянул ключи правильно? Ну, чтобы получить ключ, вам нужно зарегистрировать бесплатный аккаунт Stripe. Вам даже не нужна кредитная карта, подойдет обычный старый банковский счет! После того, как вы получите свой собственный ключ, вам придется заменить мой открытый ключ на ваш при вызове Stripe (как показано ниже). Они предлагают «тестовые» и «настоящие» ключи, поэтому вы легко тестируете свое приложение без особых усилий. При использовании тестовых ключей Stripe также принимает тестовые кредитные карты.

Все должно работать до того момента, когда вы нажимаете кнопку пожертвования, и ничего не происходит. Давайте добавим последний бит магии.

 var launchStripe = flight.component(function () {
    // Magic
});

Этот компонент запустит Stripe для нас. Чтобы сделать это, мы сначала должны создать экземпляр Stripe вверху нашего документа (не внутри компонента).

 var handler = StripeCheckout.configure({
    key: 'pk_test_hue7wHe5ri0xzDRsBSZ9IBEC', //replace this with your key!
    image: 'http://freedesignfile.com/upload/2014/06/Cup-of-coffee-design-vector-material-03.jpg',
    locale: 'auto',
    token: function(token) {
        console.log(token);
        var html = 'Thank you! <i class="fa fa-beer"></i>';
        Materialize.toast(html, 3000);
        // Send to server
    }
});

Хотя знакомство с серверной частью немного выходит за рамки этой статьи, как уже упоминалось выше, в Stripe есть библиотека Node.js, которая может быть немного менее пугающей для тех, кто знаком с JavaScript. Вы можете отправить свой ключ на Node-сервер через HTTP-запрос, используя такой модуль, как Express . В ответе вашего Node-сервера на этот запрос вы бы сделали что-то вроде этого:

 var stripe = require("stripe")(
  "sk_test_yourkeyhere"
);

stripe.customers.create({
  description: 'Coffeehouse Customer',
  source: "tok_yourtoken" // This comes from our front end JavaScript code
}, function(err, customer) {
  // asynchronously called
});

Если вам нужен более полный пример использования Stripe и Node.js, в статье SitePoint о создании службы SMS ежедневных подтверждений с Stripe & Syncano более подробно рассматривается сторона процесса Node.js и даже показано, как вы можете использовать микросервисная платформа, такая как Syncano, для запуска кода Node.js из бэкэнда.

Теперь вернемся к нашему компоненту. Сначала мы добавим метод initialize

 this.after('initialize', function () {
    this.on('callStripe', this.launch);
});

В основном мы слушаем пользовательское событие, запускаемое изнутри, а не событие DOM. Все, что нам нужно сделать сейчас, это создать обратный вызов, который запустит Stripe.

 this.launch = function (event, data) {
    $('#stripe-widget').closeModal();
    handler.open({
        name: 'the Coffeehouse',
        description: 'Thank You!',
        currency: "usd",
        amount: data.amount
    });
};

Наша функция ожидает два аргумента, событие и данные. Событие такое же, как обычно, но данные включают полезную нагрузку, которую мы включили при первоначальном запуске события в предыдущем компоненте. Полезная нагрузка — это просто наша сумма, потому что это все, что мы добавили к событию. Но в более сложных случаях это может быть полным ответом на вызов API.

Другая вещь, которую мы делаем, — это запуск формы оплаты Stripe с использованием суммы, которую мы получили от предыдущего компонента. Вы можете найти полную документацию Checkout здесь .

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

 launchStripe.attachTo(document);

Наш готовый код должен выглядеть примерно так. Мы сделали довольно много за менее чем 100 строк JavaScript!

Вывод

Надеюсь, к настоящему моменту Flight имеет смысл, но в любом случае вы можете найти полную документацию здесь . Как вы можете видеть, система компонентов Flight обеспечивает чрезвычайно читаемый и модульный код. Например, вы можете повторно использовать компонент launchStripeenableButton Полет очень хорош, и потому что он не предписывает особый подход, возможности безграничны.

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