Объединить динамические и статические классы через css binding, knockout.js

В knockout.js мы можем использовать привязку css для статических классов

<div data-bind="css: {'translucent ': number() < 10}">static dynamic css classes</div>

и динамический

<div data-bind="css: color">static dynamic css classes</div>

Я попытался http://jsfiddle.net/tT9PK/1/ объединить его в нечто вроде

css: {color, translucent: number() < 10}

чтобы получить динамический класс color и статический translucent в то же время, но я получаю сообщение об ошибке. Есть ли способ сделать это?

Ответ 1

Вы можете добавить динамический класс по свойству css, а затем добавить статический класс с помощью свойства attr

<div data-bind="attr: { 'class': color }, css: { 'translucent': number() < 10 }">
  static dynamic css classes
</div>

Обязательно добавьте все предопределенные классы к этой привязке attr: { 'class': color }

Ответ 2

Я решил эту проблему некоторое время назад, просто клонируя привязку css как css2.

 ko.bindingHandlers['css2'] = ko.bindingHandlers.css;

Обычно вы не можете использовать один и тот же обработчик привязки дважды в атрибуте привязки данных, поэтому это позволило мне сделать следующее:

<div data-bind="css: color, css2: { 'translucent': number() < 10 }">static dynamic css classes</div>

Я не могу решить, предпочитаю ли я это, или ответ @Aleksey, но это может быть единственным выбором, если у вас есть несколько динамических классов для добавления.

Ответ 3

Правильно... и еще больше запустите вас, проверьте эту модификацию.

http://jsfiddle.net/Fv27b/2/

Здесь вы увидите, что мы не только объединяем параметры, но и полностью создаем нашу привязку... что приводит к гораздо более переносимому расширению не только этой модели представлений, но и любой модели просмотра может быть в вашем проекте... так что вам нужно будет только один раз написать это!

ko.bindingHandlers.colorAndTrans = {
    update: function(element, valAccessor) {
        var valdata = valAccessor();
        var cssString = valdata.color();
        if (valdata.transValue() < 10) cssString += " translucent";
        element.className = cssString;
    }
}

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

data-bind="colorAndTrans: { color: color, transValue: number }"

Надеюсь, что это больше, чем ответ на ваш вопрос!

Ответ 4

Лучше всего, наверное, не комбинировать их. Вместо этого используйте вычисленное свойство вашей модели представления, чтобы объединить их в одно свойство, которое можно связать динамически. Таким образом, вы также можете не вводить логику в свое представление с помощью номера() < 10, что в любом случае является более чистым.

Подобно этому, например:

viewModel.colorAndTrans = ko.computed(function () {
    var cssString = viewModel.color();
    if (viewModel.number() < 10) {
        cssString += " translucent"
    }
    return cssString;
});

См. этот рабочий пример: http://jsfiddle.net/tT9PK/4/

Ответ 5

Если вы действительно попадаете в сложный стиль, просто накапливайте все в вычисленном свойстве. Вы можете сделать это, как сказал Алекс, или немного более читаемым:

vm.divStyle = ko.computed(function() {
        var styles = [];

        if (vm.isNested()) styles.push('nested');
        if (vm.isTabular()) styles.push('tabular');
        else styles.push('non-tabular');
        if (vm.color()) styles.push(vm.color());

        return styles.join(' ');
});

Основной недостаток заключается в том, что вы перемещаете часть определения вида в модель представления, которая должна быть более независимой. Альтернативой является предоставление всей логики выше как простой вызов функции js, и пусть нокаут оценит ее.

Ответ 6

Еще пара опций:

Подобно предложениям по использованию вычисленных, вы можете вставить выражение:

<div data-bind="css: [color(), (number() < 10 ? 'translucent' : 'notTranslucent')].join(' ')">static dynamic css classes</div>

В качестве альтернативы настраиваемому обработчику привязки, специфичному для этого случая, вы можете создать тот, который принимает массив смешанных спецификаций css и передает их в исходный обработчик css:

<div data-bind="cssArray: [color, {translucent: number() < 10}]">static dynamic css classes</div>

Обработчик:

 ko.bindingHandlers.cssArray = {
    update: function (element, valueAccessor, allBindingsAccessor, data, context) {
        var arr = ko.unwrap(valueAccessor());
      for (var i=0; i<arr.length; ++i) {
        var wrapped = function () { return ko.unwrap(arr[i]) };
        ko.bindingHandlers.css.update(element, wrapped, allBindingsAccessor, data, context);
      }
    }
  }

Скриншот демо

Ответ 7

Существует более элегантное решение этой проблемы с помощью вычисленных имен свойств (для FF > 34, Chrome, Safari > 7.1):

<div data-bind="css: { [color]: true,'translucent': number() < 10 }">
    static dynamic css classes
</div>

В то время как color - это свойство со строковым значением.

Если значение color является наблюдаемым, нам нужно очистить имя класса до этого наблюдаемого обновления. Если мы этого не сделаем, каждое изменение добавит еще один класс и не удалит предыдущий. Это можно легко выполнить вручную, но я написал расширитель для тех, кто заинтересован.

ko.extenders.css = function(target, value) {
  var beforeChange;
  var onChange;

  //add sub-observables to our observable
  target.show = ko.observable(true);

  beforeChange = function(oldValue){
    target.show(false);
  }
  onChange = function(newValue){
    target.show(true);
  }
  target.subscribe(beforeChange, null, "beforeChange");
  target.subscribe(onChange);
  return target;
};

С помощью этого расширителя ваш код JavaScript будет выглядеть следующим образом:

function MyViewModel() {
    this.color = ko.observable("red").extend({ css: true });
    this.number = ko.observable(9)
};

И ваша разметка будет такой простой:

<div data-bind="css: { [color()]: color.show(),'translucent': number() < 10 }">
    static dynamic css classes
</div>

У меня есть кодовое перо, демонстрирующее эту технику: http://codepen.io/USIUX/pen/WryGZQ

Я также представил вопрос с нокаутом в надежде, что однажды пользовательский расширитель не понадобится: https://github.com/knockout/knockout/issues/1990

Ответ 8

Хороший вопрос, проблема, похоже, связана с привязкой css, которая не смешивает два типа, color(): color() != '' не работает (было бы хорошо).

Мне нравится подход @Simon_waver, простой и практичный.

Возможно, на момент ответа вопрос не поддерживался (Idk), но с текущим нокаутом, также объединяющим классы: data-bind="css: computed"

viewModel.computed = ko.pureComputed(function() {
   return viewModel.color() + (viewModel.number() < 10 ? ' translucent' : '');
});