Bei der Übergabe an eine Direktive durch ng-transclude funktioniert HTML-Inhalt mit der Referenz {{item}}, die Sie wiederholen möchten (durch ng-repeat="item in collection" implementiert in der Direktive) nicht mit Version 1.2.18
oh gesetzgeber. @petebacondarwin möchtest du noch eine davon machen? das ist wirklich die Sache "keine Geschwister-Scopes mit ng-transclude erstellen", es ist nur so, dass es in diesem Fall aufgrund von Defekten vorher funktioniert hat
(vielleicht) verwandt: https://github.com/angular/angular.js/issues/7842
Leider funktioniert die ng-transclude
Transklusion nicht so. Was Sie versuchen, ist, eine Container-Direktive zu erstellen, die ihre untergeordneten Elemente als "Vorlage" dafür verwendet, was in Ihrer eigenen Direktive ausgestanzt werden soll.
Ich bin in der Vergangenheit ein paar Mal darauf gestoßen und hatte eine lange Debatte mit Misko darüber. Der transkludierte Inhalt ist per Definition an den Geltungsbereich des Ortes gebunden, an dem die Richtlinie instanziiert wird; nicht in den Anwendungsbereich des Musters der Richtlinie.
Zuvor hat dies möglicherweise funktioniert, da wir die Transklusion in einigen Fällen, bei denen es sich um verschachtelte Transklusionsszenarien handelte, tatsächlich an den falschen Bereich gebunden haben.
Sie müssen hier also nicht wirklich Transklusion verwenden, da Sie wirklich versuchen, einfach den inneren HTML-Code in Ihr eigenes Template einzufügen. Sie können dies in der Kompilierungsfunktion wie folgt tun:
http://plnkr.co/edit/j3NwMGxkVRM6QMhmydQC?p=preview
app.directive('test', function(){
return {
restrict: 'E',
compile: function compile(tElement, tAttrs, tTransclude) {
// Extract the children from this instance of the directive
var children = tElement.children();
// Wrap the chidren in our template
var template = angular.element('<div ng-repeat="item in collection"></div>');
template.append(children);
// Append this new template to our compile element
tElement.html('');
tElement.append(template);
return {
pre: function preLink(scope, iElement, iAttrs, crtl, transclude) {
scope.collection = [1, 2, 3, 4, 5];
},
post: function postLink(scope, iElement, iAttrs, controller) {
console.log(iElement[0]);
}
};
}
};
});
Ein weiteres Beispiel dafür (glaube ich):
Index.html:
<html ng-app='myApp'>
<head>
<title>AngularJS Scopes</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<script src='index.js'></script>
</head>
<body ng-controller='myController'>
<people>Hello {{person.name}}</people>
</body>
</html>
index.js:
var myApp = angular.module( 'myApp', [] );
myApp.controller( 'myController', function( $scope ) {
$scope.people = [
{ name: 'Rob' },
{ name: 'Alex' },
{ name: 'John' }
];
});
myApp.directive( 'people', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-repeat="person in people" ng-transclude></div>',
}
});
Hat mit Angular 1.2.1 gearbeitet, aber nicht mit 1.2.18;
Der unschuldige Entwickler konnte nur erwarten, dass der obige Code funktioniert. Dieses Dokument sagt:
...manchmal ist es wünschenswert, ein ganzes Template anstelle eines Strings oder eines Objekts übergeben zu können. Nehmen wir an, wir möchten eine Komponente "Dialogfeld" erstellen. Das Dialogfeld sollte jeden beliebigen Inhalt umschließen können.
Während die ngTransclude-Dokumentation sagt:
Direktive, die den Einfügepunkt für das transcludierte DOM der nächsten übergeordneten Direktive markiert, die Transclusion verwendet. Jeglicher vorhandener Inhalt des Elements, auf dem diese Anweisung platziert wird, wird entfernt, bevor der transkludierte Inhalt eingefügt wird.
Wie unterscheidet sich dies von der @petebacondarwin- Definition:
...eine Container-Direktive erstellen, die ihre untergeordneten Elemente als "Vorlage" dafür verwendet, was in Ihrer eigenen Direktive ausgestanzt werden soll
Ich verstehe wirklich nicht, warum Transklusion hier nicht die richtige Lösung ist. Wenn überhaupt, würde ich erwarten, dass die vernünftige Lösung darin besteht, den Vorlagenbereich in die Transklusionsfunktion einzufügen.
Der Unterschied besteht darin, dass transkludierter Inhalt an die "Außenseite" gebunden ist, dh an den Bereich des Ortes, an dem das <people>
Element gefunden wird.
Was Sie dagegen wünschen, ist, dass die Inline-Vorlage an das "Innere", dh den Geltungsbereich der Richtlinie, gebunden ist.
Wenn Ihre Direktive keinen eigenen Geltungsbereich schafft, ist dies ungefähr dasselbe. Wenn Ihre Richtlinie beispielsweise einen isolierten Geltungsbereich schafft, ist dies definitiv nicht dasselbe. Der innere Bereich hat keinen Zugriff auf den äußeren Bereich.
Ich denke, Sie könnten Ihre eigene Anweisung erstellen, die tatsächlich den Vorlagenbereich in die Transklusionsfunktion einfügt ...
Was Sie sagen, macht absolut Sinn - aber nur, wenn Sie die Tiefen von Angular verstehen. Mein Punkt ist, dass das obige Beispiel irgendwie ohne zu viel zusätzliche Arbeit funktionieren sollte.
Scheint mir sehr vernünftig und sehr praktisch, dass die Transklusionsfunktion irgendwie auf den Bereich der Vorlage (oder des 'inneren') zugreifen kann. Ich kann mir viele Fälle vorstellen, warum dies erforderlich sein wird.
Das gleiche Problem wird in diesem Blog erklärt . Ich gehe davon aus, dass sich immer mehr Leute über den aktuellen Stand der Dinge beschweren werden (es gibt bereits eine Vielzahl verwandter Probleme auf Github aufgrund dieses Verhaltens).
Und vielen Dank für den Code. Ich reproduziere es hier zum Nutzen anderer:
var myApp = angular.module( 'myApp', [] );
myApp.controller( 'myController', function( $scope ) {
$scope.people = [
{ name: 'Rob' },
{ name: 'Alex' },
{ name: 'John' }
];
});
myApp.directive( 'people', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-repeat="person in people" inject></div>'
}
});
myApp.directive('inject', function(){
return {
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 innerScope = $scope.$new();
$transclude(innerScope, function(clone) {
$element.empty();
$element.append(clone);
$element.on('$destroy', function() {
innerScope.$destroy();
});
});
}
};
});
:-) Ich stimme zu, dass Transklusion kein einfaches Thema ist, das ohne viel Kopfkratzen und Tastaturhämmern zu verstehen ist.
Vielleicht müssen wir die Dokumentation noch weiter klären, dass ng-transclude den transcludierten Inhalt an den "äußeren" Bereich bindet?
Persönlich finde ich die aktuelle Dokumentation, zumindest auf dieser zentralen Seite , ziemlich eindeutig und klar:
Was genau bewirkt diese Transklusionsoption? transclude macht den Inhalt einer Richtlinie mit dieser Option Zugriff auf den Geltungsbereich außerhalb der Richtlinie anstatt innerhalb der Richtlinie.
(und alles, was folgt, veranschaulicht dies weiter).
Ich würde in Erwägung ziehen, vielleicht die von Ihnen bereitgestellte Richtlinie in den Rahmen aufzunehmen und sie möglicherweise als „ng-transclude-intern“ zu bezeichnen? Ich kenne mindestens eine weitere Person, die sich mit der Direktive namens "transscope" daran versucht hat.
Ich habe eine Lösung versucht, aber ich stand vor einem anderen Problem, warum der Geltungsbereich des ng-repeat-Elternteils nicht der Geltungsbereich der Richtlinie ist
@luboid Der Grund, der in Ihrem Plunker nicht funktioniert, ist, dass Ihre Direktive einen isolierten Geltungsbereich hat und das modifizierte kompilierte DOM (tun Sie dies übrigens nicht, dies ist eine dumme Möglichkeit, dieses Problem zu lösen) verwendet a gleichgeordnetes Element des übergeordneten Elements des Isolate-Bereichs.
Ich werde ein Beispiel für einen geeigneten Weg hinzufügen, um dies so zu gestalten, wie Sie es erwarten. (Aber das ist im Allgemeinen immer noch ein ziemlich schreckliches Design, es gibt keinen guten Grund, dies zu tun)
Tatsächlich können Sie dies mit ng-repeat oder anderen Element-Transclusion-Anweisungen nicht wirklich beheben. Also ja, das wird seit Version 1.2.0 nicht mehr funktionieren
@petebacondarwin Eine wirklich var innerScope
anstatt nur:
myApp.directive( 'inject', function() {
return {
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 ));
}
$transclude( $scope, function( clone ) {
$element.empty();
$element.append( clone );
});
}
};
});
Es ist im Allgemeinen am besten, einen neuen Geltungsbereich zu erstellen, wenn Sie einige neue Elemente kompilieren möchten, da Sie nicht sicher sind, ob die ursprüngliche Direktive mit einer komplexen Direktive verbunden ist, die auch einen Geltungsbereich erstellen möchte usw. Aber Sie können möglicherweise Folgendes erhalten weg damit hier...
Danke für die Hilfe,
Ich werde externe Vorlagen ($templateCache) verwenden, dann geht die Sache etwas einfach, die Basisvorlage wird mit Direktivenbereich kompiliert
@Izhaki @petebacondarwin Vielen Dank für diese Include-Direktive. Gerade endlich von 1.2.16 auf 1.2.20 aktualisiert und es hat ein bisschen gedauert, bis ich sah, warum meine App so stark kaputt ging.
Ich dachte, Transklusion sei ein ziemlich einfaches Konzept, bevor ich diesen Thread gefunden habe. Nö.
Danke an alle, die mitgeholfen haben, dies aufzuklären. Das Update auf 1.2.21 hat aufgrund der hier beschriebenen Probleme einen Großteil unserer Benutzeroberfläche beschädigt, und jetzt sind wir auf dem richtigen Weg.
@caitp @petebacondarwin : Es wäre sehr hilfreich, wenn wir zwei zusätzliche Informationen erhalten könnten:
Ich verstehe, was nicht mehr funktioniert, aber ich sehe im Changelog in der Nähe dieser Version keine spezifischen Dinge, die mit diesem speziellen Problem zu korrelieren scheinen.
https://github.com/angular/angular.js/blob/master/CHANGELOG.md#120 -timely-delivery-2013-11-08
Ich habe mich speziell auf diese bezogen
- Isolate nur an Kinder weitergeben, die zur Isolate-Direktive gehören (d0efd5ee)
- Isolate-Scope wirklich isolieren (909cabd3, #1924, #2500)
Aber ich kann mich nicht mehr wirklich erinnern, wovon ich überhaupt gesprochen habe, wer weiß
Im Changelog gibt es nur zwei Arten von Meldungen: Änderungen und Breaking Changes. Dies wurde nicht als Breaking Change angesehen, sondern eher als Bugfix. Es war schwer vorherzusagen, dass viele Menschen dieses Verhalten nutzten. Aber es sollte wahrscheinlich in die Migrationsdokumentation gehen (die etwas Liebe brauchen)
Sho' 'nuff. Das sind die. Ich habe nach Änderungen im Zusammenhang mit der Transklusion gesucht, aber dies ist eindeutig eine Änderung des Isolat-Bereichs, die mit der Transklusion verbunden ist. Vielen Dank!
Diese Änderung hat auch unseren Code gebrochen. Es wäre schön, eine einfache deklarative Möglichkeit zu haben, auf die person
Variablen dieser Direktive zuzugreifen und sie im übergeordneten Geltungsbereich zu verwenden. Scheint ein häufiger Anwendungsfall zu sein, nachdem man gesehen hat, wie viele Leute es benutzt haben, bevor dieses bug
in 1.2.18 behoben wurde.
Hier ist die Demo, die in 1.2.17 funktioniert und seit 1.2.18 kaputt ist
@evgenyneu : Angenommen, Sie kontrollieren sowohl den inneren Inhalt als auch die Container-Direktive. Wir haben festgestellt, dass die Verwendung von ng-include anstelle von transclude klar und einfach ist und uns das gewünschte Vererbungsmuster liefert. Wir übergeben einfach einen Vorlagennamen als Attribut an die Deklaration der äußeren Direktive und platzieren eine ng-include-Direktive an der gleichen Stelle wie früher die Transclude-Direktive. Problem gelöst.
Wenn Sie möchten, dass die innere Inhaltsvorlage in die Ansichtsvorlage integriert wird, verwenden Sie einfach ng-template, um die Vorlage an derselben Position zu umschließen, die Sie zuvor verwendet haben.
@xmlilley ,
Hier ist die Demo: http://plnkr.co/edit/4MwEL3?p=preview
Leute, ihr wisst, ihr könnt das stattdessen einfach richtig machen http://plnkr.co/edit/fw7thti1u4F9ArxsuYkQ?p=preview --- es ist nicht perfekt, aber es bringt euch dorthin
@caitp , nett, danke. Wir haben viele Lösungen!
Für diejenigen, die sich dieses Problem ansehen, habe ich eine 'verbesserte' ng-transclude-Direktive erstellt, deren Attributwert den gesuchten internen Bereich definiert und einer der folgenden sein kann:
Anwendungsbeispiel:
template:
'<div ng-transclude="parent">' +
'</div>'
Sehen Sie sich diesen Plunk an, um ein Beispiel für alle zu sehen.
Die Ausgabe sieht so aus:
.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;
}
}
}
})
@Izhaki - FWIW, +10. Verletzt nicht die aktuellen Konventionen, bietet aber eine saubere, deklarative Möglichkeit, freiwillig auf einen gemeinsamen Anwendungsfall zuzugreifen. Vielen Dank!
:+1: für @Izhakis Lösung. Ich werde dies so verwenden, wie es ist, aber ich würde mich viel wohler fühlen, wenn es in Angular enthalten wäre.
+1
@Izhaki +1, tolles Beispiel!
Vielen Dank für die benutzerdefinierte Transclude-Anweisung. Am Ende habe ich nur einen separaten benutzerdefinierten Transclude erstellt, anstatt die Standardeinstellung zu überschreiben. Es gibt ein paar Funktionsaufrufe in Ihrem Patch, von denen ich nicht sicher bin, woher sie kommen. minErr() und StartingTag()
@dehru - diese Funktionen sind intern in AngularJS.
@Izhaki vielen Dank! Ich habe Ihr Plunk gegabelt, das den transcludierten Inhalt wiederholt, falls es jemand interessiert.
Es ist erwähnenswert, dass ng-transclude="parent" möglicherweise nicht so funktioniert, wie Sie es erwarten.
http://plnkr.co/edit/S6ngqz?p=preview
@Izhaki Sehr schön.
Sieht aus wie etwas, das in einem Pull-Request zu eckig sein sollte.
Denkst du zumindest in einer separaten Direktive mit einem Github-Repo, dass du es veröffentlichen kannst (damit ich einige Änderungen anbieten kann....)?
Vielen Dank
Izhaki, das ist großartig. Ich _herz_ dich.
@Izhaki Dies ist eine sehr schöne Lösung für das Problem der
Ich bin jedoch verwirrt und habe mich gefragt, ob Sie erklären könnten, wie sich die Vererbung der $transclude-Funktion von der äußeren Direktive auf die ngTransclude-Direktive überträgt? Es ist nirgendwo ausdrücklich angegeben, das ich finden konnte, aber ich nahm an, dass transclude: true
in der Direktive verwendet werden musste, um die $transclude-Funktion in der Linkfunktion zu verwenden. Nachdem ich ein wenig mit dem Code gespielt hatte, stellte ich fest, dass die Verwendung von transclude: true
den Code tatsächlich bricht. Was ist hier los?
Ich habe tagelang damit gekämpft und auch mit der Tatsache, dass transclude die transcludierten Elemente in den transclude-Platzhalter einfügt, anstatt sie durch sie zu ersetzen.
Ich habe festgestellt, dass ng-transclude-replace beide Probleme löst! http://gogoout.github.io/angular-directives-utils/#/api/ng -directives-utils.transcludeReplace
Ich muss die ng-repeat-Direktive nicht in die Repeat-Direktive selbst oder ähnliches überführen! Alles scheint zu funktionieren, einschließlich aller Validierungen/Bindungen.
Hier ein Ausschnitt meines Codes:
<form-field label="Roles" required>
<checkbox-group>
<checkbox ng-repeat="role in roles" label="{{role.description}}">
<input type="checkbox" name="selectedRoles" ng-model="role.selected" value="{{role.description}}" ng-required="!hasRole" />
</checkbox>
</checkbox-group>
</form-field>
@abobwhite Danke für die Erwähnung von ng-transclude-replace , tolle Lösung.
Ich bin vor kurzem auf dieses Problem gestoßen. Während @Izhaki für mich funktioniert hat, bin ich gespannt, ob sich in der Zeit seit dieser Diskussion eine Best Practice herausgebildet hat. Gab es insbesondere ein Interesse daran, ng-transclude="sibling | parent | child"
Teil des eckigen Kerns zu machen?
@telekid - Wir haben derzeit nicht vor, diese Funktion in den Kern aufzunehmen.
Ich sehe, dass es Lösungen und Workarounds gibt, aber es wäre sehr praktisch, wenn es eine Möglichkeit gäbe, auf den internen Anwendungsbereich der Richtlinie zuzugreifen.
Ich möchte darauf hinweisen, dass ich den Transclude- Mod, den Angular 1.5 aktualisiert habe, damit er mit Multi-Slot-
Zweig: https://github.com/NickBolles/ngTranscludeMod/tree/Angular1.5-multi-slot
PR: https://github.com/Izhaki/ngTranscludeMod/pull/2
Plunker: http://plnkr.co/edit/5XGBEX0muH9CSijMfWsH?p=preview
Am Ende habe ich nur $parent
. Es ist der Schrank für Vanille, ohne zu viele Dinge hinzufügen zu müssen.
Also ich habe sowas wie:
angular.module('test').directive('myDirectiveWithTransclusion', function() {
return {
restrict: 'E'
transclude: {
transcludeThis: 'transcludeThis'
}
template: "<div ng-repeat='item in array'><div ng-transclude='transcludeThis'></div></div>"
}
})
<my-directive-with-transclusion>
<transclude-this>
{{$parent.item}}
</transclude-this>
</my-directive-with-transclusion>
Hallo @moneytree-doug: Ich verwende diese Lösung, die Sie bereitgestellt haben, aber ich finde den Umfang von transclude - dies ist immer noch der Richtlinienbereich, nicht das Kind des neuen Bereichs, der von ng-repeat generiert wird. Können Sie mir einige Vorschläge machen?
@szetin Könnten Sie eine Jsfiddle aufstellen, die mir zeigt, was Sie erwarten und was passiert?
Hilfreichster Kommentar
Am Ende habe ich nur
$parent
. Es ist der Schrank für Vanille, ohne zu viele Dinge hinzufügen zu müssen.Also ich habe sowas wie: