Angular.js: angular 1.2.18: masalah ng-repeat dengan transclude

Dibuat pada 17 Jun 2014  ·  48Komentar  ·  Sumber: angular/angular.js

Saat diteruskan ke arahan oleh ng-transclude, konten html dengan referensi {{item}} yang ingin Anda ulangi (melalui ng-repeat="item in collection" yang diterapkan dalam arahan) tidak berfungsi dengan versi 1.2.18

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

Komentar yang paling membantu

Apa yang akhirnya saya lakukan hanyalah menggunakan $parent . Ini lemari vanila tanpa harus menambahkan terlalu banyak hal.

Jadi saya punya sesuatu seperti:

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>

Semua 48 komentar

oh hukum. @petebacondarwin ingin melakukan salah satu dari ini? ini benar-benar "jangan buat ruang lingkup saudara dengan ng-transclude" lagi, hanya saja itu berhasil sebelumnya untuk kasus ini karena kerusakan

Sayangnya, ini bukan cara kerja transklusi ng-transclude . Apa yang Anda coba lakukan adalah membuat arahan wadah yang menggunakan anak-anaknya sebagai "templat" dari apa yang harus dihilangkan di dalam arahan Anda sendiri.

Saya mengalami ini beberapa kali di masa lalu dan berdebat panjang dengan Misko tentang hal itu. Konten yang ditransklusikan menurut definisi terikat pada ruang lingkup tempat direktif itu dipakai; tidak ke ruang lingkup template direktif.

Sebelumnya ini mungkin berhasil karena kami sebenarnya mengikat transklusi ke cakupan yang salah dalam beberapa kasus yang melibatkan skenario transklusi bersarang.

Jadi sebenarnya Anda tidak benar-benar perlu menggunakan transklusi di sini karena apa yang sebenarnya Anda coba lakukan hanyalah menyuntikkan HTML bagian dalam ke dalam template Anda sendiri. Anda dapat melakukan ini dalam fungsi kompilasi seperti ini:

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

Contoh lain dari ini (saya percaya):

indeks.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>

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

Bekerja dengan Angular 1.2.1, tetapi tidak dengan 1.2.18;

Pengembang yang tidak bersalah hanya bisa mengharapkan kode di atas berfungsi. Dok ini mengatakan:

...terkadang diinginkan untuk dapat melewatkan seluruh template daripada string atau objek. Katakanlah kita ingin membuat komponen "kotak dialog". Kotak dialog harus dapat membungkus konten sembarang.

Sementara dokumentasi ngTransclude mengatakan:

Arahan yang menandai titik penyisipan untuk DOM yang ditransklusikan dari direktif induk terdekat yang menggunakan transklusi. Konten apa pun yang ada dari elemen tempat arahan ini ditempatkan akan dihapus sebelum konten yang ditransklusikan dimasukkan.

Bagaimana ini berbeda dari definisi @petebacondarwin :

...buat direktif kontainer yang menggunakan turunannya sebagai "templat" dari apa yang harus dihilangkan di dalam direktif Anda sendiri

Saya benar-benar tidak mengerti mengapa transklusi bukanlah solusi yang tepat di sini. Jika ada yang saya harapkan solusi yang masuk akal untuk melibatkan menyuntikkan ruang lingkup templat ke fungsi transklusi.

Perbedaannya adalah bahwa konten yang ditransklusi terikat ke "luar", yaitu ruang lingkup tempat elemen <people> ditemukan.
Sedangkan yang Anda inginkan adalah agar templat sebaris diikat ke "di dalam", yaitu ruang lingkup direktif.
Jika arahan Anda tidak membuat ruang lingkupnya sendiri, maka ini adalah hal yang kurang lebih sama. Jika arahan Anda membuat ruang lingkup yang terisolasi, katakanlah, maka itu jelas bukan hal yang sama. Lingkup dalam tidak memiliki akses ke lingkup luar.

Saya kira Anda dapat membuat arahan Anda sendiri yang memang menyuntikkan ruang lingkup templat ke dalam fungsi transklusi ...

Apa yang Anda katakan masuk akal - tetapi itu hanya jika Anda memahami kedalaman Angular. Maksud saya adalah bahwa contoh di atas entah bagaimana harus bekerja tanpa terlalu banyak kerja ekstra.

Menurut saya sangat masuk akal, dan sangat praktis untuk fungsi transklusi untuk dapat mengakses ruang lingkup templat (atau 'dalam'). Saya dapat memikirkan banyak kasus mengapa ini diperlukan.

