Angular.js: ng-transclude sollte keinen neuen Geschwisterbereich erstellen.

Erstellt am 20. Dez. 2013  ·  69Kommentare  ·  Quelle: angular/angular.js

Dies ist eher eine Änderungsanforderung und ich würde gerne sehen, was andere Leute denken.

Meiner bescheidenen Meinung nach sollte ng-transclude keinen eigenen Bereich schaffen oder zumindest eine Möglichkeit haben, dies zu verhindern. Der Grund dafür ist, dass eine Richtlinie, die eine Transklusion anfordert, bereits Mittel hat, um anzugeben, ob sie einen Geltungsbereich oder einen isolierten Geltungsbereich oder gar keinen Geltungsbereich haben möchte oder nicht. Es verwendet die Anweisung ng-transclude, um zu markieren, wo der Inhalt eingefügt werden soll. Wenn ng-transclude seinen eigenen Geschwisterbereich erstellt, werden die Erwartungen der Richtlinie gebrochen, die definiert, welche Art von Bereich gewünscht wird, und es kommt die Manifestation der populären Verwirrung zwischen "Wert" und "Objekt.Wert" zum Ausdruck.

Hier ist ein Beispiel dafür, wo ein neuer Anwendungsbereich meiner Meinung nach keinen Sinn ergibt:

ui.directive('box', function() {
    return {
        restrict: 'E',
        transclude: true,
        template: '<div ng-transclude/>',
        replace: true,
        scope: {}
    };
});

Alles, was diese Anweisung verlangt, ist, das <box>content</box> durch ein <div>content</div> und den Inhalt zu ersetzen, um den isolierten Bereich zu haben.

Das Erstellen einer verschachtelten Struktur solcher Richtlinien führt zu einer Verschmutzung des Bereichsbaums. Hier ist ein Plunker-Beispiel (http://plnkr.co/edit/DwukVGGprFFjQuVY8yTz) von drei verschachtelten Direktiven, die eine Bereichsbaumstruktur wie diese erstellen:

< Scope (002) : ng-app
    < Scope (003) : ng-controller
        < Scope (004) : box
        < Scope (005) : ng-transclude
            < Scope (006) : box
            < Scope (007) : ng-transclude
                < Scope (008) : box
                < Scope (009) : ng-transclude

Dieses Verhalten scheint seinem Zweck keinen Mehrwert zu verleihen, sorgt jedoch bei Anfängern für große Verwirrung.

Im Moment verwende ich die folgende Problemumgehung, mit der genau das erreicht wird, was das vorherige Beispiel bewirkt:

ui.directive('box', function() {
    return {
        restrict: 'E',
        transclude: true,
        template: '<div/>',
        replace: true,
        scope: {},
        link: function(scope, element, attrs, transclude) {
            transclude(scope.$parent, function(content) {
                element.append(content);
            });
        }
    };
});

Hier ist ein Plunker-Beispiel (http://plnkr.co/edit/46v6IBLkhS71L1WbUDFl), das dieses Konzept veranschaulicht. Es lässt den Scope-Baum schön und ordentlich:

< Scope (002) : ng-app
    < Scope (003) : ng-controller
        < Scope (004) : box
        < Scope (005) : box
        < Scope (006) : box

Und die 2-Wege-Bindung funktioniert so, wie viele es erwarten, wenn sie 'value' und nicht 'object.value' binden. (Ich glaube, dass die Tatsache, dass in einigen Fällen nur "Wert" übergeben wird, in anderen Fällen nicht funktioniert und die Natur der prototypischen Vererbung in Javascript verantwortlich gemacht wird, keine gute Entschuldigung ist. Die Tatsache, dass viele Menschen dieses Verhalten als unerwartet empfinden, weist darauf hin, dass es einen architektonischen Fehler gibt .)

Ich würde gerne hören, was andere Leute denken und Fälle verwenden, in denen sie denken, dass es Sinn macht, einen neuen Geschwisterbereich für ng-transclude zu schaffen.

Lots of comments $compile high won't fix bug

Hilfreichster Kommentar

Gut, dass ich vor Jahren zu Ember gewechselt bin. :) :)

