web-dev-qa-db-fra.com

Liaison KnockoutJS lorsque la source est nulle / non définie

Existe-t-il un moyen plus court/plus propre de faire le test nul/non défini?

<select data-bind="options: SelectedBusinessLine() ? SelectedBusinessLine().Clusters() : [],
                               optionsText: 'Title',
                               value: SelectedCluster,
                               optionsCaption: 'Select Cluster..'">
            </select>

Au lieu de

data-bind="options: SelectedBusinessLine() ? SelectedBusinessLine().Clusters() : [],

j'aimerais

data-bind="options: SelectedBusinessLine().Clusters(),

donner ou prendre le ()

Ou au moins une vérification de l'opérateur null plus simple '??' SelectedBusinessLine ?? []

Ou un paramètre de liaison pour vérifier automatiquement l'échec nul ou silencieux.

Des idées si c'est possible?

41
Zapacila

Cette page propose plusieurs solutions. La partie pertinente est celle-ci:

Protection contre les objets nuls

Si vous avez un observable qui contient un objet et que vous souhaitez vous lier aux propriétés de cet objet, vous devez être prudent s'il y a une chance qu'il puisse être nul ou non défini. Vous pouvez écrire votre reliure comme:

<span data-bind="text: selectedItem() ? selectedItem().name() : 'unknown'"></span>

Il existe plusieurs façons de gérer celle-ci. La manière préférée serait d'utiliser simplement la liaison de modèle:

var viewModel = {
  items: ko.observableArray(),
  selectedItem: ko.observable()
};

<ul data-bind="template: { name: 'editorTmpl', data: selectedItem }"></ul>
<script id="editorTmpl" type="text/html">
  <li>
    <input data-bind="value: name" />
  </li>
</script>

Avec cette méthode, si selectedItem est nul, alors il ne rendra rien. Ainsi, vous ne verriez pas d'inconnu comme vous l'auriez fait dans la reliure d'origine. Cependant, cela a l'avantage supplémentaire de simplifier vos liaisons, car vous pouvez maintenant simplement spécifier directement les noms de vos propriétés plutôt que selectedItem().name. C'est la solution la plus simple.

Juste pour explorer certaines options, voici quelques alternatives:

Vous pouvez utiliser un observable calculé, comme nous l'avons fait auparavant.

viewModel.selectedItemName = ko.computed(function() {
  var selected = this.selected();
  return selected ? selected.name() : 'unknown';
}, viewModel);

Cependant, cela ajoute une nouvelle fois à notre modèle de vue que nous ne voulons peut-être pas et nous devrons peut-être répéter cela pour de nombreuses propriétés.

Vous pouvez utiliser une liaison personnalisée comme:

<div data-bind="safeText: { value: selectedItem, property: 'name', default: 'unknown' }"></div>

ko.bindingHandlers.safeText = {
  update: function(element, valueAccessor, allBindingsAccessor) {
    var options = ko.utils.unwrapObservable(valueAccessor()),
    value = ko.utils.unwrapObservable(options.value),
    property = ko.utils.unwrapObservable(options.property),
    fallback = ko.utils.unwrapObservable(options.default) || "",
    text;

    text = value ? (options.property ? value[property] : value) : fallback;

    ko.bindingHandlers.text.update(element, function() { return text; });
  }
};

Est-ce mieux que l'original? Je dirais probablement que non. Il évite le JavaScript dans notre liaison, mais il est toujours assez détaillé.

Une autre option serait de créer un observable augmenté qui fournit un moyen sûr d'accéder aux propriétés tout en permettant à la valeur réelle d'être nulle. Pourrait ressembler à:

ko.safeObservable = function(initialValue) {
  var result = ko.observable(initialValue);
  result.safe = ko.dependentObservable(function() {
    return result() || {};
  });

  return result;
};

Il ne s'agit donc que d'un observable qui expose également un observable calculé nommé coffre-fort qui retournera toujours un objet vide, mais l'observable réel peut continuer à stocker null.

