Применить knockout.js

В этой статье пример из учебника по Knockout.js. Это одна из популярных библиотек JavaScript, которая позволяет использовать Model-View-View модели для веб-приложения.

Фреймворк Knockout.js — это новый взгляд на создание приложений JavaScript. Основной идеей является разделение логики и представления. Мы должны создать модель и связать её с презентацией. Чтобы связать View и ViewModel используются атрибуты data-* , для демонстрации библиотеки подготовлен калькулятор.

Демо

Разметка HTML для калькулятора

<script src="js/knockout-2.2.0.js"></script>
<div class="calculator">
  <p>Калькулятор</p>
    <h3 data-bind="text: commandline"></h3>
    <div data-bind="foreach: numbers" class="numbers">
        <button data-bind="value: val, text: val, click: $parent.addNumber"></button>
    </div>
    <div data-bind="foreach: commands" class="commands">
        <button data-bind="value: command, text: command, click: $parent.addCommand, disable: $parent.hasNumbers"></button>
    </div>
    <button data-bind="click: doCalculate" style="width: 330px">=</button>
</div>
<script src="js/main.js"></script>

Калькулятор состоит из десяти кнопок (цифры) и девять команд. В примере использовано два набора: цифры и команды (для создания кнопки) вместо добавления их вручную. Теперь мы можем рассмотреть и разобраться в модели, и понять как это работает.

к меню ↑

JavaScript для калькулятора

Файл js/main.js

var CalculatorModel = function() {
    var self = this;

    // набор возможных команд
    self.commands = [
        {command: ' + '},
        {command: ' - '},
        {command: ' * '},
        {command: ' / '},
        {command: 'sin', action: 'Math.sin(__param__)'},
        {command: 'cos', action: 'Math.cos(__param__)'},
        {command: 'tan', action: 'Math.tan(__param__)'},
        {command: 'ln', action: 'Math.log(__param__)'},
        {command: 'log', action: 'Math.log(__param__) / Math.log(10)'},
    ];

    // спектр возможных чисел
    self.numbers = [
        {val: 1},
        {val: 2},
        {val: 3},
        {val: 4},
        {val: 5},
        {val: 6},
        {val: 7},
        {val: 8},
        {val: 9},
        {val: 0},
    ];

    // результат командной строки
    self.commandline = ko.observable('');

    // последний активная команда
    self.lastCommand = ko.observable('');

    // нужна ли нам очистка?
    self.needCleanup = ko.observable(false);

    // добавить номер функции
    self.addNumber = function(e) {
        if (self.needCleanup()) {
            self.commandline('');
            self.needCleanup(false);
        }

        // нам не нужно добавлять ведущих нулей
        if (this.val == 0 && self.commandline() == '') {
            return;
        }
        self.commandline(self.commandline() + this.val);
    };

    // функция добавить команду
    self.addCommand = function(e) {
        if (e.action && self.commandline()) { // в случае команды, которые не требуют второе значение - мы должны рассчитать значение
            var newCommand = e.action.replace('__param__', self.commandline());
            self.commandline(eval(newCommand));
            self.needCleanup(true);
        }

        if (self.lastCommand() == '') { // поместить команду в командной строке
            if (! e.action) {
                self.commandline(self.commandline() + e.command);
            }
            self.lastCommand(e.command);
        }
    };

    // вычисление
    self.doCalculate = function(e) {
        self.commandline(eval(self.commandline()));

        if (self.lastCommand() != '') {
            self.lastCommand('');
        }
        self.needCleanup(true);
    };

    // отключить кнопки, если бы мы не добавили какие-либо цифры
    self.hasNumbers = ko.computed(function() {
        return self.commandline() == '';
    }, self);
};

ko.applyBindings(new CalculatorModel());

Сначала в калькуляторе определены два массива (команды и цифры). Они содержат все возможные цифры и действия как обычные команды: сложение, вычитание, умножение и деление. Для других функций (SIN, COS и т.д.) мы должны использовать два числа.

Основная идея этого калькулятора, для цифр составить строку математических действий. Если необходимо сложить две цифры (2 и 3) мы должны создать команду 2 +3, когда мы нажимаем на кнопку ‘=’ должен быть результат. Для кнопок применим foreach , чтобы отобразить все кнопки (с цифрами и действиями):

<div data-bind="foreach: numbers" class="numbers">
    <button data-bind="value: val, text: val, click: $parent.addNumber"></button>
</div>
<div data-bind="foreach: commands" class="commands">
    <button data-bind="value: command, text: command, click: $parent.addCommand, disable: $parent.hasNumbers"></button>
</div>

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

к меню ↑

CSS для калькулятора

Файл css/main.css

.calculator {
    /*css3 gradient*/
    background: #e2e2e2; /* Old browsers */
    background: -moz-linear-gradient(top,  #e2e2e2 0%, #dbdbdb 50%, #d1d1d1 51%, #fefefe 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#e2e2e2), color-stop(50%,#dbdbdb), color-stop(51%,#d1d1d1), color-stop(100%,#fefefe)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%); /* IE10+ */
    background: linear-gradient(to bottom,  #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e2e2e2', endColorstr='#fefefe',GradientType=0 ); /* IE6-9 */

    display: block;
    margin: 20px auto 0;
    padding: 20px;
    position: relative;
    width: 340px;

    /*css3 border radius*/
    border-radius: 5px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;

    /*css3 shadow*/
    -webkit-box-shadow: 7px 7px 5px rgba(50, 50, 50, 0.75);
    -moz-box-shadow:    7px 7px 5px rgba(50, 50, 50, 0.75);
    box-shadow:         7px 7px 5px rgba(50, 50, 50, 0.75);
}
.calculator button {
    background-color: #eeeeee;
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #cccccc));
    background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
    background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
    background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
    background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
    background-image: linear-gradient(top, #eeeeee, #cccccc);
    border: 1px solid #ccc;
    border-bottom: 1px solid #bbb;
    border-radius: 3px;
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
    color: #333;
    font: bold 11px/1 "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
    margin: 5px;
    padding: 8px 0;
    text-align: center;
    text-shadow: 0 1px 0 #eee;
    width: 100px;
}
.calculator button:hover {
    background-color: #dddddd;
    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #dddddd), color-stop(100%, #bbbbbb));
    background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
    background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
    background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
    background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
    background-image: linear-gradient(top, #dddddd, #bbbbbb);
    border: 1px solid #bbb;
    border-bottom: 1px solid #999;
    cursor: pointer;
    text-shadow: 0 1px 0 #ddd;
}
.calculator button:active {
    border: 1px solid #aaa;
    border-bottom: 1px solid #888;
    -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
    box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
}
.calculator p {
    margin-bottom: 15px;
    text-align: center;
}
.calculator h3 {
    background-color: rgba(255, 255, 255, 0.4);
    height: 23px;
    margin-bottom: 10px;
    padding: 8px;
    text-align: right;
}
.calculator div {
    background-color: rgba(255, 255, 255, 0.4);
    margin-bottom: 10px;

    /*css3 border radius*/
    border-radius: 5px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
}