
Для отслеживания изменений, в объекте ViewModel, может помочь dirtyFlag, входящий в состав библиотеки KoLite, которая в свою очередь расширяет возможности knockout, добавляя новые возможности:
- dirtyFlag — предназначен для отслеживания изменений как в отдельных, так и в совокупности свойств ViewModel
- asyncCommand — предназначается для выполнения асинхронных команд
- activity — предназначена для индикации активности. Используется совместно с asyncCommand
Описание
Пакет библиотеки расширения knockout написали два человека: Hans Fjällemark и John Papa
Посмотреть исходные коды и примеры можно тут.
Пример использования dirtyFlag для детектирования изменений в объекте ViewModel
Для примера возьмем вот такой объект ViewModel:
function Vm(){ var user = { name : ko.observable('Alexey'), email : ko.observable('alexey@mail.me'), address : ko.observable('Moskow st. Petrov 12'), awards : ko.observableArray([new Award('silver','The best speaker'), new Award('gold','Brain Storm #2')]) }, dirtyFlag = ko.DirtyFlag(user); return { user : user, dirtyFlag : dirtyFlag, modelStatus : ko.computed(function(){ return dirtyFlag().isDirty() ? "Changes detected" : "No changes"; }), clearDirty : function(){ dirtyFlag().reset(); } } };
В данной ViewModel используется свойство
dirtyFlag, которое будет отслеживать изменения значений внутри составного свойства user. Для того чтобы указать что именно на свойстве user будет производиться трекинг, используется конструкция вида: ko.DirtyFlag(user);. После биндинга модели, dirtyFlag будет отслеживать изменения в свойстве user. Для того чтобы получить статус «загрязненности» данных нужно вызвать dirtyFlag().isDirty(). isDirty() возвращает true если обнаружены изменения и false в противном случае. Если пользователь произвел изменения, а затем вернул данные в первоначальное состояние, то isDirty вернет false, так как сохраняет первоначальное состояние анализируемых свойств (т.е. копию данных объекта). Это хорошо видно, если посмотреть на код dirtyFlag. Ниже привожу полный код примера, в котором можно убедиться в возможностях dirtyFlag.
HTML код примера
<div class ="userform" data-bind="with: user"> <h4>Test user information</h4> <label>Name</label> <input type="text" data-bind="value: name"/> <label>E-mail</label> <input type="text" data-bind="value: email"/> <label>Address</label> <input type="text" data-bind="value: address"/> <div class="awards-title"> <h4>Awards:</h4> </div> <div class="userAwardForm" data-bind="foreach: awards"> <div class="userAwardForm"> <label>Type of award:</label> <input type="text" data-bind="value: awardType"> <label>Name of Contest</label> <input type="text" data-bind="value: contestName"> </div> </div> </div> <div class="dirty-status"> <span>Dirty status: </span><a href="#" data-bind="text: modelStatus(), css: {ok : !dirtyFlag().isDirty(), nok : dirtyFlag().isDirty()}" class="modern"></a> </div> <input type="button" class="Button" value="Reset dirty status" data-bind="click: clearDirty">
JavaScript код примера
$(function() { function Award(atp, contestNamev){ var awardType = ko.observable(atp), contestName = ko.observable(contestNamev); return { awardType : awardType, contestName : contestName } } function Vm(){ var user = { name : ko.observable('Alexey'), email : ko.observable('alexey@mail.me'), address : ko.observable('Moskow st. Petrov 12'), awards : ko.observableArray([new Award('silver','The best speaker'), new Award('gold','Brain Storm #2')]) }, dirtyFlag = ko.DirtyFlag(user); return { user : user, dirtyFlag : dirtyFlag, modelStatus : ko.computed(function(){ return dirtyFlag().isDirty() ? "Changes detected" : "No changes"; }), clearDirty : function(){ dirtyFlag().reset(); } } }; ko.applyBindings(new Vm()); });
CSS код примера
body{ background-color: #456192 ; margin:20px; } label{ display:block; color: white; } div.userform, div.userAwardForm{ padding: 10px; border: solid white 1px; } div.userAwardForm{ margin-top: 10px; } div.userform h4{ font-weight : bold; font-size:24px; margin-bottom: 15px; color: white; } .awards-title{ margin-top:10px; } .modern { height: 40px; padding: 0 5px; line-height: 40px; font-size: 14px; font-weight: bold; color: white; text-shadow: -1px -1px 0 #333; border-radius: 3px; text-decoration:none; } .ok{ background: -moz-linear-gradient(#84AB69, #334823); background: -webkit-linear-gradient(#84AB69, #334823); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#84AB69', endColorstr='#334823')"; } .nok{ background: -moz-linear-gradient(#ED3812, #7A1F05); background: -webkit-linear-gradient(#ED3812, #7A1F05); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ED3812', endColorstr='#7A1F05')"; } .dirty-status{ height: 50px; } .dirty-status span{ color: white; } .Button { position: relative; cursor: pointer; display: inline-block; border: 1px solid #d5d5d5; text-decoration: none; font: 16px Arial; font-weight: bold; text-align: center; text-shadow:0px 0px 0px #444444; background: #ffffff; padding: 10px 16px; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; color: #c13737; -moz-box-shadow:inset 0px 0px 0px; -webkit-box-shadow:inset 0px 0px 0px; box-shadow:inset 0px 0px 0px, 0 3px 0 #aeaeae, 0 5px 0 #c13737, 0 8px 0 #aeaeae, 0 10px 6px rgba(0,0,0, .60); } .Button:hover { background: #eeeeee; color: #eb5e7f; -moz-box-shadow:inset 0px 0px 0px; -webkit-box-shadow:inset 0px 0px 0px; box-shadow:inset 0px 0px 0px , 0 3px 0 rgba(235,94,127, .30), 0 3px 0 #aeaeae, 0 5px 5px #eb5e7f, 0 5px 0 #eb5e7f, 0 8px 0 rgba(235,94,127, .15), 0 8px 0 #aeaeae, 0 10px 6px rgba(0,0,0, .60); } .Button:active { transform: translateY(2px); -moz-transform: translateY(2px); -ms-transform: translateY(2px); -o-transform: translateY(2px); -webkit-transform: translateY(2px); -moz-box-shadow:inset 0px 0px 0px; -webkit-box-shadow:inset 0px 0px 0px; box-shadow:inset 0px 0px 0px, 0 3px 0 rgba(235,94,127, .30), 0 3px 0 #eeeeee, 0 5px 5px rgba(#eb5e7f, .65), 0 5px 0 #eb5e7f, 0 6px 0 rgba(235,94,127, .15), 0 6px 0 #eeeeee, 0 6px 6px rgba(0,0,0, .65); }
В примере, в качестве индикации выводятся надписи: «No changes» — если изменения не обнаружены и «Changes detected» — если изменения найдены. А так же производится изменение фона на котором выводятся надписи для индикации.
На форме примера расположена кнопка«Reset dirty status», которая и производит демонстрацию работы метода
reset у свойства dirtyFlag ViewModel. Для того чтобы текущие изменения были приняты, как базовые, нужно произвести вызов dirtyFlag().reset();.Заключение
Думаю что данная библиотека поможет с легкостью отслеживать изменения на необходимых свойствах ViewModel.
Спасибо за внимание!
P.S. Картинку к посту нарисовал сам :)
