Автоматически обрезать пробелы по всем наблюдаемым значениям
У меня есть ViewModel в нокауте, который получен в основном из плагина сопоставления (то есть динамически). Это прекрасно работает. Тем не менее, теперь мой клиент хочет, чтобы я убедился, что все входы имеют пробелы, отделенные от перед отправкой на сервер. Очевидно, что код обрезки очень прост, но, будучи относительно новым для Knockout, я точно не знаю, где разместить этот код. Я читал о extenders, но это кажется довольно многословным и повторяющимся, чтобы вернуться назад и добавить это к каждому наблюдаемому. Плюс я даже не уверен, что могу сделать это для динамически генерируемых наблюдаемых (a la, плагин отображения).
Есть ли какой-либо центральный механизм, который я могу расширить/переопределить, где я могу вставлять некоторый код обрезки каждый раз, когда наблюдаемые изменения? В основном я стараюсь избегать часов, потраченных на все наши формы и добавляя специальный синтаксис привязки в HTML, если мне это не нужно.
Спасибо.
Ответы
Ответ 1
У меня была та же проблема. Я написал расширение, поэтому вы можете вызвать trimmed
в своей модели просмотра, не изменяя привязки. Например:
var vm = {
myValue: ko.observable('').trimmed()
}
Расширение:
ko.subscribable.fn.trimmed = function() {
return ko.computed({
read: function() {
return this().trim();
},
write: function(value) {
this(value.trim());
this.valueHasMutated();
},
owner: this
});
};
Код на JSFiddle с примерами.
Ответ 2
На всякий случай, если кто-то столкнется с этой проблемой с более новыми версиями Knockout, текущий топ-ранжированный ответ будет работать неправильно.
Здесь обновлен fiddle и код, чтобы отобразить необходимые изменения:
ko.subscribable.fn.trimmed = function() {
return ko.computed({
read: function() {
return this().trim();
},
write: function(value) {
this(value.trim());
this.valueHasMutated();
},
owner: this
}).extend({ notify: 'always' });
};
Если кто-нибудь знает, зачем нужен extend
, пожалуйста, дайте мне знать. Мне потребовалось бесконечно, чтобы понять, почему он не работает правильно в Knockout 3.1.0
Ответ 3
Вы можете написать пользовательскую привязку, которая обрезает наблюдаемую. Что-то похожее на это
http://jsfiddle.net/belthasar/fRjdq/
Ответ 4
Используя решение Joe в качестве отправной точки, мы внедрили его несколько иначе.
Примечание:
- В круглых скобках
ko.observable()
нет ничего
- Новая функция чтения
trimmed
просто возвращает this()
и не получает никаких исключений типа null или undefined.
Код модели:
var vm = {
myValue: ko.observable().trimmed()
}
Расширение:
ko.subscribable.fn.trimmed = function() {
return ko.computed({
read: function() {
return this();
},
write: function(value) {
this(value.trim());
this.valueHasMutated();
},
owner: this
});
};
Ответ 5
Вы можете создать настраиваемую привязку, которая вызывает внутреннюю привязку value
, или вы можете перезаписать привязку value
для автоматической обрезки до того, как она действительно привязится (не рекомендуется).
Основная идея:
- Перехватить привязку
value
- Оберните пройденный наблюдаемый в
computed
- Сделайте привязку
read
и write
от вычисленного вместо исходного наблюдаемого
- Когда приходит новый вход, обрезайте его, прежде чем мы его напишем
- Когда изменяется значение модели, обрезайте его и обновите как модель, так и UI при необходимости
ko.bindingHandlers.trimmedValue = {
init: function(element, valueAccessor, allBindings) {
const ogValue = valueAccessor();
let newVa = valueAccessor;
// If this is a type="text" element and the data-bound value is observable,
// we create a new value accessor that returns an in-between layer to do
// our trimming
if (element.type === "text" && ko.isObservable(ogValue)) {
const trimmedValue = ko.observable().extend({"trim": true});
// Write to the model whenever we change
trimmedValue.subscribe(ogValue);
// Update when the model changes
ogValue.subscribe(trimmedValue);
// Initialize with model value
trimmedValue(ogValue());
// From now on, work with the trimmedValue
newVa = () => trimmedValue;
}
// Note: you can also use `ko.applyBindingsToNode`
return ko.bindingHandlers.value.init(element, newVa, allBindings)
}
}
// Our observable to check our results with
var myObs = ko.observable("test ");
myObs.subscribe(function(newValue) {
console.log("Change: \"" + newValue + "\"");
});
// The extender that does the actual trim
ko.extenders.trim = function(target, option) {
return ko.computed({
read: target,
write: function(val) {
target(
val && typeof val.trim === "function"
? val.trim()
: val
);
// This makes sure the trimming always resets the input UI
if (val !== target.peek()) {
target.valueHasMutated();
}
}
}).extend({notify: "always"});
};
ko.applyBindings({
myObs: myObs
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<h4><code>type="text" trimmedValue</code></h4>
<input type="text" data-bind="trimmedValue: myObs">
Ответ 6
Альтернативный подход, который хорошо работает для нас - обрезать, когда поле редактируется:
$(document.body).on('blur', 'input, textarea', function () { this.value = this.value.trim(); $(this).trigger('change'); });
Триггер события 'change' гарантирует, что KO обнаружит изменение (протестировано с KO v2).