Maintenant, vous pouvez vous y lier comme:

<div data-bind="text: selectedItem.safe().name"></div>

Vous ne verriez pas la valeur inconnue lorsqu'elle est nulle, mais elle ne provoquerait pas au moins une erreur lorsque selectedItem est nulle.

Je pense que l'option préférée serait d'utiliser la liaison de modèle dans ce cas, surtout si vous avez plusieurs de ces propriétés pour vous lier.

74
user1494736

Une façon non mentionnée dans la sinon excellente page référencée par une autre réponse est d'utiliser with

<div data-bind="with: selecteditem">
    <form>
        <fieldset>
            <div>
                <label>first name</label>
                <input data-bind="value: firstname"></input>
            </div>
            <div>
                <label>lasst name</label>
                <input data-bind="value: lastname"></input>
            </div>
        </fieldset>
        <div>
            <a href="#" data-bind="click: $root.savechanges">Save</a>
        </div>
    </form>
</div>

Toute cette interface utilisateur disparaîtra si selecteditem est null.

59
Simon_Weaver

Les œuvres "With" (probablement les autres aussi) ...)

Mais avec le "Avec" l'interface utilisateur du rôle disparaît/apparaît même s'il y a des conditions à l'intérieur ... Par exemple ... J'ai besoin de définir un bouton Statut (vrai/faux), mais seulement si le statut n'est pas nul .. .

<!-- ko ifnot: Status() == null -->
<span data-bind="if: Status">
    <a class="rk-button rk-red-action" data-bind="click: changeStatus"><i class="rk-ico rk-ico-save"></i>Desativar</a>
</span>
<span data-bind="ifnot: Status">
    <a class="rk-button rk-black-action" data-bind="click: changeStatus"><i class="rk-ico rk-ico-save"></i>Ativar</a>
</span>
<!-- /ko -->

Cela marche. Dans ce cas.

Mais parfois, juste le "With" fonctionne comme Simon_Weaver l'a dit!

4

La plupart de ces solutions ne fonctionnent pas dans un certain cas pour moi, où je définis un attribut:

<div role="combobox" data-bind="attr: {
  'aria-activedescendant': activedescendant() ? activedescendant().id : null
}">
  ...
</div>

Les liaisons template et with ne fonctionneraient pas ici, car si je n'avais pas de descendant actif, mon div serait vide. Pour ma solution, j'ai créé une méthode observable:

ko.observable.fn.get = function (propertyName, defaultValue) {
    var
    self = this(),
    propertyValue;

    if (self != null) {
        propertyValue = ko.unwrap(self[propertyName]);
    }

    return propertyValue || defaultValue;
}

Ce qui me permet de changer ma reliure en ceci:

<div role="combobox" data-bind="attr: {
  'aria-activedescendant': activedescendant.get('id')}">
  ...
</div>
1
kzh

Je préfère cette méthode

Créer une liaison personnalisée

ko.bindingHandlers.safeText = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        try {
            var tryGetValue = valueAccessor()();
            ko.bindingHandlers.text.update(element, valueAccessor());
        } catch (e) {
            ko.bindingHandlers.text.update(element, function () { return ""; });
        }
    }
};

Usage

data-bind="safeText: function() { return my().nested.object.property; }

La seule chose supplémentaire que vous devez ajouter est d'envelopper votre valeur d'origine avec 'function () {return ...}'

Cependant, cela arrêtera toutes les erreurs sous l'appel de valeur. Vous pouvez améliorer la liaison personnalisée en recherchant uniquement des exceptions "non définies". Vous pouvez également améliorer cette liaison en ajoutant une option de texte par défaut.

1
kernowcode

La plupart des solutions ci-dessus n'ont pas fonctionné pour moi, car je ne voulais l'appliquer qu'à un seul élément dans un foreach, j'ai donc modifié un peu l'approche de la réponse acceptée:

<span data-bind="text: ((typeof info).localeCompare('undefined')) ? info : ''"></span>
1
Nit