عند كتابة توجيه Angular ، يمكن للمرء استخدام أي من الوظائف التالية لمعالجة سلوك DOM ومحتوياته وإلقاء نظرة على العنصر الذي تم إعلان التوجيه عليه:
يبدو أن هناك بعض الالتباس فيما يتعلق بالوظيفة التي يجب استخدامها مرة واحدة. يغطي هذا السؤال:
بناءً على ما يلي 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
في جميع التوجيهات. بيانيا ، يمكن أن نعبر عنها هكذا:
قد يكون من المهم الإشارة إلى أنه في هذه المرحلة ، تكون القوالب التي تحصل عليها وظيفة الترجمة هي القوالب المصدر (وليس قالب المثال).
غالبًا ما تكون مثيلات DOM نتيجة لقالب مصدر يتم تقديمه إلى DOM ، ولكن قد يتم إنشاؤه بواسطة ng-repeat
، أو يتم تقديمه سريعًا.
كلما تم تقديم مثيل جديد لعنصر بتوجيه إلى DOM ، تبدأ مرحلة الارتباط.
في هذه المرحلة ، Angular تستدعي controller
، pre-link
، تكرّر الأطفال ، وتدعو post-link
في جميع التوجيهات ، مثل ذلك:
يتم تنفيذ وظائف التوجيه المختلفة من داخل وظيفتين زاويتين أخريين $compile
(حيث يتم تنفيذ التوجيه compile
) ووظيفة داخلية تسمى nodeLinkFn
(حيث controller
و preLink
و postLink
). تحدث أشياء مختلفة داخل الوظيفة الزاوية قبل وبعد توجيه وظائف التوجيه. ولعل أبرز ما هو العودية الطفل. يوضح الرسم التوضيحي المبسط التالي الخطوات الرئيسية في مراحل التحويل البرمجي والرابط:
لشرح هذه الخطوات ، دعنا نستخدم علامة 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
، يكون الأطفال "مباشرون" جاهزون. هذا يشمل:
سيبدو القالب في هذه المرحلة هكذا:
<my-element>
<div class="ng-binding">
"{{label}}"
<div ng-transclude>
<div class="ng-scope">Inner content</div>
</div>
</div>
</my-element>
إذا أراد المرء استخدام الوظيفة الأربعة ، فسيتبع التوجيه هذا النموذج:
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
},
};
});
حقيقة أن Angular تسمح بمعالجة DOM تعني أن علامة الإدخال في عملية التحويل البرمجي تختلف أحيانًا عن الإخراج. على وجه الخصوص ، قد يتم استنساخ بعض علامات الإدخال عدة مرات (كما هو الحال مع ng-repeat
) قبل عرضها على 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 وسيتم ربطه بالنطاق ذي الصلة.
يتم استدعاء وظيفة 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
، فقد يكون الخيار الأخير أسرع من الخيار السابق.
يتم استدعاء دالة controller
لكل توجيه كلما تم إنشاء مثيل لعنصر جديد مرتبط.
رسمياً ، الدالة controller
هي حيث واحد:
مرة أخرى ، من المهم أن تتذكر أنه إذا كان التوجيه يتضمن نطاقًا معزولًا ، فلن تتوفر بعد أي خصائص داخله ترث من النطاق الأصل.
عندما يتم استدعاء وظيفة post-link
، اتخذت جميع الخطوات السابقة - ملزمة ، transclusion ، إلخ.
هذا هو عادة مكان لمزيد من التلاعب DOM المقدمة.
يتم استدعاء دالة pre-link
الخاصة بكل توجيه كلما تم إنشاء عنصر جديد ذي صلة.
كما رأينا سابقًا في قسم ترتيب التحويل البرمجي ، تُسمى وظائف pre-link
الوالد ثم الطفل ، في حين تسمى وظائف post-link
child-then-parent
.
نادراً ما يتم استخدام وظيفة pre-link
، ولكن يمكن أن تكون مفيدة في السيناريوهات الخاصة ؛ على سبيل المثال ، عندما تسجل وحدة التحكم التابعة نفسها مع وحدة التحكم الأصل ، ولكن يجب أن يكون التسجيل بطريقة parent-then-child
(ngModelController
يفعل الأشياء بهذه الطريقة).