Angular.js: ng-transclude tidak boleh membuat cakupan saudara baru.

Dibuat pada 20 Des 2013  ·  69Komentar  ·  Sumber: angular/angular.js

Ini lebih merupakan permintaan perubahan dan saya ingin melihat apa yang orang lain pikirkan.

Menurut pendapat saya, ng-transclude tidak boleh membuat ruang lingkupnya sendiri atau setidaknya memiliki cara untuk mencegahnya. Alasan di balik ini adalah bahwa arahan yang meminta transklusi sudah memiliki sarana untuk menentukan apakah ia ingin memiliki cakupan atau cakupan terisolasi atau tidak ada cakupan sama sekali. Ini menggunakan direktif ng-transclude untuk menandai di mana ia ingin memasukkan konten. Ketika ng-transclude menciptakan lingkup saudaranya sendiri, hal itu merusak ekspektasi direktif yang mendefinisikan jenis cakupan yang diinginkan dan muncullah manifestasi dari kebingungan populer 'value' vs 'object.value'.

Berikut adalah contoh di mana ruang lingkup baru tidak masuk akal menurut saya:

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

Semua direktif ini ingin mengganti <box>content</box> dengan <div>content</div> dan konten memiliki ruang lingkup terisolasi.

Membuat struktur arahan bersarang seperti ini mengarah pada polusi pohon lingkup. Berikut adalah contoh plunker (http://plnkr.co/edit/DwukVGGprFFjQuVY8yTz) dari tiga direktif bersarang yang membuat struktur pohon lingkup seperti ini:

< 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

Perilaku ini tampaknya tidak menambah nilai apa pun pada tujuannya, tetapi menciptakan banyak kebingungan di antara para pemula.

Saat ini saya menggunakan solusi berikut yang mencapai persis seperti contoh sebelumnya:

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

Berikut adalah contoh plunker (http://plnkr.co/edit/46v6IBLkhS71L1WbUDFl) yang menggambarkan konsep ini. Itu meninggalkan pohon lingkup yang bagus dan rapi:

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

Dan pengikatan 2-arah bekerja seperti yang diharapkan banyak orang saat mereka mengikat 'nilai' daripada 'objek.value'. (Saya percaya bahwa fakta bahwa meneruskan hanya 'nilai' berfungsi dalam beberapa kasus tetapi tidak pada yang lain dan menyalahkan sifat pewarisan prototipe dalam javascript bukanlah alasan yang baik. Fakta bahwa banyak orang menganggap perilaku ini tidak terduga menunjukkan bahwa ada cacat arsitektural .)

Saya ingin mendengar pendapat orang lain dan menggunakan kasus yang menurut mereka membuat lingkup saudara baru untuk ng-transclude masuk akal.

Lots of comments $compile high won't fix bug

Komentar yang paling membantu

Untung saya beralih ke Ember bertahun-tahun yang lalu. :)

Semua 69 komentar

Di mana transclude membuat cakupan baru? http://plnkr.co/edit/EuHaBR26JgAegQKvwOGH?p=preview Saya tidak melihatnya

Saya berbicara tentang arahan ng-transclude. Apa yang Anda miliki dalam contoh Anda persis seperti yang dilakukan oleh pekerjaan saya.

ini adalah permintaan yang valid. kami mempertimbangkan ini untuk 1,2 tetapi itu mendekati rilis final dan tidak ingin memperkenalkan perubahan yang melanggar ini.

kita harus mempertimbangkannya untuk 1.3

Bagus! Saya senang Anda telah mempertimbangkannya.

1 untuk ini. Saya pikir masalah saya terkait dengan ini: Saya menggunakan ng-transclude dalam arahan dengan formulir dan saya harus melalui cakupan. $$ childHead untuk mengakses objek validasi formulir tetapi saya tidak memiliki masalah untuk mengakses model saya.

Berikut ini contohnya: http://fiddle.jshell.net/39cgW/3/

+1 mengalami masalah ini hari ini dan saya tidak suka membuang $parent mana-mana.

Jadi, untuk mencari solusi atas hal ini, tampaknya ada dua kemungkinan

1) ubah direktif ngTransclude untuk menentukan cakupannya (sebenarnya bisa menyusut lebih dari ini, saya pikir --- tidak perlu pengontrol)

atau

2) jangan buat cakupan baru jika cakupan tidak ditentukan

Jadi, kita bisa menghemat beberapa byte dengan opsi 1) dan itu bagus, 2) akan menjadi solusi terkecil (penghapusan 3 baris atau lebih) dan tidak jelas bagi saya bahwa ada kasus penggunaan di mana membuat ruang lingkup baru secara implisit masuk akal di sana ( mungkin ada, tetapi tampaknya sangat bertentangan dengan penjelasan tentang transklusi di dokumen)


Atau, jika Anda ingin menjadi super mewah, mungkin Anda dapat menghindari pemutusan perubahan sepenuhnya dengan mengizinkan ngTransclude untuk menentukan apakah ia menginginkan ruang lingkup baru atau tidak, melalui nilai atribut.

Pikiran?

Saya tidak mengerti perbedaan antara 1 dan 2 Anda!

Hanya pendapat saya, tapi menurut saya kompatibilitas ke belakang akan menjadi penting untuk perubahan ini. Saya membayangkan ada banyak aplikasi di luar sana (termasuk milik saya) yang telah mengatasi masalah cakupan ini dengan menggunakan hal-hal seperti $ parent dan scope. $$ childHead. Pembaruan apa pun yang mengubah perilaku ini akan menyebabkan sakit kepala (tapi mungkin lebih baik menyebabkan sakit kepala lebih cepat daripada nanti).

Meski begitu, dari sudut pandang teoretis, menurut saya lebih masuk akal jika ng-tranclude memiliki cakupan yang sama dengan arahan secara default. Inti dari mentransklusikan konten adalah Anda menginginkannya menjadi bagian konten yang mulus. Ada kalanya saya memiliki banyak arahan bersarang dengan transclude, tetapi saya masih ingin mereka berperilaku sebagai satu komponen besar. Memiliki mereka dengan semua cakupan berbeda membuat ini sangat rumit.

Semua hanya pikiranku. Paling tidak, hanya memiliki opsi akan menjadi satu langkah di atas situasi saat ini. :)

@troch untuk mengklarifikasi, kami memasukkan yang berikut ini ke pengontrol ngTranscludeDirective:

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

Direktif memanggil fungsi ini tanpa cakupan, dan dengan demikian, cakupannya tidak ditentukan ... Kemudian di boundTranscludeFn , ia membuat cakupan baru jika transcludeScope salah ...

Jadi, apa yang saya katakan adalah, untuk 1), kita cukup menentukan cakupan saat ini untuk fungsi transclude (karena direktif ini adalah arahan tetangga dari apa pun yang mungkin memiliki ruang lingkup terisolasi, ini masih harus memberi kita ruang lingkup asli) .

Atau, 2), jangan buat cakupan baru dan default saja ke cakupan saat ini (di createBoundTranscludeFn) (berpotensi merusak perubahan, dan berpotensi merusak banyak pengujian).

Keduanya cukup sederhana untuk dilakukan.

+1

+1

+1

Pasti +1

+1

+1

+1

Tolong beri +1

Anda tahu apa yang cukup keren, meskipun mungkin tidak sepenuhnya dapat dilakukan pada waktunya untuk 1.3, proksi ES6 dapat membuat

Jika implementasi Proxy tidak tersedia, mungkin bisa saja fall-back ke scope. $ New (), jadi sebenarnya mungkin untuk membuat ini bekerja cukup awal. Hal yang rumit adalah bahwa speknya agak goyah.

Jadi, Anda masih akan mendapatkan cakupan yang tidak diinginkan jika ingin hierarki cakupan sangat bersih, tetapi setidaknya Anda akan memiliki efek samping dari data binding yang tidak rusak secara tidak terduga. Saya tidak tahu.

+1

+1

+1

+1

+1

@bayu_joo

Atau, jika Anda ingin menjadi super mewah, mungkin Anda dapat menghindari pemutusan perubahan sepenuhnya dengan mengizinkan ngTransclude untuk menentukan apakah ia menginginkan ruang lingkup baru atau tidak, melalui nilai atribut.

Apa pun yang tidak memiliki perubahan yang merusak mendapatkan suara saya.

+1

+1

+1

+1

+1

+1. Saya selalu bertanya-tanya tentang perilaku ini. Saya suka solusi ini dari caitp:

@bayu_joo

Atau, jika Anda ingin menjadi super mewah, mungkin Anda dapat menghindari pemutusan perubahan sepenuhnya dengan mengizinkan> ngTransclude untuk menentukan apakah ia menginginkan cakupan baru atau tidak, melalui nilai atribut.

+1

+1

+1

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

  • silbing - Cakupan konten yang ditransklusikan adalah bersaudara dengan elemen tempat transklusi terjadi. Itulah perilaku ng-transclude saat ini.
  • induk - Cakupan konten yang ditransklusikan adalah elemen tempat transklusi terjadi.
  • child - Cakupan konten yang ditransklusikan adalah cakupan turunan ke cakupan elemen tempat transklusi terjadi.

Contoh penggunaan:

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

Contoh lengkapnya

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

Karena masalah yang saya kemukakan sebelumnya di # 8609 ditutup sebagai duplikat utas ini, saya ulangi di sini.

Menurut pendapat saya, cara saat ini membuat cakupan untuk bagian DOM yang ditransklusikan sangat tidak logis!
Ini bertentangan dengan aliran normal dalam sudut, dan harus diperbaiki!

Berikut ini ringkasan dari masalah saya sebelumnya:

Saya membuat potongan kecil untuk menggambarkan masalah saya di sini.

pelaku utama dalam arahan ini

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

Saat pengguna melakukan sesuatu seperti ini:

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


Hasilnya akan mengejutkan banyak orang, terutama pengguna baru. Dan ini bahkan kasus penggunaan yang (terlalu) disederhanakan. Coba tampilkan beberapa pesan validasi di sana;)

Ini adalah kasus penggunaan yang berbeda dengan awal mula masalah ini, tetapi saya setuju bahwa pada dasarnya ini adalah masalah yang sama!

Lebih masuk akal untuk mentransklusikan cakupan dengan elemen dom. Jika ruang lingkup baru benar-benar dibutuhkan, pengguna dapat meletakkan ngController pada elemen ngTransclude . Atau mungkin ada flag opsional pada ngTransclude yang memicu satu. Bendera itu juga dapat digunakan untuk menyelesaikan kasus penggunaan # 5489. Dan itu bisa digunakan untuk solusi caitp yang ditawarkan juga.

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1 tampaknya direktif yang ditransklusikan menciptakan cakupan bahkan ketika diminta untuk tidak melakukannya. contoh http://plnkr.co/edit/Wn81IBkE87vtigXvjmIa?p=preview

+1

+1

+1

+1

+1

+1

+1

+1

+1 — apakah ada solusi yang valid untuk ini?

@nikkwong , ada beberapa solusi yang diposting di utas ini. Saya tahu saya melakukan tautan dalam plunk yang menunjukkan solusi.

OK, jadi meskipun ini tiba, itu tidak akan sampai 1.5.x.

Tapi sebelumnya saya punya kekhawatiran. Alasan utama untuk membuat cakupan baru (sebut saja saudara dari cakupan isolate, atau lebih tepatnya anak cakupan asli tempat konten yang ditransklusikan berasal) adalah untuk mencegah kebocoran memori.

Bandingkan plunker ini:
http://plnkr.co/edit/3NVxdYGy1AFDvD0M2BYI?p=preview

dengan versi yang menggunakan kembali cakupan asal transkusi asli:
http://plnkr.co/edit/MXFz2awcqwQQ7R882Xwz?p=preview

Di versi kedua, saat Anda mengaktifkan dan menonaktifkan konten yang ditransklusikan, jumlah pengamat terus meningkat - kebocoran memori.

@petebacondarwin itu pasti harus menjadi ruang lingkup baru sehingga dapat dihancurkan dengan pengamatnya. Saya rasa sebagian besar kesedihan tentang transklusi adalah karena ng-transclude akan membuat cakupan saudara dari cakupan yang memuat, sehingga tidak mungkin untuk mengakses variabelnya melalui pewarisan prototipe. Atau apakah saya salah?

Kami menyadari bahwa masalahnya adalah cara kerja pengikatan sudut 2 arah, secara definitif ng-transclude membuat lingkup anak, tetapi akan ada banyak kesempatan di mana Anda akan menemukan diri Anda dalam situasi yang sama, misalnya ng-repeat menciptakan lingkup anak. Setelah melihat kode sudut, kami menyadari bahwa masalah dengan masalah pengikatan 2 arah tidak berfungsi pada properti dengan atribut objek, tetapi berfungsi dengan baik dengan objek itu sendiri

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

Solusi: http://plnkr.co/edit/KShClgQVwIjscXPzVRwR?p=preview

Ini tidak sempurna tetapi mengetahui ini memecahkan banyak masalah kami, tidak pernah melakukan pengikatan dua cara pada suatu atribut

Perhatikan bahwa solusi ini sudah disarankan oleh mbykovskyy, ketika dia mengangkat masalah, saya hanya memberikan contoh karena saya butuh beberapa saat untuk mengetahuinya.

@petebacondarwin Ini hanya akan menjadi kebocoran sementara, sampai lingkup penahanan akan dihancurkan. Berikut ini (plunk) [http://plnkr.co/edit/Q587WQnX0u0u7JjhtCxa?p=preview] yang menunjukkan bahwa jika Anda mengganti transclude-holding-scope, semuanya akan dibebaskan dengan baik.
Tetap saja itu poin yang perlu diperhatikan. Mungkin Waring besar di dokumen tentang kemungkinan kebocoran ini mungkin cukup untuk memperbaikinya?

Nah, kebocoran JS apa pun hanya bersifat sementara sampai Anda menyegarkan browser ;-)
Pada 8 Sep 2015 16:41, "Sander Elias" [email protected] menulis:

@petebacondarwin https://github.com/petebacondarwin Ini hanya akan menjadi
kebocoran sementara, hingga ruang lingkup penahanan akan hancur. Ini dia (a
plunk) [http://plnkr.co/edit/Q587WQnX0u0u7JjhtCxa?p=preview] yang menunjukkan
bahwa jika Anda mengganti transclude-holding-scope, semuanya akan diterima
dirilis dengan baik.
Tetap saja itu poin yang perlu diperhatikan. Mungkin Waring besar di
dokumen tentang kemungkinan kebocoran ini mungkin cukup untuk memperbaikinya?

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/angular/angular.js/issues/5489#issuecomment -138603298
.

@SanderElias Pengguna aplikasi Angular kami sibuk dari awal hingga akhir hari.
Kita sudah melihat bahwa penggunaan memori meningkat sepanjang hari dan kita harus sangat berhati-hati dengan apa yang kita taruh di halaman, memperkenalkan lebih banyak kemungkinan kebocoran yang berisiko.

@troch - transclusion sebenarnya membuat lingkup anak dari cakupan tempat konten yang ditransklusikan awalnya ditemukan. Ini rusak beberapa versi sebelumnya (pasti di 1.2) di mana sebagai gantinya itu hanya membuat anak dari induk dari lingkup direktif saat ini. Ini berarti bahwa transklusi yang bertingkat dalam benar-benar mendapatkan cakupan transklusi yang salah.

Saya harus setuju dengan @petebacondarwin , dan meskipun perilaku saat ini tidak 100% intuitif, perilaku saat ini berfungsi paling baik untuk mencegah kebocoran. Saya cenderung menutup masalah ini karena Tidak Akan Diperbaiki

Untung saya beralih ke Ember bertahun-tahun yang lalu. :)

Apakah halaman ini membantu?
0 / 5 - 0 peringkat