В данной статье я хочу рассказать про мой callback-виджет, который я решил разработать для поставленной задачи!

Суть задачи заключалась в следующем:
  • 1. Разработать удобный для заказчика и клиента виджет средства связи
  • 2. Оперативно и быстро написать его
  • 3. С поддержкой Cross Domain requests
  • 4. С быстрой установкой в одну строчку кода
  • 5. Быстро настраиваемый
  • 6. E-mail рассылка

Решение:

Перед тем как начать писать код, я продумал архитектуру и из самых простых инструментов для backend я использовал php, а front-end native js + angular без использования других jquery-подобных библиотек. Для того, чтобы виджет был уникальным, каждый виджет имеет свой универсальный ключ, по которому мы и будем получать все данные по виджету.

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

Приступим к разбору узких мест!

Как выглядит код для вставки на сайт (index.html):

<script type="text/javascript" charset="utf-8" src="http://domain.ru/widget-get?key=a3234cc36aa23123456a59c281e51e50"></script>

(key — это сгенерированный md5 ключ, по которому я получаю всю информацию по виджету, а не по ID это очень важно, а рекомендуемое место вставки это до закрываеющего тега </body>)

Как выглядит backend код написанный на php, который отдаёт нам необходимые элементы для вставки:

Обработчик:
<?php
class ControllerWidgetGet extends Controller {
	public function index(){
        if (isset($this->request->get['key'])){
            $data['key'] = $this->db->escape($this->request->get['key']);
            if ($row = $this->checkValidKey($data['key'])){
                $data['row'] = $row;
                $this->response->setOutput($this->load->view('/widget/get.tpl', $data));
            }else{
                echo 'alert("Проверьте правильность вашего ключа");';
            }
        }
    }

    public function checkValidKey($key){
        $query_key = $service = $this->db->query("SELECT * FROM `" . DB_PREFIX . "widget` WHERE `key_get` = '" . $key . "'");
        return $query_key->row;
    }
}

Шаблон (get.tpl):
document.write('<link href="http://domain.ru/widget/production/tracker.css" rel="stylesheet"/><script src="http://yastatic.net/angularjs/1.3.16/angular.min.js"></script><script src="http://domain.ru/widget/production/tracker.js"></script><div ng-app="widgetApp"><widget scolor="<?=$row['s_color'];?>" spoints="<?=$row['s_points'];?>" spmenu="<?=$row['s_p_menu'];?>" spwidget="<?=$row['s_p_widget'];?>" sworktime="<?=$row['s_work_time'];?>" key="<?=$key;?>"></widget></div>');

где:
<link href="http://domain.ru/widget/production/tracker.css" rel="stylesheet"/> - наши стили

<script src="http://yastatic.net/angularjs/1.3.16/angular.min.js"></script> - cdn используемая библиотека

<script src="http://domain.ru/widget/production/tracker.js"></script> - логика нашего приложения

<div ng-app="widgetApp"><widget scolor="<?=$row['s_color'];?>" spoints="<?=$row['s_points'];?>" spmenu="<?=$row['s_p_menu'];?>" spwidget="<?=$row['s_p_widget'];?>" sworktime="<?=$row['s_work_time'];?>" key="<?=$key;?>"></widget></div> - инициализация нашего angular приложения, с передаваемыми параметрами в Виджет

Читая различные маны(stackoverflow и др.) по вставке элементов в html с использованием js, железно работает только document.write. Но на этом вся история ещё не останавливается, на данный момент у нас только получилось загрузить к нам на сайт *.css, *.js, но нам также для вывода нашего виджета потребуется много html разметки, и тут стали приходить различные варианты!

Директива Angular поддерживает следующий template:

#1
.directive('myDialog', function() {
  return {
    templateUrl: 'http://domain.ru/my-dialog.html'
  };


#2
.directive('myDialog', function() {
  return {
    template: '<div ng-controller="MyController"><h1>{{textHello}}</h1></div>'
  };

Комментарий к #1:
Не кроссдоменное решение, он забирает методом GET .html разметку и выпадают ошибки вида:
“No 'Access-Control-Allow-Origin' header is present on the requested resource”

посмотрим что скажет на это stackoverflow

Комментарий к #2:
Здесь понятно, что внутри нашего /tracker.js мы не прибегаем к загрузке html с другого источника, а у нас есть возможность вставить его просто в параметр template, супер! Но казалось бы как так? Разве мы не можем сделать кроссдоменный запрос типа JSONP и забрать HTML, подумали вы? НЕТ!

Смотрим сюда stackoverflow

Просто замечтательно!

Инструменты напоследок:

Для сжатия я использовал следующий конфигурационный файл gulp.js

var gulp = require('gulp'),
    minifyHTML = require('gulp-minify-html'),
    sass = require('gulp-sass');
autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    uglify = require('gulp-uglify');

gulp.task('p-ht', function() {
    var opts = {
        conditionals: true,
        spare:true
    };
    return gulp.src('src/html/index.html')
        .pipe(minifyHTML(opts))
        .pipe(gulp.dest('dest/html/'))
});

gulp.task('p-st', function() {
    //return gulp.src('src/styles/main.scss')
        //.pipe(sass({style: 'expanded'}))
    return gulp.src('src/styles/tracker.css')
        .pipe(autoprefixer('last 2 version'))
        .pipe(gulp.dest('dest/styles/'))
        //.pipe(rename({suffix: '.min'} ))
        .pipe(minifycss())
        .pipe(gulp.dest('dest/styles/'))
});

gulp.task('p-sc', function() {
    return gulp.src('src/scripts/*.js')
        .pipe(concat('tracker.js'))
        .pipe(gulp.dest('dest/scripts/'))
        //.pipe(rename({suffix: '.min'}))
        .pipe(uglify())
        .pipe(gulp.dest('dest/scripts/'))
});

gulp.task('watch', function() {
    gulp.watch('src/scripts/*.js', ['p-sc'])
});


gulp.task('dev', ['p-st', 'p-sc', 'watch'], function () {

});

gulp.task('build', ['p-st', 'p-sc', 'p-ht'], function () {

});


Комментарий: при сжатии html символ одинарной кавычки экранируйте при использовании с ng-class={\'test-class\': expression}, и следите за символами в регулярных выражениях, js-редактор обычно подсвечивает, подсвеченные символы экранируйте

Для копирования кода в буфер я использовал следующий код, расположенный на stackoverflow

Заключение:

Хотелось бы отметить для более точного понимания, данный код работает на любых сайтах, а не только на локальном! Запросы, которыми я обмениваюсь со своим сервером исключительно JSONP!

Всем удачи в разработке!

Комментарии (5)


  1. rsvasilyev
    05.08.2015 19:58
    +2

    Вы хоть бы вкратце объяснили, что такое «Oblax» и что значит «виджет средства связи».


    1. DmitryMV
      06.08.2015 17:46

      Callback — это обратный звонок с сайта, т.е. клиент который решил посетить вашу страницу не смог найти необходимую ему информацию и он уже готов выйти с сайта, но вдруг Callback — виджет, это кнопка с фиксированными положением на странице экрана, нажав на которую или же поймав клиента на одном из сценариев (прокрутка в низ страницы, при входе на сайт), открывается форма в которой клиент имеет возможность выбрав удобное время и оставив телефон Заказать обратный звонок!


  1. sHinE
    06.08.2015 16:31

    Браузеры на https страницах не будут ругаться на вставку скриптов и стилей по http? Можно для доступа по протоколу загруженной страницы начинать адреса с

    //
    — типа
    //domain.ru/widget/production/tracker.js


    1. DmitryMV
      06.08.2015 17:48

      Идея хорошая, но на данный момент буду тестировать CDN на Amazon CloudFront и скоро подготовлю новую статью!


  1. antirek
    10.08.2015 07:06

    В виде кода на гитхабе было бы удобнее изучить ваше решение и взять его на вооружение.

    Названия задач в gulpfile доставляют. Вам самому удобно их различать?