Alle 69 Kommentare

Wo schafft transclude einen neuen Bereich? http://plnkr.co/edit/EuHaBR26JgAegQKvwOGH?p=preview Ich sehe es nicht

Ich spreche von der ng-transclude-Direktive. Was Sie in Ihrem Beispiel haben, ist genau das, was meine Arbeit macht.

Dies ist eine gültige Anfrage. Wir haben dies für 1.2 in Betracht gezogen, aber es näherte sich der endgültigen Veröffentlichung und wollte diese bahnbrechende Änderung nicht einführen.

wir sollten es für 1.3 betrachten

Schön! Ich bin froh, dass Sie bereits darüber nachgedacht haben.

+1 dafür. Ich denke, mein Problem hängt damit zusammen: Ich verwende ng-transclude in einer Direktive mit Formularen und muss den Gültigkeitsbereich durchlaufen. $$ childHead für den Zugriff auf das Formularüberprüfungsobjekt, aber ich habe kein Problem beim Zugriff auf meine Modelle.

Hier ist ein Beispiel: http://fiddle.jshell.net/39cgW/3/

+1 stößt heute auf dieses Problem und ich hasse es, überall $parent werfen.

Um eine Lösung dafür zu finden, scheint es zwei Möglichkeiten zu geben

1) Ändern Sie die ngTransclude-Direktive, um ihren Geltungsbereich anzugeben (sie könnte tatsächlich viel weiter verkleinert werden, glaube ich - es wird kein Controller benötigt).

oder

2) Erstellen Sie keinen neuen Bereich, wenn der Bereich nicht angegeben ist

Wir könnten also mit Option 1) ein paar Bytes sparen, und das ist schön, 2) wäre die kleinste Lösung (Löschen von 3 Zeilen oder so), und mir ist nicht klar, dass es Anwendungsfälle gibt, in denen das Erstellen eines neuen Bereichs dort implizit Sinn macht ( Vielleicht gibt es sie, aber es scheint völlig im Widerspruch zu der Art und Weise zu stehen, wie die Transklusion in den Dokumenten beschrieben wird.


Wenn Sie besonders ausgefallen sein möchten, können Sie Änderungen möglicherweise ganz vermeiden, indem Sie ngTransclude über den Attributwert angeben lassen, ob ein neuer Bereich gewünscht wird oder nicht.

Gedanken?

Ich verstehe den Unterschied zwischen deiner 1 und 2 nicht!

Nur meine Meinung, aber ich denke, Abwärtskompatibilität wird für diese Änderung wichtig sein. Ich stelle mir vor, dass es viele Apps gibt (meine eingeschlossen), die dieses Scope-Problem umgangen haben, indem sie Dinge wie $ parent und scope. $$ childHead verwendet haben. Jedes Update, das dieses Verhalten ändert, verursacht Kopfschmerzen (aber vielleicht ist es besser, früher als später Kopfschmerzen zu verursachen).

Aus theoretischer Sicht halte ich es jedoch für sinnvoller, dass ng-tranclude standardmäßig den gleichen Geltungsbereich wie die Richtlinie hat. Der Punkt beim Übertragen von Inhalten besteht darin, dass Sie möchten, dass sie nahtlos in den Inhalt einfließen. Es gibt Zeiten, in denen ich viele Anweisungen mit Transcludes verschachtelt habe, aber ich möchte immer noch, dass sie sich als eine große Komponente verhalten. Sie mit allen verschiedenen Bereichen zu haben, macht dies sehr schwierig.

Alles nur meine Gedanken. Zumindest ist es ein Schritt über der aktuellen Situation, nur die Option zu haben. :) :)

@troch Zur Verdeutlichung

        // This is the function that is injected as `$transclude`.
        function controllersBoundTransclude(scope, cloneAttachFn) {
          var transcludeControllers;

          // no scope passed
          if (arguments.length < 2) {
            cloneAttachFn = scope;
            scope = undefined;
          }

          if (hasElementTranscludeDirective) {
            transcludeControllers = elementControllers;
          }

          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers);
        }

Die Direktive ruft diese Funktion ohne Gültigkeitsbereich auf, und als solche ist der Gültigkeitsbereich undefiniert ... Dann erstellt sie in boundTranscludeFn einen neuen Gültigkeitsbereich, wenn das transcludeScope falsch ist ...

Ich sage also, für 1) können wir einfach den aktuellen Bereich für die Transclude-Funktion angeben (da diese Direktive eine benachbarte Direktive von allem ist, was einen isolierten Bereich haben könnte, sollte dies uns dennoch den ursprünglichen Bereich geben). .

Alternativ 2) erstellen Sie keinen neuen Bereich und verwenden Sie standardmäßig den aktuellen Bereich (in createBoundTranscludeFn) (möglicherweise brechen Sie Änderungen und brechen Sie möglicherweise viele Tests).

Beides ist ziemlich einfach zu machen.

+1

+1

+1

Auf jeden Fall +1

+1

+1

+1

+1 bitte

Sie wissen, was ziemlich cool wäre, obwohl es für 1.3 möglicherweise nicht vollständig machbar ist. ES6-Proxys könnten die Transklusion wirklich schön machen - mit den Eigenschaften des Transklusionsbereichs Schritt halten, aber die richtige Position in der Hierarchie haben.

Wenn die Proxy-Implementierung nicht verfügbar ist, kann sie wahrscheinlich einfach auf den Gültigkeitsbereich zurückgreifen. $ New (), sodass es möglicherweise tatsächlich möglich ist, diese Funktion relativ früh auszuführen. Das Knifflige ist, dass die Spezifikation etwas wackelig ist.

Sie würden also immer noch unerwünschte Bereiche erhalten, wenn Sie möchten, dass die Bereichshierarchie sehr sauber ist, aber zumindest würden Sie den Nebeneffekt haben, dass Datenbindungen nicht unerwartet unterbrochen werden. Ich weiß nicht.

+1

+1

+1

+1

+1

@caitp

Wenn Sie besonders ausgefallen sein möchten, können Sie Änderungen möglicherweise ganz vermeiden, indem Sie ngTransclude über den Attributwert angeben lassen, ob ein neuer Bereich gewünscht wird oder nicht.

Was keine bahnbrechenden Änderungen hat, bekommt meine Stimme.

+1

+1

+1

+1

+1

+1. Ich habe mich immer über dieses Verhalten gewundert. Ich mag diese Lösung von caitp:

@caitp

Wenn Sie besonders ausgefallen sein möchten, können Sie Änderungen möglicherweise ganz vermeiden, indem Sie> ngTransclude über den Attributwert angeben lassen, ob ein neuer Bereich gewünscht wird oder nicht.

+1

+1

+1

Für diejenigen, die dieses Problem beobachten, habe ich eine 'verbesserte' ng-transclude-Direktive erstellt, deren Attributwert den gewünschten internen Bereich definiert und einer von 3 sein kann:

  • Silbing - Der Bereich für
  • parent - Der Bereich für transkludierte Inhalte ist der des Elements, in dem die Transklusion stattfindet.
  • Kind - Der Bereich für transkludierte Inhalte ist der untergeordnete Bereich für den Bereich des Elements, in dem die Transklusion erfolgt.

Anwendungsbeispiel:

template: 
  '<div ng-transclude="parent">' +
  '</div>'

Vollständiges Beispiel

In diesem Beispiel finden Sie ein Beispiel für alles.

Die Ausgabe sieht so aus:

image

Quelle

.config(function($provide){
    $provide.decorator('ngTranscludeDirective', ['$delegate', function($delegate) {
        // Remove the original directive
        $delegate.shift();
        return $delegate;
    }]);
})

.directive( 'ngTransclude', function() {
  return {
    restrict: 'EAC',
    link: function( $scope, $element, $attrs, controller, $transclude ) {
      if (!$transclude) {
        throw minErr('ngTransclude')('orphan',
         'Illegal use of ngTransclude directive in the template! ' +
         'No parent directive that requires a transclusion found. ' +
         'Element: {0}',
         startingTag($element));
      }

      var iScopeType = $attrs['ngTransclude'] || 'sibling';

      switch ( iScopeType ) {
        case 'sibling':
          $transclude( function( clone ) {
            $element.empty();
            $element.append( clone );
          });
          break;
        case 'parent':
          $transclude( $scope, function( clone ) {
            $element.empty();
            $element.append( clone );
          });
          break;
        case 'child':
          var iChildScope = $scope.$new();
          $transclude( iChildScope, function( clone ) {
            $element.empty();
            $element.append( clone );
            $element.on( '$destroy', function() {
              iChildScope.$destroy();
            });            
          });
          break;
      }
    }
  }
})

Da das Problem, das ich zuvor in # 8609 angesprochen habe, als Duplikat dieses Threads geschlossen wurde, werde ich es hier wiederholen.

Meiner Meinung nach ist die derzeitige Art und Weise, wie ein Bereich für den transkludierten Teil des DOM erstellt wird, höchst unlogisch!
Es geht gegen den normalen Fluss im Winkel und sollte fixiert werden!

Hier ist ein Auszug aus meiner vorherigen Ausgabe:

Ich habe einen kleinen Plunk erstellt , um mein Problem hier zu veranschaulichen.

Der Haupttäter ist in dieser Richtlinie

     function pane() {
        return {
           restrict: 'E',
           transclude: true,
           scope: {
              title: '@'
           },
           template: '<div style="border: 1px solid black;">' +
              '<div style="background-color: gray">{{title}} (isolate scope id: {{$id}})</div>' +
              '<ng-transclude></ng-transclude>' +
              '</div>'
        };
     }

Wenn ein Benutzer so etwas tut:

<form>
    <pane title='enter your name'>
         <input type='text ngModel='username'>
    </pane>
    <pane title='enter your token'>
         <input type='text ngModel='token'>
   </pane>


Das Ergebnis wird für viele, insbesondere für neue Benutzer, überraschend sein. Und dies ist sogar ein (zu) vereinfachter Anwendungsfall. Versuchen Sie dort einige Validierungsmeldungen anzuzeigen;)

Dies ist ein anderer Anwendungsfall als der, mit dem dieses Problem begonnen hat, aber ich stimme zu, dass es im Grunde das gleiche Problem ist!

Es ist viel sinnvoller, den Bereich stattdessen mit dem dom-Element zu transklamieren. Wenn ein neuer Bereich wirklich benötigt wird, kann ein Benutzer trotzdem ein ngController auf das ngTransclude -Element setzen. Oder es kann ein optionales Flag auf ngTransclude , das eines auslöst. Dieses Flag kann auch verwendet werden, um den Anwendungsfall # 5489 zu lösen. Und es kann auch für die angebotene Lösung caitp verwendet werden.

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1 scheint eine ausgeschlossene Richtlinie zu schaffen, die auch dann einen Geltungsbereich schafft, wenn Sie dazu aufgefordert werden. Beispiel http://plnkr.co/edit/Wn81IBkE87vtigXvjmIa?p=preview

+1

+1

+1

+1

+1

+1

+1

+1

+ 1 - Gibt es dafür gültige Problemumgehungen?

@nikkwong , in diesem Thread wurden einige Problemumgehungen veröffentlicht. Ich weiß, dass ich einen Plunk verlinkt habe , der

OK, selbst wenn dies eintrifft, wird es nicht vor 1.5.x sein

Aber vorher habe ich Bedenken. Der Hauptgrund für das Erstellen eines neuen Bereichs (als Geschwister des isolierten Bereichs oder genauer als untergeordnetes Element des ursprünglichen Bereichs, aus dem der übertragene Inhalt stammt) besteht darin, Speicherverluste zu vermeiden.

Vergleichen Sie diesen Plunker:
http://plnkr.co/edit/3NVxdYGy1AFDvD0M2BYI?p=preview

mit einer Version, die den Umfang wiederverwendet, aus dem die ursprüngliche Transklusion stammt:
http://plnkr.co/edit/MXFz2awcqwQQ7R882Xwz?p=preview

In der zweiten Version steigt beim Ein- und Ausschalten des übertragenen Inhalts die Anzahl der Beobachter weiter an - ein Speicherverlust.

@petebacondarwin es sollte definitiv ein neues

Wir haben erneut festgestellt, dass das Problem die Art und Weise ist, wie die Winkel-2-Wege-Bindung funktioniert. Auf jeden Fall wird durch ng-transclude ein untergeordneter Bereich erstellt. Es wird jedoch zahlreiche Fälle geben, in denen Sie sich in derselben Situation befinden, z. B. durch ng-repeat wird ein untergeordneter Bereich erstellt. Nachdem wir uns den Winkelcode angesehen hatten, stellten wir fest, dass das Problem mit dem 2-Wege-Bindungsproblem nicht mit dem Objektattribut funktioniert, sondern mit Objekten selbst

Problem: http://plnkr.co/edit/Wn81IBkE87vtigXvjmIa?p=preview

Lösung: http://plnkr.co/edit/KShClgQVwIjscXPzVRwR?p=preview

Es ist nicht perfekt, aber wenn Sie wissen, dass dies viele unserer Probleme gelöst hat, binden Sie niemals ein Attribut in beide Richtungen

Beachten Sie, dass diese Lösung bereits von mbykovskyy vorgeschlagen wurde, als er das Problem ansprach. Ich gebe nur ein Beispiel, da ich eine Weile gebraucht habe, um es herauszufinden.

@petebacondarwin Es wird nur ein vorübergehendes Leck sein, bis der
Trotzdem ist es ein Punkt, der Aufmerksamkeit braucht. Vielleicht reicht eine große Warnung in den Dokumenten über dieses mögliche Leck aus, um es zu beheben?

Nun, jedes JS-Leck ist nur vorübergehend, bis Sie den Browser aktualisieren ;-)
Am 8. September 2015 um 16:41 Uhr schrieb "Sander Elias" [email protected] :

@petebacondarwin https://github.com/petebacondarwin Es wird nur eine sein
vorübergehendes Leck, bis der Haltebereich zerstört wird. Hier ist ein
plunk) [http://plnkr.co/edit/Q587WQnX0u0u7JjhtCxa?p=preview], das zeigt
dass, wenn Sie den Transclude-Holding-Bereich ausschalten, alles bekommen wird
ganz gut veröffentlicht.
Trotzdem ist es ein Punkt, der Aufmerksamkeit braucht. Vielleicht eine große Warnung in der
Dokumente über dieses mögliche Leck könnten ausreichen, um es zu beheben?

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/angular/angular.js/issues/5489#issuecomment -138603298
.

@SanderElias Benutzer unserer Angular-Anwendung sind von Anfang bis Ende des Tages beschäftigt.
Wir sehen bereits, dass die Speichernutzung im Laufe des Tages zunimmt, und wir müssen sehr vorsichtig sein, was wir auf die Seite setzen. Das Einführen weiterer möglicher Lecks ist riskant.

@troch -

Ich muss @petebacondarwin zustimmen, und selbst wenn das aktuelle Verhalten nicht 100% intuitiv ist, funktioniert das aktuelle Verhalten am besten, um ein Leck zu verhindern. Ich bin geneigt, dieses Problem als Wont Fix zu schließen

Gut, dass ich vor Jahren zu Ember gewechselt bin. :) :)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen