ИЛЬЯ ТАРАТУХИН

KNOCKOUT.js НА ПРИМЕРЕ
2ГИС-ОНЛАЙН
ДОКЛАДЧИК
С 2011 года работаю в 2ГИС




API справочника          API карт      Карты 2ГИС

>5 млн >8 млн >3 млн
                 Разрабатывал АСУ ТП     Спикер CodeFest
                 для горно-шахтного      2012
                 оборудования



WWW.2GIS.RU
АРХИТЕКТУРА WEB-APP

                  Пользователь



                  2ГИС Онлайн




                 API Справочника




WWW.2GIS.RU
АРХИТЕКТУРА CLIENT-SIDE APP



                              Пользователь



   API             API         API              API       API
Транспорта       пробок     <Место для
                          вашего сервиса>
                                            Справочник   Карт   +1


                                 2ГИС Онлайн



   WWW.2GIS.RU
DOM НА КЛИЕНТЕ




WWW.2GIS.RU
DOM НА КЛИЕНТЕ


 var newDiv = document.createElement('div');
 newDiv.className = 'my-class';
 newDiv.id = 'my-id';

 newDiv.innerHTML = 'Привет, мир!';

 $('#container').appendChild(newDiv);


              Привет, мир!
WWW.2GIS.RU
WHERE IS
ШАБЛОНИЗАТОРЫ



    jQueryTemplate
   Mustache
   Underscore.js
   Шаблонизатор резига
   Pure



WWW.2GIS.RU
СОБЫТИЯ


                                 .firmFull
                     #firmList




              .firmShort




WWW.2GIS.RU
СОБЫТИЯ

$("#firmTemplate").tmpl(someData)
.appendTo("#firmList");

$('.firmShort').live({
      click: function() {
        showFirmCard(this);
      }
    });

$('.firmFull').live({
      click: function() {
        hideFirmCard(this);
      }
    });
WWW.2GIS.RU
СОБЫТИЯ




WWW.2GIS.RU
KNOCKOUT




WWW.2GIS.RU
ПОЧЕМУ KNOCKOUT?




              Активно развивается




WWW.2GIS.RU
ПОЧЕМУ KNOCKOUT?


•   Активно развивается



          Удобное разделение
           логики и шаблонов


WWW.2GIS.RU
ПОЧЕМУ KNOCKOUT?


•   Активно развивается
•   Удобное разделение логики и шаблонов

      Функционален, есть
    декларативные биндинги


WWW.2GIS.RU
ПОЧЕМУ KNOCKOUT?


•   Активно развивается
•   Удобное разделение логики и шаблонов
•   Функционален, есть декларативные
    биндинги


    Низкий порог вхождения


WWW.2GIS.RU
MVVM

              UI Logic                       Business Logic



               View                            Model




                         ViewModel

                         Application Logic



WWW.2GIS.RU
KNOCKOUT




WWW.2GIS.RU
KNOCKOUT


<div class="dg-search-result-header">
      <span data-bind="text:
  what_text"></span>,
      <span data-bind="text:
  where_text"></span></div>




 WWW.2GIS.RU
KO.OBSERVABLE


function vm() {
    this.what_text = ko.observable('');
}
ko.applyBindings(new vm());
...
vm.what_text(response.what);


<span data-bind="text: what_text"></span>



 WWW.2GIS.RU
МАССИВЫ




WWW.2GIS.RU
МАССИВЫ


response.result = [
    {
       firmName = 'Завод №1',
       ...
    },
    {
       firmName = 'Завод №2';
       ...
    }]

 WWW.2GIS.RU
KO.OBSERVABLE_ARRAY


function vm () {
    this.firms = ko.observableArray([]);
}
...
vm.firms(response.result);


<div data-bind="foreach: firms">
  <div data-bind="text: firmName"></div>
</div>

 WWW.2GIS.RU
BINDINGS




WWW.2GIS.RU
BINDINGS


<div class="dg-search-result-header">
      <span data-bind="text:
  what_text"></span>,
      <span data-bind="text:
  where_text"></span></div>




 WWW.2GIS.RU
BINDINGS



- Текст и стиль блока
- Control flow
- Работа с формами
- Шаблонизация
- <место для ваших идей>



WWW.2GIS.RU
BINDINGS



                     #firmList
                                 .firmFull




              .firmShort




WWW.2GIS.RU
BINDINGS


<div id="firmList"
    data-bind="foreach: firms">
  <div class="firmShort"
     data-bind="visible: !isVisible"></div>
    <div class="firmFull"
     data-bind="visible: isVisible"></div>
</div>




 WWW.2GIS.RU
BINDINGS




WWW.2GIS.RU
BINDINGS


data-bind="visible: isVisible,
            click: toggleVisibility"




 WWW.2GIS.RU
BINDINGS


data-bind="visible: isVisible,
            click: toggleVisibility"




toggleVisibility = function() {
    this.isVisible(!this.isVisible());}




 WWW.2GIS.RU
BINDINGS


ko.bindingHandlers['visible'] = {
       'update': function (element, valueAccessor) {
           var value =
   ko.utils.unwrapObservable(valueAccessor());
           var isCurrentlyVisible = !(element.style.display
   == "none");
           if (value && !isCurrentlyVisible)
               element.style.display = "";
   else if ((!value) && isCurrentlyVisible)
               element.style.display = "none";
   }};




WWW.2GIS.RU
BINDINGS


