web-dev-qa-db-fra.com

combine des classes dynamiques et statiques via une liaison css, knockout.js

Dans knockout.js, nous pouvons utiliser la liaison css pour les classes statiques

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

et dynamique

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

J'ai essayé http://jsfiddle.net/tT9PK/1/ / le combiner dans quelque chose comme 

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

pour obtenir la classe dynamique color et la statique translucent en même temps, mais je reçois une erreur Y-a-t-il un moyen de faire ça?

44
Artem Svirskyi

Vous pouvez ajouter une classe dynamique par la propriété css, puis ajouter une classe statique par la propriété attr.

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

Assurez-vous d'ajouter toutes les classes prédéfinies à cette liaison attr: { 'class': color }

57
Aleksey

Il y a quelque temps, j'ai résolu ce problème en clonant simplement la liaison css en tant que css2

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

Normalement, vous ne pouvez pas utiliser le même gestionnaire de liaison deux fois dans un attribut de liaison de données. Cela m'a donc permis d'effectuer les opérations suivantes:

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

Je ne peux pas vraiment décider si je préfère toujours ceci ou la réponse de @ Aleksey, mais cela peut être le seul choix si vous avez plusieurs classes dynamiques à ajouter.

14
Simon_Weaver

Corrigez ... et pour vous lancer encore plus, vérifiez cette modification. 

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

Ici, vous verrez que non seulement nous combinons les options, mais que nous créons entièrement notre propre liaison ... ce qui se traduit par une extension beaucoup plus portable de ce modèle de vue, mais également de tout modèle de vue que vous pouvez avoir. votre projet ... vous n'aurez donc besoin de l'écrire qu'une fois!

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

Pour l'invoquer, il vous suffit de l'utiliser comme nouvelle propriété de liaison de données et d'inclure autant (ou aussi peu) d'options que possible. Sous cette condition spécifique, je viens peut-être de fournir $ data. Toutefois, si vous souhaitez une option réutilisable, vous devez préciser les types de données dont vous avez besoin en tant que paramètres. Tous les modèles d'affichage ne peuvent pas avoir les mêmes propriétés.

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

J'espère que cela fait plus que répondre à votre question!

4
beauXjames

Votre meilleur pari est probablement de ne pas les combiner. Utilisez plutôt une propriété calculée de votre modèle de vue pour les combiner en une seule propriété que vous pouvez lier de manière dynamique. De cette façon, vous pouvez également éviter de mettre de la logique dans votre vue avec la liaison number () <10, ce qui est plus clair de toute façon. 

Comme ceci, par exemple:

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

Voir cet exemple de travail: http://jsfiddle.net/tT9PK/4/

3
Matt Burland

Si vous êtes vraiment dans le cas d'un style complexe, accumulez simplement tout dans la propriété calculée. Vous pouvez le faire comme mentionné par Alex ou un peu plus lisible:

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

l'inconvénient principal est que vous déplacez une partie de la définition de vue dans le modèle de vue, ce qui devrait être plus indépendant. L’alternative est de fournir toute la logique ci-dessus sous la forme d’un appel de fonction simple et de l’analyser par knock-out.

2
mikus

Belle question, le problème semble être la liaison css n'est pas pensé pour mélanger les deux types, color(): color() != '' ne fonctionne pas (serait Nice).

J'aime l'approche de réponse de @ Simon_waver, simple et pratique.

Peut-être qu'au moment de la question n'était pas pris en charge (Idk), mais avec knockout actuel, la combinaison des classes fonctionne également: data-bind="css: computed"

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

Quelques autres options:

Semblable aux suggestions pour utiliser un calcul, vous pouvez en ligne l'expression:

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

En guise d'alternative à un gestionnaire de liaison personnalisé spécifique à ce cas, vous pouvez en créer un qui prend un tableau de spécifications css mélangées et les transmet au gestionnaire css d'origine:

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

Le gestionnaire:

 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 démo

1
Roy J

Il existe une solution plus élégante à ce problème via les noms de propriété calculés (pour FF> 34, Chrome, Safari> 7.1 ):

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

Alors que color est une propriété avec une valeur de chaîne.

Si la valeur de color est observable, nous devons effacer le nom de la classe avant les mises à jour observables. Si nous ne le faisons pas, chaque modification ajoutera une autre classe et ne supprimera pas la précédente. Cela peut facilement être accompli manuellement, mais j’ai écrit une extension pour ceux qui sont intéressés.

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

Avec cette extension, votre code JavaScript ressemblerait à ceci:

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

Et votre balisage serait aussi simple:

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

J'ai un stylo de code démontrant cette technique: http://codepen.io/USIUX/pen/WryGZQ

J'ai également soumis un problème avec knockout dans l'espoir qu'un jour l'extension personnalisée ne sera pas nécessaire: https://github.com/knockout/knockout/issues/1990

1
Kenneth Moore

Je créerais la valeur de liaison css dans votre modèle de vue. Vous pouvez définir une computed qui retourne un objet ou une chaîne.

Quelques exemples, en utilisant 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