Для отслеживания изменений, в объекте 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. Картинку к посту нарисовал сам :)