ko.bindingHandlers['animateVisible'] = {
       'update': function (element, valueAccessor) {
           var value =
   ko.utils.unwrapObservable(valueAccessor());
           var isCurrentlyVisible = !(element.style.display
   == "none");
           var slideSpeed = 200;
           if (value && !isCurrentlyVisible)
            $(element).slideDown(slideSpeed,
   callback);      else if ((!value) &&
   isCurrentlyVisible)
            $(element).slideUp(slideSpeed,
   callback);
        }};


WWW.2GIS.RU
BINDINGS


data-bind="animateVisible: isVisible,
            click: toggleVisibility"




toggleVisibility = function() {
    this.isVisible(!this.isVisible());}




 WWW.2GIS.RU
BINDINGS

<script type="text/my-tpl" id="firm-tpl">
    //firm template code</script>




WWW.2GIS.RU
BINDINGS

<script type="text/my-tpl" id="catalog-tpl">
    //some template code
  <div data-bind="template: {
                      name: 'firm-tpl',
                      foreach: firms
                  }"></div>
</script>




WWW.2GIS.RU
BINDINGS
BINDING-CONTEXT

                    vm




                                vm.firms[n]




WWW.2GIS.RU
ПЕЧАТЬ




WWW.2GIS.RU
ПЕЧАТЬ

<script type="text/my-tpl" id="print-tpl">
    //some template code
  <div data-bind="template: {
                      name: 'firm-tpl',
                      foreach: firms,
                      templateOptions: {
                         isPrint: 1
                      }
                   }"></div>
</script>


WWW.2GIS.RU
BINDINGS
 <div class="phone-number"
      data-bind="visible:
 $context.isPrint)">
   <!-- Some code-->
 </div>




WWW.2GIS.RU
BINDINGS


 <div class="contacts"
    data-bind="template: {
                 name: 'firm-tpl',
                 data: $data,
                 templateOptions: {
                   isPrint: $context.isPrint
                 }
               }">
   <!-- Some code-->
 </div>


WWW.2GIS.RU
BINDING CONTEXT




                $context
                  $data
              templateOptions




WWW.2GIS.RU
BINDING CONTEXT




                               •   $parent
         $context              •   $parentContext
              $data            •   $root
     templateOptions           •   $index
                               •   $element




WWW.2GIS.RU
BINDINGS

function initBalloon (options) {
      map.createBalloon({
          point: options.point;
          content: options.template
      });   }




WWW.2GIS.RU
BINDINGS

function initBalloon (options) {
      map.createBalloon({
          point: options.point;
          content: options.template
      });


     var container = $('#balloonContent');
       ko.applyBindingsToNode(container ,
   vm);}




WWW.2GIS.RU
КОГДА МНОГО "ЕСЛИ"




WWW.2GIS.RU
КОГДА МНОГО "ЕСЛИ"


this.showPreloader = ko.computed(function(){
    return this.firmsLoad() &&
  this.geoLoad();});




<span id="preloader"
    data-bind="visible: showPreloader">
</span>

WWW.2GIS.RU
KO.COMPUTED



       Не наблюдайте один computed
               внутри другого




WWW.2GIS.RU
KO.COMPUTED


1. Не наблюдайте один computed внутри другого


        Не меняйте observable внутри
                 computed




WWW.2GIS.RU
KO.COMPUTED


1. Не наблюдайте один computed внутри другого
2. Не меняйте observable внутри computed


  Используйте computed только там,
        где это необходимо




WWW.2GIS.RU
PLUGINS




WWW.2GIS.RU
PLUGINS



knockout.address




WWW.2GIS.RU
PLUGINS



knockout.address


ko.linkObservableToUrl(vm.history, 'history');




  window.location       vm.myObservable


 WWW.2GIS.RU
PLUGINS




   https://github.com/SteveSanderson/knockout/wiki/Plugins

WWW.2GIS.RU
БОЛЬШИЕ ПРОЕКТЫ




   107             22              18
                 Computed       Observable
 Observable                       Array




WWW.2GIS.RU
БОЛЬШИЕ ПРОЕКТЫ




 200+             107           22 18

Functions

WWW.2GIS.RU
БОЛЬШИЕ ПРОЕКТЫ




 13                200+         107
                                      22

                                 18
WWW.2GIS.RU
БОЛЬШИЕ ПРОЕКТЫ


Namespace.ViewModelModules.<ourModule> = {
    _observables: {
      <ourObservable>: <defaultData>,
      <ourComputed>:
  function(){/*computedCode*/}
    },
    <someProperty>: 100500,
    _initModule: function(){/*initCode*/},
    <function>: function(){/*fBody*/}}




 WWW.2GIS.RU
БОЛЬШИЕ ПРОЕКТЫ


Namespace.ViewModelModules.<ourModule> = {
    _observables: {
      <ourObservable>: <defaultData>,



                150
      <ourComputed>:
  function(){/*computedCode*/}
    },
    <someProperty>: 100500,
    _initModule: function(){/*initCode*/},
    <function>: function(){/*fBody*/}}




 WWW.2GIS.RU
 WWW.2GIS.RU
IDE



data-bind="
  //очень много
  //кода
  //который выглядит
  //как одна сплошная строка
"


WWW.2GIS.RU
IDE




WWW.2GIS.RU
IDE




WWW.2GIS.RU
WWW.2GIS.RU
АНАЛОГИ


    - AngularJS
    - Backbone.js
    - Ember.js
    - ExtJS
    - CorMVC
    - AsanaLuna
    - ...

WWW.2GIS.RU
ВОПРОСЫ?




WWW.2GIS.RU
Knockoutjs на примере 2ГИС-Онлайн
Knockoutjs на примере 2ГИС-Онлайн

Knockoutjs на примере 2ГИС-Онлайн