Заметки с тегом «utility» (страница 4)

Привязка данных к объектам страницы

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

Для небольших виджетов я нашел оптимальным отделять этап создания представления и подключения контролера.


var obj = [], i;
obj.push('<div class="wrapper">');
for (i = 0; i < model.lenght; i++) {
    obj.push('<div class="item">');
    obj.push('<h1>', model[i].title, '</h1>');
    obj.push('<div class="input"><input type="text" value="', model[i].value, '" /></div>');
    obj.push('</div>');
}
obj.push('</div>');
$(root).html(obj.join(''));
$('div.input input', root).change(function () {
    // нужно сохранить значение в модель
});

В обработчике нужно сохранить значение поля ввода в соответствующее поле модели. Но как узнать, какое поле было изменено? Некоторое время я при создании представления использовал атрибут id , по которому можно было впоследствии определить порядковый индекс в модели или перебором найти аналогичное значение у идентификационного поля элемента модели. Тут в бой вступали сложные регулярные выражения, различные проверки и т.п. Внутри я понимал, что это далеко не самый лучший способ и решил найти более эффективное решение. Оно в итоге действительности оказалось простым.


(function () {
    var uidx = 1, storage = {};

    /**
     * Generate an id that is unique among application
     * @method guid
     * @param pre {String} optional guid prefix
     * @return {String} the guid
     */
    APP.guid = function (pre) {
        var p = (pre) || 'app';
        return p + '-' + uidx++;
    };

    /**
     * Make a save point for linking DOMNodes and Objects via IDs
     * @param obj {Object}
     * @param pre {String} optional guid prefix
     * @return {String} the guid
     */
    APP.savepoint = function (obj, pre) {
        var guid = APP.guid(pre);
        storage[guid] = obj;
        return guid;
    };

    /**
     * Recall data for save point
     * @param guid {String} id of save point
     * @return {Object} stored data
     */
    APP.recall = function (guid) {
        return (guid in storage) ? storage[guid] : null;
    };
})();

Теперь поле ввода я создаю с уникальным id.


obj.push('<div class="input"><input type="text" value="', model[i].value, '" id="', APP.savepoint(model[i]), '" /></div>');

А реализация обработчика выглядит не менее сложной.

function () {
    var o = APP.recall(this.id);
    o.value = this.value;
}

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

Оставте свой комментарий

Очередь: синхронное выполнение функций

С использованием событийной (event-driven ) модели программирования появляется множество асинхронных вызовов, которые не всегда должны выполняться асинхронно. Чтобы упорядочить этот хаос я написал небольшой модуль, реализующий очередь.


/**
 * @method  add       добавить функцию в очередь
 * @method  interate  выполнить первую в очереди функцию и удалить ее из очереди
 * @method  clear     очистить очередь
 */
var Queue = function () {
    this.members = [];
};
Queue.prototype = {
    add: function (f) {
        if (f instanceof Function) {
            this.members.push(f);
        }
    },
    iterate: function () {
        if (this.members.length > 0) {
            var func = this.members.shift();
            func.call(this);
        }
    },
    clear: function () {
        this.members = [];
    }
};

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


function () {
    // сохраняем контекст (очередь) в локальную переменную
    var queue = this;
    $.get('data.xml', function (data) {
        // в этом месте this уже не ссылается на очередь
        process(data);
        // подолжаем исполнение очереди
        queue.iterate();
    });
}

Комментарии к заметке: 1