it-swarm.asia

Angular التوجيهات - متى وكيف يتم استخدام التحويل البرمجي والتحكم والرابط المسبق وما بعد الارتباط

عند كتابة توجيه Angular ، يمكن للمرء استخدام أي من الوظائف التالية لمعالجة سلوك DOM ومحتوياته وإلقاء نظرة على العنصر الذي تم إعلان التوجيه عليه:

  • جمع
  • مراقب
  • قبل الارتباط
  • بعد الارتباط

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

أساسيات التوجيه

وظيفة الطبيعة ، ما يفعل وما لا

أسئلة ذات صلة:

448
Izhaki

في أي ترتيب يتم تنفيذ وظائف التوجيه؟

لتوجيه واحد

بناءً على ما يلي plunk ، ضع في الاعتبار علامة HTML التالية:

<body>
    <div log='some-div'></div>
</body>

مع إعلان التوجيه التالي:

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});

سيكون الإخراج وحدة التحكم:

some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

يمكننا أن نرى أن compile يتم تنفيذها أولاً ، ثم controller ، ثم pre-link والأخير هو post-link.

لتوجيهات متداخلة

ملاحظة: لا ينطبق ما يلي على التوجيهات التي تجعل أطفالهم في وظيفة الارتباط الخاصة بهم. هناك عدد قليل جدًا من التوجيهات (Angular) تقوم بذلك (مثل ngIf أو ngRepeat أو أي توجيه باستخدام transclude). سيكون لهذه التوجيهات أصلاً دالة link تسمى _ ​​قبل وتسمى توجيهاتهم الفرعية compile.

غالبًا ما تكون علامة HTML الأصلية مكونة من عناصر متداخلة ، ولكل منها توجيهها الخاص. كما هو الحال في العلامات التالية (انظر الشرير ):

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>

سيظهر إخراج وحدة التحكم بالشكل التالي:

// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

يمكننا التمييز بين مرحلتين هنا - المرحلة الترجمة والمرحلة الرابط.

مرحلة الترجمة

عندما يتم تحميل DOM Angular يبدأ مرحلة الترجمة ، حيث يجتاز العلامات من أعلى إلى أسفل ، ويستدعي compile في جميع التوجيهات. بيانيا ، يمكن أن نعبر عنها هكذا:

An image illustrating the compilation loop for children

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

مرحلة الارتباط

غالبًا ما تكون مثيلات DOM نتيجة لقالب مصدر يتم تقديمه إلى DOM ، ولكن قد يتم إنشاؤه بواسطة ng-repeat ، أو يتم تقديمه سريعًا.

كلما تم تقديم مثيل جديد لعنصر بتوجيه إلى DOM ، تبدأ مرحلة الارتباط.

في هذه المرحلة ، Angular تستدعي controller ، pre-link ، تكرّر الأطفال ، وتدعو post-link في جميع التوجيهات ، مثل ذلك:

An illustration demonstrating the link phase steps

166
Izhaki

ماذا يحدث بين هذه المكالمات وظيفة؟

يتم تنفيذ وظائف التوجيه المختلفة من داخل وظيفتين زاويتين أخريين $compile (حيث يتم تنفيذ التوجيه compile) ووظيفة داخلية تسمى nodeLinkFn (حيث controller و preLink و postLink). تحدث أشياء مختلفة داخل الوظيفة الزاوية قبل وبعد توجيه وظائف التوجيه. ولعل أبرز ما هو العودية الطفل. يوضح الرسم التوضيحي المبسط التالي الخطوات الرئيسية في مراحل التحويل البرمجي والرابط:

An illustration showing Angular compile and link phases

لشرح هذه الخطوات ، دعنا نستخدم علامة HTML التالية:

<div ng-repeat="i in [0,1,2]">
    <my-element>
        <div>Inner content</div>
    </my-element>
</div>

مع التوجيه التالي:

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '<div>{{label}}<div ng-transclude></div></div>'
    }
});

جمع

compile API يبدو هكذا:

compile: function compile( tElement, tAttributes ) { ... }

غالبًا ما تكون المعلمات مسبوقة بـ t للدلالة على العناصر والسمات المقدمة هي تلك الخاصة بقالب المصدر ، بدلاً من تلك الموجودة في المثيل.

قبل الاستدعاء إلى compile المحتوى المتضمن (إن وجد) ، تتم إزالة القالب ويتم تطبيقه على العلامات. وبالتالي ، سيبدو العنصر الموفر للدالة compile هكذا:

<my-element>
    <div>
        "{{label}}"
        <div ng-transclude></div>
    </div>
</my-element>

لاحظ أنه لا يتم إعادة إدراج المحتوى الذي تم تجاوزه في هذه المرحلة.

بعد الدعوة إلى التوجيه .compile ، ستجتاز Angular جميع العناصر الفرعية ، بما في ذلك العناصر التي قد تم تقديمها للتو بواسطة التوجيه (عناصر القالب ، على سبيل المثال).

إنشاء مثيل

في حالتنا ، سيتم إنشاء ثلاث حالات من قالب المصدر أعلاه (بواسطة ng-repeat). وبالتالي ، سيتم تنفيذ التسلسل التالي ثلاث مرات ، مرة واحدة لكل مثيل.

مراقب

يتضمن controller API:

controller: function( $scope, $element, $attrs, $transclude ) { ... }

عند الدخول في مرحلة الارتباط ، يتم توفير وظيفة الارتباط التي يتم إرجاعها عبر $compile الآن مع نطاق.

أولاً ، تنشئ وظيفة الارتباط نطاقًا فرعيًا (scope: true) أو نطاقًا معزولًا (scope: {...}) إذا طُلب منك ذلك.

ثم يتم تنفيذ وحدة التحكم ، مع توفير نطاق عنصر المثيل.

قبل الارتباط

واجهة برمجة التطبيقات pre-link تبدو هكذا:

function preLink( scope, element, attributes, controller ) { ... }

لا يحدث شيء تقريبًا بين استدعاء رمز التوجيه .controller ووظيفة .preLink. Angular- ما زالت تقدم توصية بشأن كيفية استخدام كل منها.

بعد استدعاء .preLink ، ستعمل وظيفة الارتباط على اجتياز كل عنصر فرعي - استدعاء وظيفة الارتباط الصحيحة وإرفاقها بالنطاق الحالي (والذي يمثل النطاق الأصل للعناصر الفرعية).

بعد الارتباط

يشبه post-link API تلك الخاصة بوظيفة pre-link:

function postLink( scope, element, attributes, controller ) { ... }

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

هذا يعني أنه بحلول الوقت الذي يتم فيه استدعاء .postLink ، يكون الأطفال "مباشرون" جاهزون. هذا يشمل:

  • ربط البيانات
  • تطبيق transclusion
  • نطاق المرفقة

سيبدو القالب في هذه المرحلة هكذا:

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>
90
Izhaki

كيف تعلن الوظائف المختلفة؟

ترجمة ، تحكم ، قبل الارتباط وبعد الارتباط

إذا أراد المرء استخدام الوظيفة الأربعة ، فسيتبع التوجيه هذا النموذج:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.
            return {
                pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
                    // Pre-link code goes here
                },
                post: function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-link code goes here
                }
            };
        }
    };  
});

لاحظ أن التحويل البرمجي يُرجع كائنًا يحتوي على وظائف ما قبل الارتباط و post-link ؛ في Angular نقول أن دالة الترجمة تُرجع دالة template .

ترجمة ، تحكم & بعد الارتباط

إذا لم يكن pre-link ضروريًا ، فبإمكان دالة التحويل البرمجي ببساطة إرجاع دالة النشر اللاحق بدلاً من كائن التعريف ، مثل ذلك:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.
            return function postLink( scope, element, attributes, controller, transcludeFn ) {
                    // Post-link code goes here                 
            };
        }
    };  
});

في بعض الأحيان ، يرغب المرء في إضافة طريقة compile ، بعد تحديد طريقة (نشر) link. لهذا ، يمكن للمرء استخدام:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        compile: function compile( tElement, tAttributes, transcludeFn ) {
            // Compile code goes here.

            return this.link;
        },
        link: function( scope, element, attributes, controller, transcludeFn ) {
            // Post-link code goes here
        }

    };  
});

المراقب و بعد الارتباط

إذا لم تكن هناك حاجة إلى وظيفة ترجمة ، فبإمكان المرء تخطي الإعلان الخاص به تمامًا وتوفير وظيفة ما بعد الارتباط ضمن خاصية link لعنصر تكوين التوجيه:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        controller: function( $scope, $element, $attrs, $transclude ) {
            // Controller code goes here.
        },
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});

لا تحكم

في أي من الأمثلة المذكورة أعلاه ، يمكن للمرء ببساطة إزالة الدالة controller إذا لم تكن هناك حاجة إليها. على سبيل المثال ، إذا كانت وظيفة post-link مطلوبة فقط ، فيمكنك استخدام:

myApp.directive( 'myDirective', function () {
    return {
        restrict: 'EA',
        link: function postLink( scope, element, attributes, controller, transcludeFn ) {
                // Post-link code goes here                 
        },          
    };  
});
43
Izhaki

ما هو الفرق بين قالب المصدر و قالب مثيل ؟

حقيقة أن Angular تسمح بمعالجة DOM تعني أن علامة الإدخال في عملية التحويل البرمجي تختلف أحيانًا عن الإخراج. على وجه الخصوص ، قد يتم استنساخ بعض علامات الإدخال عدة مرات (كما هو الحال مع ng-repeat) قبل عرضها على DOM.

المصطلحات الزاوية غير متسقة بعض الشيء ، لكنها ما زالت تميز بين نوعين من العلامات:

  • قالب المصدر - العلامات المراد استنساخها ، إذا لزم الأمر. إذا تم استنساخها ، فلن يتم تقديم هذا الترميز إلى DOM.
  • قالب مثيل - الترميز الفعلي الذي سيتم تقديمه إلى DOM. إذا كان الاستنساخ متورطًا ، فستكون كل حالة نسخة.

توضح العلامات التالية هذا:

<div ng-repeat="i in [0,1,2]">
    <my-directive>{{i}}</my-directive>
</div>

يحدد مصدر HTML

    <my-directive>{{i}}</my-directive>

الذي بمثابة القالب المصدر.

ولكن كما هو ملفوف داخل توجيه ng-repeat ، سيتم استنساخ قالب المصدر هذا (3 مرات في حالتنا). هذه النسخ هي قالب مثيل ، سيظهر كل منها في DOM وسيتم ربطه بالنطاق ذي الصلة.

30
Izhaki

ترجمة وظيفة

يتم استدعاء وظيفة compile لكل توجيه مرة واحدة فقط ، عندما Angular bootstraps.

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

في المقام الأول ، يتم ذلك لأغراض التحسين ؛ النظر في العلامات التالية:

<tr ng-repeat="raw in raws">
    <my-raw></my-raw>
</tr>

سيعرض التوجيه <my-raw> مجموعة معينة من ترميز DOM. لذلك يمكننا إما:

  • اسمح لـ ng-repeat بتكرار القالب المصدر (<my-raw>) ، ثم عدِّل ترميز كل قالب مثيل (خارج دالة compile).
  • قم بتعديل القالب المصدر لإشراك الترميز المطلوب (في دالة compile) ، ثم اترك ng-repeat لتكراره.

إذا كان هناك 1000 عنصر في مجموعة raws ، فقد يكون الخيار الأخير أسرع من الخيار السابق.

فعل:

  • قم بمعالجة الترميز بحيث يعمل كقالب لمثيلات (استنساخ).

لا

  • إرفاق معالجات الأحداث.
  • فحص العناصر الطفل.
  • إعداد الملاحظات على السمات.
  • إعداد الساعات على النطاق.
23
Izhaki

وظيفة تحكم

يتم استدعاء دالة controller لكل توجيه كلما تم إنشاء مثيل لعنصر جديد مرتبط.

رسمياً ، الدالة controller هي حيث واحد:

  • يعرّف منطق وحدة التحكم (الأساليب) التي يمكن مشاركتها بين وحدات التحكم.
  • يبدأ متغيرات النطاق.

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

فعل:

  • تحديد منطق تحكم
  • بدء متغيرات النطاق

لا:

  • فحص العناصر الفرعية (قد لا يتم تقديمها بعد ، أو ترتبط بالنطاق ، وما إلى ذلك).
19
Izhaki

وظيفة بعد الارتباط

عندما يتم استدعاء وظيفة post-link ، اتخذت جميع الخطوات السابقة - ملزمة ، transclusion ، إلخ.

هذا هو عادة مكان لمزيد من التلاعب DOM المقدمة.

فعل:

  • التلاعب DOM (المقدمة ، وبالتالي إنشاء مثيل) العناصر.
  • إرفاق معالجات الأحداث.
  • فحص العناصر الطفل.
  • إعداد الملاحظات على السمات.
  • إعداد الساعات على النطاق.
19
Izhaki

وظيفة قبل الارتباط

يتم استدعاء دالة pre-link الخاصة بكل توجيه كلما تم إنشاء عنصر جديد ذي صلة.

كما رأينا سابقًا في قسم ترتيب التحويل البرمجي ، تُسمى وظائف pre-link الوالد ثم الطفل ، في حين تسمى وظائف post-linkchild-then-parent.

نادراً ما يتم استخدام وظيفة pre-link ، ولكن يمكن أن تكون مفيدة في السيناريوهات الخاصة ؛ على سبيل المثال ، عندما تسجل وحدة التحكم التابعة نفسها مع وحدة التحكم الأصل ، ولكن يجب أن يكون التسجيل بطريقة parent-then-child (ngModelController يفعل الأشياء بهذه الطريقة).

لا:

  • فحص العناصر الفرعية (قد لا يتم تقديمها بعد ، أو ترتبط بالنطاق ، وما إلى ذلك).
15
Izhaki