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

Preview:

Citation preview

С 2011 года работаю в 2ГИС:

- API Справочника > 5млн- API Карт > 8млн- Карты 2ГИС > 3млнСпикер

dump 2013

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

2ГИС Онлайн

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

АРХИТЕКТУРА WEB-APP

WWW.2GIS.RU

АРХИТЕКТУРА CLIENT-SIDE APP

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

2ГИС Онлайн

API Транспорта

API пробок

API <Место для

вашего сервиса>

API Справочник

API Карт +1

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.RUWWW.2GIS.RU

СОБЫТИЯ

.firmFull

WWW.2GIS.RUWWW.2GIS.RU

.firmShort

#firmList

СОБЫТИЯ

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

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

$('.firmFull').live({ click: function() { hideFirmCard(this); } });WWW.2GIS.RUWWW.2GIS.RU

СОБЫТИЯ

ПОЧЕМУ KNOCKOUT?

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

WWW.2GIS.RUWWW.2GIS.RU

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

ПОЧЕМУ KNOCKOUT?

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

WWW.2GIS.RUWWW.2GIS.RU

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

ПОЧЕМУ KNOCKOUT?

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

WWW.2GIS.RUWWW.2GIS.RU

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

биндинги

ПОЧЕМУ KNOCKOUT?

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

WWW.2GIS.RUWWW.2GIS.RU

MVVM

View Model

ViewModel

UI Logic Business Logic

Application Logic

WWW.2GIS.RUWWW.2GIS.RU

KNOCKOUT

WWW.2GIS.RUWWW.2GIS.RU

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

KNOCKOUT

WWW.2GIS.RUWWW.2GIS.RU

function vm() {this.what_text = ko.observable('');

}ko.applyBindings(new vm());...vm.what_text(response.what);

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

KO.OBSERVABLE

WWW.2GIS.RUWWW.2GIS.RU

МАССИВЫ

WWW.2GIS.RUWWW.2GIS.RU

response.result = [ { firmName = 'Музей №1', ... }, { firmName = 'Музей №2'; ... }]

МАССИВЫ

WWW.2GIS.RUWWW.2GIS.RU

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

}...vm.firms(response.result);

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

</div>

KO.OBSERVABLE_ARRAY

WWW.2GIS.RUWWW.2GIS.RU

BINDINGS

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

BINDINGS

— Текст и стиль блока

— Control flow

— Работа с формами

— Шаблонизация

— <место для ваших идей>

WWW.2GIS.RUWWW.2GIS.RU

.firmFull

WWW.2GIS.RUWWW.2GIS.RU

.firmShort

#firmList

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>

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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"; }};

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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); }};

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

BINDINGS

BINDING-CONTEXT

vm

vm.firms[n]

WWW.2GIS.RUWWW.2GIS.RU

ПЕЧАТЬ

WWW.2GIS.RUWWW.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.RUWWW.2GIS.RU

BINDINGS

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

WWW.2GIS.RUWWW.2GIS.RU

BINDINGS

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

WWW.2GIS.RUWWW.2GIS.RU

$context

$data

templateOptions

BINDING CONTEXT

WWW.2GIS.RUWWW.2GIS.RU

BINDING CONTEXT

● $parent● $parentContext● $root● $index● $element

WWW.2GIS.RUWWW.2GIS.RU

$context

$data

templateOptions

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

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

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

BINDINGS

WWW.2GIS.RUWWW.2GIS.RU

КОГДА МНОГО "ЕСЛИ"

WWW.2GIS.RUWWW.2GIS.RU

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

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

КОГДА МНОГО "ЕСЛИ"

WWW.2GIS.RUWWW.2GIS.RU

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

KO.COMPUTED

WWW.2GIS.RUWWW.2GIS.RU

WWW.2GIS.RU

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

KO.COMPUTED

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

WWW.2GIS.RUWWW.2GIS.RUWWW.2GIS.RU

WWW.2GIS.RU

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

KO.COMPUTED

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

WWW.2GIS.RUWWW.2GIS.RU

PLUGINS

PLUGINS

knockout.address

WWW.2GIS.RUWWW.2GIS.RU

PLUGINS

window.location vm.myObservable

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

WWW.2GIS.RUWWW.2GIS.RU

knockout.address

PLUGINS

WWW.2GIS.RUWWW.2GIS.RU

$('#firmFull').dataBind({ animateVisible: isVisible, click: toggleVisibility});

https://gist.github.com/joelnet/1006808

PLUGINS

WWW.2GIS.RUWWW.2GIS.RU

Unobtrusive Knockout support library for jQuery

Joel Thoms

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

PLUGINS

WWW.2GIS.RUWWW.2GIS.RU

БОЛЬШИЕ ПРОЕКТЫ

107 22 18

Observable Computed ObservableArray

WWW.2GIS.RUWWW.2GIS.RU

Functions

БОЛЬШИЕ ПРОЕКТЫ

200+ 107 22 18

WWW.2GIS.RUWWW.2GIS.RU

БОЛЬШИЕ ПРОЕКТЫ

18

WWW.2GIS.RUWWW.2GIS.RU

107200+13 22

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

БОЛЬШИЕ ПРОЕКТЫ

WWW.2GIS.RUWWW.2GIS.RU

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

WWW.2GIS.RU

БОЛЬШИЕ ПРОЕКТЫ

150

WWW.2GIS.RUWWW.2GIS.RU

https://github.com/2gis/dgKoModulizer

dgKoModulizer

WWW.2GIS.RUWWW.2GIS.RU

IDE

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

WWW.2GIS.RUWWW.2GIS.RU

IDE

WWW.2GIS.RUWWW.2GIS.RU

IDE

WWW.2GIS.RUWWW.2GIS.RU

ПРОИЗВОДИТЕЛЬНОСТЬ

WWW.2GIS.RUWWW.2GIS.RU

DOM.Build()

АНАЛОГИ

— AngularJS— Backbone.js— Ember.js— ExtJS�— CorMVC— AsanaLuna— ...

WWW.2GIS.RUWWW.2GIS.RU

Илья Таратухин@darklifa

Recommended