Angular.js: الزاوي 1.2.18: ng- تكرار المشكلة مع transclude

تم إنشاؤها على ١٧ يونيو ٢٠١٤  ·  48تعليقات  ·  مصدر: angular/angular.js

عند التمرير إلى التوجيه بواسطة ng-transclude ، فإن محتوى html بالمرجع {{item}} الذي تريد تكراره (من خلال ng-تكرار = "العنصر في المجموعة" المطبق في التوجيه) لا يعمل مع الإصدار 1.2.18

http://embed.plnkr.co/EvzF25sPD3uZLQivDFqy/preview

التعليق الأكثر فائدة

ما انتهى بي الأمر هو استخدام $parent . إنها خزانة الفانيليا دون الحاجة إلى إضافة الكثير من الأشياء.

لذلك لدي شيء مثل:

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>

ال 48 كومينتر

يا لودي. petebacondarwin تريد أن تفعل واحدة أخرى من هؤلاء؟ هذا حقًا هو "عدم إنشاء نطاق شقيق باستخدام ng-transclude" الشيء مرة أخرى ، إنه فقط نجح من قبل في هذه الحالة بسبب الانكسار

للأسف ، هذه ليست الطريقة التي يعمل بها التحويل ng-transclude . ما تحاول القيام به هو إنشاء توجيه حاوي يستفيد من توابعه كـ "قالب" لما يجب إزالته داخل التوجيه الخاص بك.

واجهت هذا عدة مرات في الماضي وأجرت نقاشًا طويلًا مع ميسكو حول هذا الموضوع. المحتوى الذي يتم ترميزه مرتبط بنطاق المكان حيث يتم إنشاء مثيل للتوجيه ؛ ليس في نطاق قالب التوجيه.

في السابق ، قد يكون هذا قد نجح لأننا كنا في الواقع نربط التضمين بالنطاق الخاطئ في بعض الحالات التي تضمنت سيناريوهات التضمين المتداخلة.

لذلك في الواقع لا تحتاج حقًا إلى استخدام التضمين هنا لأن ما تحاول فعله حقًا هو ببساطة إدخال HTML الداخلي في القالب الخاص بك. يمكنك القيام بذلك في وظيفة الترجمة مثل هذا:

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

مثال آخر على هذا (على ما أعتقد):

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

عملت مع Angular 1.2.1 ، ولكن ليس مع 1.2.18 ؛

يمكن للمطور الأبرياء فقط توقع عمل الكود أعلاه. هذا المستند يقول:

... في بعض الأحيان يكون من المرغوب فيه أن تكون قادرًا على تمرير قالب كامل بدلاً من سلسلة أو كائن. لنفترض أننا نريد إنشاء مكون "مربع حوار". يجب أن يكون مربع الحوار قادرًا على التفاف أي محتوى عشوائي.

بينما تقول وثائق ngTransclude :

التوجيه الذي يحدد نقطة الإدراج لـ DOM المستعرض لأقرب توجيه أصلي يستخدم التضمين. ستتم إزالة أي محتوى موجود للعنصر الذي تم وضع هذا التوجيه عليه قبل إدراج المحتوى المتضمن.

كيف يختلف هذا عن تعريف petebacondarwin :

... قم بإنشاء توجيه حاوية يستخدم العناصر الفرعية الخاصة به كـ "نموذج" لما يجب إزالته داخل التوجيه الخاص بك

أنا حقًا لا أفهم لماذا لا يكون التحويل هو الحل الصحيح هنا. إذا كان هناك أي شيء أتوقع أن يتضمن الحل المعقول حقن نطاق القالب في وظيفة التضمين.

الفرق هو أن المحتوى المتضمن مرتبط بـ "الخارج" ، أي نطاق المكان الذي يوجد فيه العنصر <people> .
في حين أن ما تريده هو أن يرتبط القالب المضمن بـ "الداخل" ، أي نطاق التوجيه.
إذا لم ينشئ التوجيه الخاص بك نطاقه الخاص ، فهذا هو نفس الشيء تقريبًا. إذا كان التوجيه الخاص بك ينشئ نطاقًا معزولًا ، على سبيل المثال ، فمن المؤكد أنه ليس نفس الشيء. النطاق الداخلي ليس له حق الوصول إلى النطاق الخارجي.

أعتقد أنه يمكنك إنشاء التوجيه الخاص بك والذي يقوم بالفعل بحقن نطاق القالب في وظيفة التضمين ...

ما تقوله منطقي تمامًا - لكن هذا فقط إذا فهمت أعماق Angular. نقطتي هي أن المثال أعلاه يجب أن يعمل بطريقة ما دون الكثير من العمل الإضافي.

يبدو لي أنه معقول جدًا وعملي للغاية بالنسبة لوظيفة التحويل لتكون قادرة على الوصول إلى نطاق القالب (أو النطاق "الداخلي") بطريقة أو بأخرى. أستطيع أن أفكر في العديد من الحالات لماذا ستكون هناك حاجة إلى ذلك.

تم شرح نفس المشكلة في هذه المدونة . اعتقادي أن المزيد والمزيد من الناس سيشتكون من كيفية سير الأمور في الوقت الحالي (هناك بالفعل العديد من المشكلات ذات الصلة على Github نتيجة لهذا السلوك).

وشكرا جزيلا على الكود. أنا أقوم بتكرارها هنا لصالح الآخرين:

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

:-) أوافق على أن التضمين ليس موضوعًا سهل الفهم دون الكثير من حك الرأس وضرب لوحة المفاتيح.
ربما نحتاج إلى توضيح التوثيق بشكل أكبر حول حقيقة أن ng-transclude سيربط المحتوى المتضمن بالنطاق "الخارجي"؟

أنا شخصياً أشعر أن التوثيق الحالي ، على الأقل في هذه الصفحة المحورية ، واضح وواضح إلى حد ما:

ماذا يفعل خيار الشفرة هذا بالضبط؟ يجعل transclude محتويات التوجيه مع هذا الخيار لها حق الوصول إلى النطاق خارج التوجيه بدلاً من الداخل.

(وكل ما يلي يوضح ذلك أكثر).

سأفكر في إضافة التوجيه الذي قدمته إلى إطار العمل ، وربما وصفه بأنه "ng-transclude-internal"؟ أعرف شخصًا آخر على الأقل قام بعمل هذا ، باستخدام التوجيه المسمى "المنظار".

لقد جربت الحل ، لكنني واجهت مشكلة أخرى ، لماذا في نطاق ng-تكرار الأصل ليس نطاق التوجيه

http://plnkr.co/edit/7j92IC؟p=preview

luboid السبب الذي لا يعمل في plunker هو أن توجيهك له نطاق معزول ، وسيستخدم DOM المترجم المعدل (لا تفعل هذا ، بالمناسبة ، هذه طريقة سخيفة لحل هذه المشكلة) أخ من والد المنظار المعزول.

سأضيف مثالاً على الطريقة الصحيحة لجعل ذلك يعمل بالطريقة التي تتوقعها. (لكن ، لا يزال هذا تصميمًا مروعًا جدًا بشكل عام ، ولا يوجد سبب وجيه للقيام بذلك)

في الواقع ، فكر في الأمر ، مع توجيهات ng-تكرار أو توجيهات أخرى لتضمين العناصر لا يمكنك إصلاح ذلك حقًا. حسنًا ، لن يعمل هذا أبدًا منذ الإصدار 1.2.0

petebacondarwin سؤال مبتدئ حقا هنا. لماذا استخدم var innerScope بدلاً من مجرد:

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

من الأفضل عمومًا إنشاء نطاق جديد إذا كنت ستجمع بعض العناصر الجديدة ، نظرًا لأنك لست متأكدًا مما إذا كان التوجيه الأصلي مرتبطًا ببعض التوجيهات المعقدة التي تريد أيضًا إنشاء نطاق ، وما إلى ذلك ، ولكن قد تتمكن من الحصول على بعيدا عنها هنا ...

شكرا للمساعدة،
سأستخدم قوالب خارجية ($ templateCache) ، ثم تسير الأمور إلى حد ما ، ويتم تجميع القالب الأساسي بنطاق توجيهي

Izhakipetebacondarwin شكرا جزيلا لوالتي تشمل التوجيه. تم التحديث أخيرًا من 1.2.16 إلى 1.2.20 واستغرق الأمر بعض الشيء لمعرفة سبب بدء تطبيقي في التعطل بشدة.

اعتقدت أن التضمين كان مفهومًا بسيطًا جدًا قبل أن أجد هذا الموضوع. لا.

شكرًا لكل من ساعد في توضيح هذا الأمر. أدى التحديث إلى 1.2.21 إلى كسر الكثير من واجهتنا بسبب المشكلات الموضحة هنا ، ونحن الآن على المسار الصحيح.

caitppetebacondarwin: إنه سيكون من المفيد جدا لو استطعنا الحصول على اثنين من أجزاء إضافية من المعلومات:

  1. أي تغيير حدث في مكان ما "حول الإصدار 1.2.0" (كما قالت كيتلين) أدى إلى انفجار هذا النهج فجأة؟ أفهم ما الذي لم يعد يعمل بعد الآن ، لكنني لا أرى أي شيء محدد في سجل التغيير بالقرب من هذا الإصدار يبدو أنه مرتبط بهذه المشكلة بالذات.
  2. ما هو النهج البديل الذي يجب على المستخدمين النهائيين استخدامه بدلاً من ng-transclude إذا أردنا حاويات معزولة قابلة لإعادة الاستخدام يمكن أن يرث منها المحتوى الداخلي التعسفي؟ مثال: تطبيق كبير متعدد المستأجرين حيث تكون الواجهة متغيرة وقائمة على البيانات بالكامل. لذلك ، لدينا أشياء مثل عناصر التحكم في القائمة التي تحتاج إلى الذهاب لاسترداد البيانات لملء نفسها ، ثم التفاعل / السلوك المتسق. نود أن نكون قادرين على توحيد توجيه غلاف يوفر هذه الأشياء. لكن القوالب التي نستخدمها لعناصر القائمة (والتي قد تحتوي على توجيهات داخلية) تختلف باختلاف الموقف. وغالبًا ما تكون القوائم متداخلة ، بشكل متكرر ، بناءً على هيكل شجرة البيانات التي تنشئها. لذلك ، نحتاج إلى عزل تلك القوائم المتداخلة. نحن نتحكم في كل الكود ، لذلك نحن لا نستخدم التحويل من أجل عزل الداخلي عن الخارجي. بدلاً من ذلك ، نحن نتبع نهجًا إعلانيًا لطيفًا للحاويات ومحتواها التعسفي ، كما هو موصى به صراحة في دليل التوجيه. هل ng-include الخيار الأفضل لهذا الآن (تمرير المسار إلى القالب الداخلي كسمة في إعلان الحاوية) ، على الرغم من أنه يبعدنا عن القوالب المضمنة ، وبالتالي يشعرنا بقدر أقل من الاصطلاحات؟

أفهم ما الذي لم يعد يعمل بعد الآن ، لكنني لا أرى أي شيء محدد في سجل التغيير بالقرب من هذا الإصدار يبدو أنه مرتبط بهذه المشكلة بالذات.

https://github.com/angular/angular.js/blob/master/CHANGELOG.md#120 -timely-delivery-2013-11-08

