Knockout.js предоставляет мощный механизм привязки данных через observable и computed свойства, что позволяет легко синхронизировать интерфейс с удалёнными источниками данных. Для интеграции с REST API ключевым элементом является организация асинхронных запросов и обновление view model на основе полученной информации.
Первым шагом является определение view model с observable-свойствами, которые будут автоматически обновлять интерфейс при изменении данных. Например:
function UserModel(data) {
this.id = ko.observable(data.id);
this.name = ko.observable(data.name);
this.email = ko.observable(data.email);
}
Для коллекций удобно использовать observableArray, которая отслеживает добавление, удаление и изменение элементов:
function AppViewModel() {
this.users = ko.observableArray([]);
this.isLoading = ko.observable(false);
this.error = ko.observable(null);
}
Knockout.js не содержит встроенных инструментов для работы с AJAX,
поэтому обычно используются fetch или сторонние
библиотеки, например, Axios. Пример с
fetch:
AppViewModel.prototype.loadUsers = function() {
const self = this;
self.isLoading(true);
self.error(null);
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) throw new Error('Ошибка загрузки данных');
return response.json();
})
.then(data => {
const mappedUsers = data.map(user => new UserModel(user));
self.users(mappedUsers);
})
.catch(err => self.error(err.message))
.finally(() => self.isLoading(false));
};
Ключевые моменты:
self = this обеспечивает корректную
работу внутри промисов.push,
remove, splice.Интеграция с REST API часто требует операций CRUD. Для каждой операции создаются методы view model:
AppViewModel.prototype.addUser = function(newUserData) {
const self = this;
fetch('https://api.example.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newUserData)
})
.then(response => response.json())
.then(data => self.users.push(new UserModel(data)))
.catch(err => self.error(err.message));
};
AppViewModel.prototype.updateUser = function(user) {
fetch(`https://api.example.com/users/${user.id()}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: user.name(),
email: user.email()
})
})
.catch(err => self.error(err.message));
};
AppViewModel.prototype.deleteUser = function(user) {
const self = this;
fetch(`https://api.example.com/users/${user.id()}`, { method: 'DELETE' })
.then(() => self.users.remove(user))
.catch(err => self.error(err.message));
};
Особенности:
isLoading и
error.Knockout.js позволяет автоматически обновлять DOM при изменении observable. Для работы с REST API полезны следующие привязки:
<div data-bind="visible: isLoading">Загрузка...</div>
<div data-bind="visible: error, text: error"></div>
<ul data-bind="foreach: users">
<li>
<span data-bind="text: name"></span> — <span data-bind="text: email"></span>
<button data-bind="click: $parent.deleteUser">Удалить</button>
</li>
</ul>
<button data-bind="click: loadUsers">Обновить список</button>
Принципы работы:
ko.computed, чтобы вычислять
значения на основе массива без ручного перебора элементов.Knockout.js хорошо сочетается с современными подходами работы с асинхронностью:
AppViewModel.prototype.loadUsersAsync = async function() {
const self = this;
self.isLoading(true);
self.error(null);
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) throw new Error('Ошибка загрузки данных');
const data = await response.json();
self.users(data.map(user => new UserModel(user)));
} catch (err) {
self.error(err.message);
} finally {
self.isLoading(false);
}
};
Использование async/await делает код более линейным и
удобочитаемым, особенно при цепочках запросов или сложной обработке
данных.
Для приложений, которые требуют постоянного обновления данных с сервера, можно сочетать Knockout.js с WebSocket или Server-Sent Events (SSE):
const eventSource = new EventSource('https://api.example.com/users/stream');
eventSource.onmess age = function(event) {
const updatedUser = new UserModel(JSON.parse(event.data));
const existingUser = appViewModel.users().find(u => u.id() === updatedUser.id());
if (existingUser) {
existingUser.name(updatedUser.name());
existingUser.email(updatedUser.email());
} else {
appViewModel.users.push(updatedUser);
}
};
Это обеспечивает мгновенное обновление данных без повторных запросов к REST API.