wake-up-neo.com

kombinieren Sie dynamische und statische Klassen durch css binding, knockout.js

In knockout.js können wir die css-Bindung für statische Klassen verwenden

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

und dynamisch

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

Ich habe versucht http://jsfiddle.net/tT9PK/1/ in so etwas zu kombinieren 

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

um die dynamische Klasse color und die statische translucent gleichzeitig zu erhalten, aber ich erhalte eine Fehlermeldung. Gibt es eine Möglichkeit, dies zu tun?

44
Artem Svirskyi

Sie können eine dynamische Klasse über die css-Eigenschaft hinzufügen und dann eine statische Klasse über die attr-Eigenschaft hinzufügen

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

Stellen Sie sicher, dass Sie vordefinierte Klassen zu dieser Bindung hinzufügen attr: { 'class': color }

57
Aleksey

Ich habe dieses Problem vor einiger Zeit gelöst, indem ich einfach die css-Bindung als css2 geklont habe. 

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

Normalerweise können Sie denselben Bindungshandler nicht zweimal in einem Data-Bind-Attribut verwenden. Daher konnte ich Folgendes tun:

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

Ich kann mich nicht ganz entscheiden, ob ich das oder die Antwort von Aleksey immer noch vorziehen möchte. Dies kann jedoch die einzige Wahl sein, wenn Sie mehrere dynamische Klassen hinzufügen.

14
Simon_Weaver

Korrigieren ... und um Sie noch weiter zu starten, überprüfen Sie diese Änderung. 

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

Hier sehen Sie, dass wir nicht nur die Optionen kombinieren, sondern auch unsere eigene Bindung vollständig erstellen. Dies führt zu einer wesentlich mobileren Erweiterung nicht nur dieses Ansichtsmodells, sondern jedes Ansichtsmodells, das Sie möglicherweise haben Ihr Projekt ... Sie müssen dieses nur einmal schreiben!

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

Um dies aufzurufen, verwenden Sie es einfach als neue Data-Bind-Eigenschaft und können so viele (oder so wenige) Optionen wie möglich enthalten. Unter dieser speziellen Bedingung habe ich vielleicht $ data angegeben. Wenn Sie jedoch eine wiederverwendbare Option wünschen, müssen Sie genauer festlegen, welche Datentypen Sie als Parameter benötigen, und nicht alle Ansichtsmodelle verfügen möglicherweise über dieselben Eigenschaften.

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

Hoffen Sie, dass dies mehr als nur Ihre Frage beantwortet!

4
beauXjames

Ihre beste Wette ist wahrscheinlich, sie nicht zu kombinieren. Verwenden Sie stattdessen eine berechnete Eigenschaft Ihres Ansichtsmodells, um sie zu einer einzigen Eigenschaft zu kombinieren, die Sie dynamisch binden können. Auf diese Weise können Sie auch vermeiden, Logik mit der Bindung number () <10 in Ihre Ansicht zu setzen, die ohnehin sauberer ist. 

So zum Beispiel:

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

Siehe dieses Arbeitsbeispiel: http://jsfiddle.net/tT9PK/4/

3
Matt Burland

Wenn Sie wirklich in einen komplizierten Stilfall geraten, sammeln Sie einfach alles in der berechneten Eigenschaft an. Du kannst es wie Alex erwähnt oder etwas lesbarer machen:

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(' ');
});

der Hauptnachteil ist, dass Sie einen Teil der Ansichtsdefinition in das Ansichtsmodell verschieben, das unabhängiger sein sollte. Die Alternative besteht darin, die gesamte Logik als einfachen js-Funktionsaufruf bereitzustellen und durch Auswerten auswerten zu lassen.

2
mikus

Schöne Frage, das Problem scheint zu sein, dass die Bindung css nicht dazu gedacht ist, die beiden Arten zu mischen, color(): color() != '' funktioniert nicht (wäre nett).

Ich mag die Antwortmethode von @ Simon_waver, einfach und praktisch.

Vielleicht wurde zum Zeitpunkt der Frage nicht unterstützt (Idk), aber mit aktuellem knockout funktioniert auch das Kombinieren der Klassen: data-bind="css: computed"

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

Ein paar weitere Optionen:

Ähnlich wie die Vorschläge zur Verwendung eines berechneten Befehls können Sie den Ausdruck einbetten:

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

Als Alternative zu einem benutzerdefinierten Bindungshandler, der für diesen Fall spezifisch ist, können Sie einen erstellen, der ein Array mit gemischten CSS-Spezifikationen übernimmt und an den ursprünglichen CSS-Handler übergibt:

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

Der Handler:

 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);
      }
    }
  }

Fiddle Demo

1
Roy J

Es gibt eine elegantere Lösung für dieses Problem durch berechnete Eigenschaftsnamen (für FF> 34, Chrome, Safari> 7.1 ):

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

Während color eine Eigenschaft mit einem String-Wert ist.

Wenn der Wert von color beobachtbar ist, müssen Sie den Klassennamen vor den beobachtbaren Aktualisierungen löschen. Wenn wir dies nicht tun, wird jede Änderung eine andere Klasse hinzufügen und die vorherige nicht entfernen. Dies kann leicht manuell durchgeführt werden, aber ich habe eine Erweiterung für die Interessenten geschrieben.

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;
};

Mit dieser Erweiterung würde Ihr JavaScript-Code folgendermaßen aussehen:

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

Und Ihr Markup wäre so einfach:

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

Ich habe einen Code-Stift, der diese Technik demonstriert: http://codepen.io/USIUX/pen/WryGZQ

Ich habe auch ein Problem mit dem Knockout eingereicht, in der Hoffnung, dass eines Tages der benutzerdefinierte Extender nicht erforderlich sein wird: https://github.com/knockout/knockout/issues/1990

1
Kenneth Moore

Ich würde den css-Bindungswert in Ihrem Viewmodel erstellen. Sie können ein computed definieren, das entweder ein Objekt oder eine Zeichenfolge zurückgibt.

Einige Beispiele mit ES2015:

const App = function() {
  this.number = ko.observable(12);
  this.color = ko.observable("red");
  
  this.cssConfigObj = ko.pureComputed(() => ({
    "italic": this.number() > 10,
    [this.color()]: true
  }));
  
  this.cssConfigStr = ko.pureComputed(() => 
    `${this.color()} ${this.number() > 10 ? "italic" : ""}`
  );
};

ko.applyBindings(new App());
.monospaced {
  font-family: monospace;
}

.italic {
  font-style: italic;
}

.red {
  color: red; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div
  class="monospaced"
  data-bind="css: cssConfigObj"
>Hello world</div>

<div
  class="monospaced"
  data-bind="css: cssConfigStr"
>Hello world</div>

0
user3297291