Masalah yang sama dijelaskan di blog ini . Menurut saya, semakin banyak orang akan mengeluh tentang keadaan saat ini (sudah ada banyak masalah terkait di Github sebagai akibat dari perilaku ini).

Dan terima kasih banyak untuk kodenya. Saya mereplikasi di sini untuk kepentingan orang lain:

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

:-) Saya setuju bahwa transklusi bukanlah topik yang mudah dipahami tanpa banyak menggaruk kepala dan membenturkan keyboard.
Mungkin kita perlu mengklarifikasi dokumentasi lebih jauh seputar fakta bahwa ng-transclude akan mengikat konten yang ditransklusikan ke ruang lingkup "luar"?

Secara pribadi, saya merasa dokumentasi saat ini, setidaknya di halaman penting ini , cukup eksplisit dan jelas:

Apa yang dilakukan opsi transklusi ini, tepatnya? transclude membuat isi arahan dengan opsi ini memiliki akses ke ruang lingkup di luar arahan daripada di dalam.

(dan segala sesuatu yang mengikuti mengilustrasikan ini lebih lanjut).

Saya akan mempertimbangkan untuk menambahkan mungkin arahan yang Anda berikan ke kerangka kerja, mungkin mencapnya 'ng-transclude-internal'? Saya tahu setidaknya satu orang lagi yang mencoba ini, dengan arahan yang disebut 'transscope'.

Saya telah mencoba solusi, tetapi saya menghadapi masalah lain, mengapa ke dalam lingkup induk ng-repeat bukan lingkup arahan

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

@luboid alasan yang tidak berfungsi di plunker Anda adalah karena arahan Anda memiliki ruang lingkup yang terisolasi, dan DOM terkompilasi yang dimodifikasi (omong-omong, jangan lakukan ini, ini cara konyol untuk menyelesaikan masalah ini) akan menggunakan a saudara dari induk ruang lingkup isolat.

Saya akan menambahkan contoh cara yang tepat untuk membuatnya bekerja seperti yang Anda harapkan. (Tapi, ini masih merupakan desain yang cukup buruk secara umum, tidak ada alasan bagus untuk melakukan ini)

Sebenarnya, kalau dipikir-pikir, dengan ng-repeat atau arahan transklusi elemen lainnya Anda tidak dapat benar-benar memperbaikinya. Jadi ya, itu tidak akan berfungsi sejak tentang versi 1.2.0

@petebacondarwin Pertanyaan yang sangat pemula di sini. Mengapa menggunakan var innerScope alih-alih hanya:

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

Biasanya yang terbaik adalah membuat lingkup baru jika Anda akan mengkompilasi beberapa elemen baru, karena Anda tidak yakin apakah arahan asli digabungkan dengan beberapa arahan kompleks yang juga ingin membuat lingkup, dll. Tetapi Anda mungkin bisa mendapatkannya jauhi itu disini...

Terimakasih atas bantuannya,
Saya akan menggunakan template eksternal ($templateCache), kemudian semuanya menjadi sedikit sederhana, template dasar dikompilasi dengan lingkup direktif

@Izhaki @petebacondarwin Terima kasih banyak untuk itu termasuk arahan. Akhirnya diperbarui dari 1.2.16 ke 1.2.20 dan butuh sedikit waktu untuk melihat mengapa aplikasi saya mulai rusak begitu keras.

Saya pikir transklusi adalah konsep yang cukup sederhana sebelum saya menemukan utas ini. Tidak.

Terima kasih kepada semua orang yang telah membantu untuk menjernihkan ini. Pembaruan ke 1.2.21 merusak banyak antarmuka kami karena masalah yang dijelaskan di sini, dan sekarang kami berada di jalur yang benar.