كنت أشير إلى هذه على وجه الخصوص

  • قم بتمرير نطاق العزل فقط للأطفال الذين ينتمون إلى توجيه العزل (d0efd5ee)
  • اجعل نطاق العزل يعزل حقًا (909cabd3 ، # 1924 ، # 2500)

لكني لا أستطيع حقاً تذكر ما كنت أتحدث عنه بعد الآن ، من يعلم ヽ ༼ ຈ ل͜ ຈ ༽ ノ

في سجل التغيير ، هناك نوعان فقط من الرسائل: التغييرات والتغييرات الفاصلة. لم يكن هذا يعتبر تغييرًا جذريًا ، بل إصلاح خطأ. كان من الصعب التكهن بأن الكثير من الناس استخدموا هذا السلوك. لكن ربما يجب أن تذهب إلى مستندات الهجرة (التي تحتاج إلى بعض الحب)

شو 'نف. هؤلاء هم. كنت أبحث عن تغييرات تتعلق بالترجمة ، لكن من الواضح أن هذا تغيير في نطاق معزول يكون عرضيًا للترجمة. شكرا!

هذا التغيير كسر رمزنا أيضًا. سيكون من الجيد أن يكون لديك طريقة تعريفية سهلة للوصول إلى متغيرات التوجيه person واستخدامها في النطاق الأصلي. يبدو أنها حالة استخدام شائعة بعد رؤية عدد الأشخاص الذين كانوا يستخدمونها قبل أن يتم إصلاح هذا bug في 1.2.18.

هذا هو العرض التوضيحي الذي يعمل في 1.2.17 والذي تم كسره منذ 1.2.18

http://plnkr.co/edit/QswOxN؟p=preview

evgenyneu : بافتراض أنك تتحكم في كل من المحتوى الداخلي وتوجيه الحاوية ، وجدنا أن استخدام ng-include بدلاً من transclude واضح وسهل ويعطينا نمط الوراثة الذي نريده. نقوم ببساطة بتمرير اسم قالب كسمة في إعلان التوجيه الخارجي ، ووضع التوجيه ng-include في نفس المكان الذي كان لدينا فيه سابقًا التوجيه transclude. تم حل المشكلة.

إذا كنت تريد أن يكون قالب المحتوى الداخلي مضمنًا في قالب العرض ، فما عليك سوى استخدام ng-template لتغليف القالب في نفس الموضع الذي كنت تستخدمه من قبل.

xmlilley ، نصيحة ممتازة ، شكرًا جزيلاً لك. إنه أنظف نهج بالفعل.

ها هو العرض التوضيحي: http://plnkr.co/edit/4MwEL3؟p=preview

أيها الرجال الذين تعرفهم ، يمكنك القيام بذلك بشكل صحيح بدلاً من ذلك http://plnkr.co/edit/fw7thti1u4F9ArxsuYkQ؟p=preview --- إنه ليس مثاليًا ، ولكنه يأخذك إلى هناك

@ caitp ، لطيفة ، شكرا. لدينا الكثير من الحلول!

بالنسبة لأولئك الذين يشاهدون هذه المشكلة ، قمت بإنشاء توجيه "محسّن" ng-transclude ، تحدد قيمة السمة الخاصة به النطاق الداخلي المطلوب ويمكن أن يكون واحدًا من 3:

  • silbing - نطاق المحتويات
  • الأصل - نطاق المحتويات المتضمنة هو نطاق العنصر حيث يحدث التضمين.
  • تابع - نطاق المحتويات المتضمنة هو نطاق تابع لنطاق العنصر حيث يحدث التضمين.

استخدام المثال:

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

المثال الكامل

انظر هذا المربع للحصول على مثال للجميع.

يبدو الإخراج كما يلي:

image

مصدر

.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. لا يكسر الاتفاقيات الحالية ، ولكنه يضيف طريقة واضحة ومعلنة للوصول طوعيًا إلى حالة استخدام شائعة. شكرا!

: +1: لحل Izhaki . سأستخدم هذا كما هو ولكني سأشعر براحة أكبر إذا تم تضمينه في الزاوية.

+1

Izhaki +1 ، مثال رائع!

شكرا لتوجيه التضمين المخصص. انتهى بي الأمر فقط إلى إنشاء ترجمة مخصصة منفصلة بدلاً من تجاوز الافتراضي. هناك استدعاءات وظيفية في الباتش الخاص بك لست متأكدًا من مصدرها. minErr () وبدء العلامة ()

dehru - هذه الوظائف داخلية في AngularJS.

Izhaki شكرا جزيلا! لقد شورت مكبرك الذي يكرر المحتوى المتضمن ، إذا كان أي شخص مهتمًا.
من الجدير بالذكر أن ng-transclude = "parent" قد لا تعمل بالطريقة التي تتوقعها.
http://plnkr.co/edit/S6ngqz؟p=preview

plunker_ng-transclude_ng-repeat

تضمين التغريدة
يبدو أنه شيء يجب أن يكون في طلب سحب إلى زاوية ..
على الأقل في توجيه منفصل باستخدام Github repo ، هل تعتقد أنه يمكنك نشره (حتى أتمكن من تقديم بعض التغييرات ....)؟
شكرا

إزحاقي ، هذا رائع. تسببت لك بألم.

Izhaki هذا حل لطيف للغاية لمشكلة نطاقات الأطفال. شكرا.

لكني في حيرة من أمري ، وكنت أتساءل عما إذا كان بإمكانك أن تشرح ، كيف تنتقل وراثة دالة transclude $ من التوجيه الخارجي إلى التوجيه ngTransclude؟ لم يُذكر صراحةً في أي مكان رسمي يمكنني العثور عليه ، لكنني افترضت أنه يجب استخدام transclude: true في التوجيه لاستخدام وظيفة $ transclude في وظيفة الارتباط. بعد اللعب بالكود قليلاً ، اكتشفت أن استخدام transclude: true يكسر الشفرة بالفعل. ماذا يجري هنا؟

لقد ناضلت مع هذا لعدة أيام بالإضافة إلى حقيقة أن transclude يُدرج العناصر المسترجعة داخل العنصر النائب transclude بدلاً من استبداله بها.

لقد وجدت أن ng-transclude-replace يعالج كلتا القضيتين! http://gogoout.github.io/angular-directives-utils/#/api/ng -directives-utils.transcludeReplace

لست بحاجة إلى نشر التوجيه ng-تكرار في التوجيه المكرر نفسه أو أي شيء من هذا القبيل! يبدو أن كل شيء يعمل ، بما في ذلك جميع عمليات التحقق من الصحة / الارتباطات.

هذا مقتطف من الكود الخاص بي:

<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 شكرًا على ذكر ng-transclude-replace ، إصلاح رائع.

لقد واجهت هذه المشكلة مؤخرًا. بينما يعملIzhaki معي ، أشعر بالفضول لمعرفة ما إذا كانت أفضل الممارسات قد ظهرت في الوقت الذي مضى منذ هذه المناقشة. على وجه الخصوص ، هل كان هناك أي اهتمام بجعل ng-transclude="sibling | parent | child" جزءًا من اللب الزاوي؟

telekid - ليس لدينا خطط لتضمين هذه الميزة في النواة في الوقت الحالي.

أرى أن هناك حلولًا وإيجاد حلول ، ولكن سيكون من الملائم جدًا أن تكون هناك طريقة للوصول إلى النطاق الداخلي للتوجيه.

أود أن أشير إلى أنني قمت بتحديث تعديل التضمين الذي أنشأه التحويل متعدد الفتحات.
الفرع: 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

ما انتهى بي الأمر هو استخدام $parent . إنها خزانة الفانيليا دون الحاجة إلى إضافة الكثير من الأشياء.

لذلك لدي شيء مثل:

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>

مرحبًا @ moneytree-doug: أستخدم هذا الحل الذي قدمته ، لكني أجد نطاق التضمين - لا يزال هذا هو النطاق التوجيهي ، وليس التابع للنطاق الجديد الذي تم إنشاؤه بواسطة ng-تكرار. هل يمكن أن تعطيني بعض الاقتراحات؟

szetin هل يمكنك وضع jsfiddle لتظهر لي ما تتوقعه مقابل ما يحدث؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات