Bedingte Logik in der AngularJS-Vorlage

71

Ich habe eine eckige Vorlage, die so aussieht ...

<div ng-repeat="message in data.messages" ng-class="message.type">

    <div class="info">
        <div class="type"></div>
        <div class="from">From Avatar</div>
        <div class="createdBy">Created By Avatar</div>
        <div class="arrowTo">
            <div class="arrow"></div>
            <div class="to">To Avatar</div>
        </div>
        <div class="date">
            <div class="day">25</div>
            <div class="month">Dec</div>
        </div>
    </div>

    <div class="main">
        <div class="content">
            <div class="heading2">{{message.title}}</div>
            <div ng-bind-html="message.content"></div>
        </div>

    </div>

    <br />
    <hr />
    <br />

</div>

Ich habe eine JSfiddle eingerichtet , um die gebundenen Daten anzuzeigen .

Was ich tun muss, ist, die Divisionen "von", "nach" und "Pfeil nach" abhängig vom Inhalt der Daten bedingt anzuzeigen.

Das Protokoll ist das ...

  • Wenn die Daten ein "from" -Objekt enthalten, zeigen Sie das "from" -Div an und binden Sie die Daten, zeigen Sie jedoch nicht das "createdBy" -Div an.
  • Wenn es kein "from" -Objekt gibt, aber ein "createdBy" -Objekt, zeigen Sie das div "createdBy" an und binden Sie die Daten.
  • Wenn die Daten ein "to" -Objekt enthalten, zeigen Sie das div "Pfeil nach" an und binden Sie die Daten.

Oder im Klartext, wenn es eine Absenderadresse gibt, zeigen Sie sie an, andernfalls zeigen Sie, wer den Datensatz stattdessen erstellt hat, und wenn es eine Absenderadresse gibt, zeigen Sie dies auch.

Ich habe mir überlegt, ng-switch zu verwenden, aber ich denke, ich müsste zusätzliches Markup hinzufügen, das ein leeres div hinterlässt, wenn es keine Daten gibt. Außerdem müsste ich Switch-Direktiven verschachteln und bin mir nicht sicher, ob das funktionieren würde.

Irgendwelche Ideen?

AKTUALISIEREN:

Wenn ich meine eigene Direktive schreiben würde (wenn ich wüsste wie!), Dann ist hier ein Pseudocode, um zu zeigen, wie ich ihn verwenden möchte ...

<div ng-if="showFrom()">
    From Template Goes Here
</div>
<div ng-if="showCreatedBy()">
    CreatedBy Template Goes Here
</div>
<div ng-if="showTo()">
    To Template Goes Here
</div>

Jedes von diesen würde verschwinden, wenn die Funktion / der Ausdruck als falsch bewertet würde.

Jonhobbs
quelle
Ich hatte mehr oder weniger die gleiche Frage und verwendete schließlich Angular UI und deren UI-IF-Implementierung. Es entfernte das, was ich wollte, aus dem DOM und daher wurde das Problem gelöst. Weitere Informationen finden Sie hier: angle-ui.github.com
alainvd

Antworten:

89

Angular 1.1.5 führte die ng-if- Direktive ein. Das ist die beste Lösung für dieses spezielle Problem. Wenn Sie eine ältere Version von Angular verwenden, sollten Sie die ui-if- Direktive von angle- ui verwenden .

Wenn Sie hier angekommen sind und nach Antworten auf die allgemeine Frage "Bedingte Logik in Vorlagen" suchen, sollten Sie auch Folgendes berücksichtigen:


Ursprüngliche Antwort:

Hier ist eine nicht so großartige "ng-if" -Richtlinie:

myApp.directive('ngIf', function() {
    return {
        link: function(scope, element, attrs) {
            if(scope.$eval(attrs.ngIf)) {
                // remove '<div ng-if...></div>'
                element.replaceWith(element.children())
            } else {
                element.replaceWith(' ')
            }
        }
    }
});

das erlaubt diese HTML-Syntax:

<div ng-repeat="message in data.messages" ng-class="message.type">
   <hr>
   <div ng-if="showFrom(message)">
       <div>From: {{message.from.name}}</div>
   </div>    
   <div ng-if="showCreatedBy(message)">
      <div>Created by: {{message.createdBy.name}}</div>
   </div>    
   <div ng-if="showTo(message)">
      <div>To: {{message.to.name}}</div>
   </div>    
</div>

Geige .

replaceWith () wird verwendet, um nicht benötigten Inhalt aus dem DOM zu entfernen.

Wie bereits auf Google+ erwähnt, kann ng-style wahrscheinlich zum bedingten Laden von Hintergrundbildern verwendet werden, falls Sie ng-show anstelle einer benutzerdefinierten Direktive verwenden möchten. (Zum Nutzen anderer Leser erklärte Jon auf Google+: "Beide Methoden verwenden ng-show, was ich zu vermeiden versuche, weil es display: none verwendet und zusätzliches Markup im DOM hinterlässt. Dies ist ein besonderes Problem in diesem Szenario, weil Das versteckte Element hat ein Hintergrundbild, das in den meisten Browsern noch geladen wird. ").
Siehe auch Wie wende ich CSS-Stile in AngularJS bedingt an?

Das Winkel-ui ui-wenn Direktive überwacht Änderungen an der If-Bedingung / dem If-Ausdruck. Meins nicht. Während meine einfache Implementierung die Ansicht korrekt aktualisiert, wenn sich das Modell so ändert, dass sie nur die Vorlagenausgabe beeinflusst, wird die Ansicht nicht korrekt aktualisiert, wenn sich die Bedingung / Ausdrucksantwort ändert.

Wenn sich beispielsweise der Wert eines from.name im Modell ändert, wird die Ansicht aktualisiert. Wenn Sie dies tun delete $scope.data.messages[0].from, wird der from-Name aus der Ansicht entfernt, die Vorlage jedoch nicht aus der Ansicht, da die if-Bedingung / der if-Ausdruck nicht überwacht wird.

Mark Rajcok
quelle
1
Vielen Dank, Mark, ich werde wahrscheinlich UI-IF verwenden. Aus dem Grund, den Sie angeben, ist dies wahrscheinlich die kugelsicherste Methode. Ich habe mir jedoch den Code für ui-if angesehen und er macht eine Menge Dinge, die ich nicht verstehe, und ich bin immer nervös, wenn ich Code verwende, den ich nicht verstehe.
Jonhobbs
Ich habe gerade bemerkt, dass ui-if sich nicht selbst ersetzt, aber immer noch einen Wrapper div um meine Vorlage hinterlässt. vermutlich hat es dies aus irgendeinem Grund zu tun, was damit zu tun hat, dass man den Ausdruck im Auge behalten will?
Jonhobbs
20

Sie könnten die ngSwitch- Direktive verwenden:

  <div ng-switch on="selection" >
    <div ng-switch-when="settings">Settings Div</div>
    <span ng-switch-when="home">Home Span</span>
    <span ng-switch-default>default</span>
  </div>

Wenn Sie nicht möchten, dass das DOM mit leeren Divs geladen wird, müssen Sie Ihre benutzerdefinierte Direktive mit $ http erstellen, um die (Unter-) Vorlagen zu laden, und $ compile, um sie in das DOM einzufügen, wenn eine bestimmte Bedingung erreicht ist.

Dies ist nur ein (ungetestetes) Beispiel. Es kann und sollte optimiert werden:

HTML:

<conditional-template ng-model="element" template-url1="path/to/partial1" template-url2="path/to/partial2"></div>

Richtlinie:

app.directive('conditionalTemplate', function($http, $compile) {
   return {
      restrict: 'E',
      require: '^ngModel',
      link: function(sope, element, attrs, ctrl) {
        // get template with $http
        // check model via ctrl.$viewValue
        // compile with $compile
        // replace element with element.replaceWith()
      }
   };
});
Asgoth
quelle
Ich habe eine neue Geige mit ng-switch erstellt, so gut ich kann - jsfiddle.net/jon64digital/pGwRu/5 - Ich glaube jedoch nicht, dass es aus einigen Gründen funktionieren wird. (1) -when = "" scheint nicht zu funktionieren, um zu überprüfen, ob das Attribut fehlt. (2) Selbst wenn es so wäre, würde das DOM meiner Meinung nach immer noch mit leeren Divs und zusätzlichen Wrapper-Divs übersät sein.
Jonhobbs
Ich bin noch nicht weit genug fortgeschritten, um meine eigene Direktive zu schreiben, aber instinktiv fühlt es sich falsch an, einen Ajax-Aufruf zu verwenden, um die Vorlagen abzurufen. Wäre es möglich, die Vorlage in den HTML-Code aufzunehmen (innerhalb der Direktive)? Ich vermute, der Ausdruck wäre ein Attribut auf dem Direktiven-Tag (das eine Funktion aufrufen könnte). Wenn der Ausdruck als wahr ausgewertet wird, wird die innere Vorlage gebunden und angezeigt. Wenn der Ausdruck als falsch bewertet wird, wird das Element aus dem DOM entfernt.
Jonhobbs
Sie können auch fest codierte Vorlagen (Strings) verwenden, ja. aber dann wäre es weniger wiederverwendbar.
Asgoth
Ich bin gerade in Richtung der Angular UI IF-Direktive gezeigt worden. Es sieht so aus, wie ich es brauche, aber ich verstehe nicht, wie es funktioniert, deshalb bin ich immer vorsichtig, wenn ich etwas blind in mein Projekt einbeziehe, wenn ich nicht verstehe, was es tut.
Jonhobbs
Asgoth, ich spreche nicht von einer Hardcodierung der Vorlage im JS-Code der Direktive. Ich meine, es zwischen die äußeren Tags der Direktive zu setzen, z. B. <conditional-template expression = "evaluierenThis ()"> Vorlage geht hierher </ bedingte Vorlage>.
Jonhobbs
6

Sie können ng-show für jedes div-Element in der Schleife verwenden. Wollten Sie das: http://jsfiddle.net/pGwRu/2/ ?

<div class="from" ng-show="message.from">From: {{message.from.name}}</div>
matko.kvesic
quelle
1
Ich glaube, dass ng-hide nur die Anzeige setzt: keine, es entfernt nichts aus dem DOM.
Jonhobbs
In Ihrem Beispiel wird "createdBy" weiterhin angezeigt, auch wenn "from" angezeigt wird. Es sollte immer nur "createdBy" angezeigt werden, wenn keine Absenderadresse vorhanden ist.
Jonhobbs
Ja, es werden keine Elemente aus dom entfernt. Für createdBy verstecken, wenn von ist, fügen Sie einfach Kriterien zur Bedingung hinzu: jsfiddle.net/pGwRu/3
matko.kvesic