@caitp @petebacondarwin : Akan sangat membantu jika kita bisa mendapatkan dua informasi tambahan:

  1. Perubahan putus mana yang terjadi di suatu tempat "sekitar versi 1.2.0" (seperti yang dikatakan Caitlin) yang mengakibatkan pendekatan ini tiba-tiba meledak? Saya mengerti apa yang tidak berfungsi lagi, tetapi saya tidak melihat hal spesifik apa pun di changelog di dekat rilis itu yang tampaknya berkorelasi dengan masalah khusus ini.
  2. Apa pendekatan alternatif yang harus digunakan pengguna akhir sebagai pengganti ng-transclude jika kita ingin wadah yang terisolasi dan dapat digunakan kembali dari mana konten dalam sewenang-wenang dapat mewarisi? Contoh: aplikasi multi-penyewa yang besar di mana bahkan antarmukanya bervariasi dan sepenuhnya digerakkan oleh data. Jadi, kami memiliki hal-hal seperti kontrol daftar yang perlu mengambil data untuk mengisi sendiri, kemudian memiliki interaksi/perilaku yang konsisten. Kami ingin dapat membakukan direktif pembungkus yang menyediakan hal-hal itu. Tetapi templat yang kami gunakan untuk item daftar (yang mungkin berisi arahan dalam) bervariasi tergantung pada situasinya. Dan daftar sering bersarang, secara rekursif, berdasarkan struktur pohon dari data yang menghasilkannya. Jadi, kita perlu isolasi untuk daftar bersarang tersebut. Kami mengontrol semua kode, jadi kami tidak menggunakan transklusi untuk mengisolasi bagian dalam dari luar. Sebaliknya, kami hanya mengejar pendekatan deklaratif yang bagus untuk wadah dan konten arbitrernya, seperti yang direkomendasikan secara eksplisit dalam Panduan Petunjuk. Apakah ng-include opsi yang lebih baik untuk ini sekarang (melewati jalur ke templat bagian dalam sebagai atribut pada deklarasi wadah), meskipun itu menjauhkan kita dari templat sebaris, dan dengan demikian terasa sedikit kurang idiomatis?

Saya mengerti apa yang tidak berfungsi lagi, tetapi saya tidak melihat hal spesifik apa pun di changelog di dekat rilis itu yang tampaknya berkorelasi dengan masalah khusus ini.

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

Saya mengacu pada ini secara khusus

  • hanya berikan ruang lingkup isolat ke anak-anak yang termasuk dalam arahan isolat (d0efd5ee)
  • buat ruang lingkup isolat benar-benar mengisolasi (909cabd3, #1924, #2500)

Tapi, saya tidak ingat lagi apa yang saya bicarakan, siapa tahu ل͜ຈ༽ノ

Di changelog, hanya ada dua jenis pesan: perubahan dan pemutusan perubahan. Ini tidak dianggap sebagai perubahan yang melanggar, melainkan perbaikan bug. Sulit untuk memprediksi bahwa banyak orang menggunakan perilaku ini. Tapi itu mungkin harus masuk ke dokumen migrasi (yang membutuhkan cinta)

Sho' 'nuff. Mereka adalah orang-orang. Saya mencari perubahan yang terkait dengan transklusi, tetapi ini jelas merupakan perubahan dalam lingkup isolasi yang terkait dengan transklusi. Terima kasih!

Perubahan ini juga merusak kode kami. Akan menyenangkan untuk memiliki beberapa cara deklaratif yang mudah untuk mengakses variabel person direktif tersebut dan menggunakannya dalam lingkup induk. Sepertinya ini adalah kasus penggunaan umum setelah melihat berapa banyak orang yang menggunakannya sebelum bug ini diperbaiki di 1.2.18.

Ini adalah demo yang berfungsi di 1.2.17 dan rusak sejak 1.2.18

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

@evgenyneu : Dengan asumsi Anda mengontrol konten dalam dan direktif wadah, kami telah menemukan bahwa menggunakan ng-include alih-alih transclude jelas, mudah, dan memberi kami pola pewarisan yang kami inginkan. Kami hanya meneruskan nama template sebagai atribut pada deklarasi direktif luar, dan menempatkan direktif ng-include di tempat yang sama dengan direktif transclude sebelumnya. Masalah terpecahkan.

Jika Anda ingin templat konten dalam menjadi sebaris pada templat tampilan, gunakan saja ng-template untuk membungkus templat di posisi yang sama dengan yang Anda gunakan sebelumnya.

@xmlilley , tip yang luar biasa, terima kasih banyak. Ini memang pendekatan yang paling bersih.

Ini demonya: http://plnkr.co/edit/4MwEL3?p=preview

Guys, Anda tahu Anda bisa melakukan ini dengan benar http://plnkr.co/edit/fw7thti1u4F9ArxsuYkQ?p=preview --- itu tidak sempurna, tetapi itu membawa Anda ke sana

@caitp , bagus, terima kasih. Kami punya banyak solusi!

Bagi mereka yang melihat masalah ini, saya telah membuat direktif ng-transclude yang 'ditingkatkan', nilai atribut yang menentukan lingkup internal yang dicari dan dapat berupa salah satu dari 3:

  • silbing - Lingkup konten yang ditransklusikan adalah saudara kandung dari elemen di mana transklusi terjadi. Itulah perilaku ng-transclude saat ini.
  • induk - Lingkup konten yang ditransklusikan adalah elemen tempat transklusi terjadi.
  • anak - Lingkup konten yang ditransklusikan adalah lingkup anak ke lingkup elemen tempat transklusi terjadi.

Contoh penggunaan:

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

Contoh lengkap

Lihat plunk ini untuk contoh semuanya.

Outputnya terlihat seperti ini:

image

Sumber

.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. Tidak melanggar konvensi saat ini, tetapi menambahkan cara yang bersih dan deklaratif untuk secara sukarela mengakses kasus penggunaan umum. Terima kasih!

:+1: untuk solusi @Izhaki . Saya akan menggunakan ini apa adanya tetapi akan merasa jauh lebih nyaman jika disertakan dalam angular.

+1

@Izhaki +1, contoh yang bagus!

Terima kasih atas arahan transklusi khusus. Saya akhirnya hanya membuat transklusi khusus yang terpisah alih-alih mengesampingkan default. Ada beberapa panggilan fungsi di tambalan Anda yang saya tidak yakin dari mana asalnya. minErr() dan startingTag()

@dehru - fungsi ini internal untuk AngularJS.

@Izhaki terima kasih banyak! Saya telah memotong plunk Anda yang mengulangi konten yang ditransklusikan, jika ada yang tertarik.
Perlu disebutkan bahwa ng-transclude="parent" mungkin tidak berfungsi seperti yang Anda harapkan.
http://plnkr.co/edit/S6ngqz?p=preview

plunker_ng-transclude_ng-repeat

@Izhaki Sangat bagus.
Sepertinya sesuatu yang harus dalam permintaan tarik ke sudut..
Setidaknya dalam arahan terpisah dengan repo Github, apakah menurut Anda Anda dapat menerbitkannya (sehingga saya dapat menawarkan beberapa perubahan ....)?
Terima kasih

Izhaki, ini luar biasa. aku _hati_ kamu.

@Izhaki Ini adalah solusi yang sangat bagus untuk masalah cakupan anak. Terima kasih.

Saya bingung, dan bertanya-tanya apakah Anda bisa menjelaskan, bagaimana pewarisan fungsi $transclude dibawa dari direktif luar ke direktif ngTransclude? Itu tidak secara eksplisit dinyatakan di mana pun resmi yang dapat saya temukan, tetapi saya berasumsi bahwa transclude: true harus digunakan pada arahan untuk menggunakan fungsi $transclude dalam fungsi tautan. Setelah bermain dengan kode sedikit, saya menemukan menggunakan transclude: true benar-benar merusak kode. Apa yang terjadi di sini?

Saya berjuang dengan ini selama berhari-hari serta fakta bahwa transclude menyisipkan elemen yang ditransklusikan dalam placeholder transclude alih-alih menggantinya dengan mereka.

Saya telah menemukan bahwa ng-transclude-replace menangani kedua masalah! http://gogoout.github.io/angular-directives-utils/#/api/ng -directives-utils.transcludeReplace

Saya tidak perlu menyebarkan arahan ng-repeat ke dalam arahan berulang itu sendiri atau semacamnya! Semua tampaknya berfungsi, termasuk semua validasi/pengikatan.

Berikut ini cuplikan kode saya:

<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 Terima kasih telah menyebutkan ng-transclude-replace , perbaikan hebat.

Saya baru-baru ini mengalami masalah ini. Sementara @Izhaki 's bekerja untuk saya, saya ingin tahu apakah praktik terbaik telah muncul sejak diskusi ini. Secara khusus, apakah ada minat untuk menjadikan ng-transclude="sibling | parent | child" bagian dari inti sudut?

@telekid - Kami tidak memiliki rencana untuk memasukkan fitur ini ke dalam inti sekarang.

Saya melihat ada solusi dan solusi, tetapi akan sangat nyaman jika ada cara untuk mengakses ruang lingkup di dalam arahan.

Saya ingin menunjukkan bahwa saya memperbarui mod transklusi yang dibuat oleh transklusi multi-slot.
Cabang: 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

Apa yang akhirnya saya lakukan hanyalah menggunakan $parent . Ini lemari vanila tanpa harus menambahkan terlalu banyak hal.

Jadi saya punya sesuatu seperti:

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>

Hai @moneytree-doug: Saya menggunakan solusi yang Anda berikan ini, tetapi saya menemukan ruang lingkup transclude-ini masih lingkup arahan, bukan anak dari lingkup baru yang dihasilkan oleh ng-repeat. Bisakah Anda memberi saya beberapa saran?

@szetin Bisakah Anda memasang jsfiddle yang menunjukkan kepada saya apa yang Anda harapkan vs apa yang terjadi?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat