ما الفرق بين "@" و "=" في النطاق التوجيهي في AngularJS؟




angularjs-directive angularjs-scope (10)

لقد قرأت وثائق AngularJS حول هذا الموضوع بعناية ، ثم تلاعبت بتوجيه. ها هي fiddle

وهنا بعض المقتطفات ذات الصلة:

  • من HTML:

    <pane bi-title="title" title="{{title}}">{{text}}</pane>
    
  • من التوجيه جزء:

    scope: { biTitle: '=', title: '@', bar: '=' },
    

هناك عدة أشياء لا أحصل عليها:

  • لماذا يتعين علي استخدام "{{title}}" مع '@' و "title" مع '=' ؟
  • هل يمكنني أيضًا الوصول إلى النطاق الرئيسي مباشرةً ، بدون تزيين العنصر الخاص بي باستخدام سمة؟
  • تقول الوثائق "في كثير من الأحيان يكون من المرغوب فيه تمرير البيانات من النطاق المعزول عن طريق تعبير والنطاق الأصلي" ، ولكن يبدو أن ذلك يعمل بشكل جيد مع الربط ثنائي الاتجاه أيضًا. لماذا يكون مسار التعبير أفضل؟

لقد وجدت أداة كمان أخرى تُظهر حل التعبير أيضًا: http://jsfiddle.net/maxisam/QrCXh/


لماذا يتعين علي استخدام "{{title}}" مع " @ " و "title" مع " =

@ يربط خاصية نطاق محلي / توجيهي بالقيمة التي تم تقييمها لسمة DOM . إذا كنت تستخدم title=title1 أو title="title1" ، فإن قيمة "عنوان" سمة DOM هي ببساطة title1 السلسلة title1 . إذا كنت تستخدم title="{{title}}" ، فإن قيمة "سمة" سمة DOM هي القيمة interpolated لـ {{title}} ، ومن هنا ستكون السلسلة أي خاصية النطاق الأم "عنوان" تم تعيينها حاليًا. نظرًا لأن قيم السمات هي دائمًا سلاسل ، فستنتهي دائمًا بقيمة سلسلة لهذه الخاصية في نطاق التوجيه عند استخدام @ .

= يربط خاصية نطاق محلي / توجيه بخاصية نطاق الأصل . لذلك باستخدام = ، يمكنك استخدام اسم خاصية الطراز / النطاق الرئيسي كقيمة لسمة DOM. لا يمكنك استخدام {{}} s with = .

باستخدام @ ، يمكنك القيام بأشياء مثل title="{{title}} and then some" - {{title}} يتم تقريبه ، ثم تكون السلسلة "وبعضها" متسلسلة معها. السلسلة المتسلسلة النهائية هي ما تحصل عليه خاصية النطاق المحلي / التوجيهي. (لا يمكنك القيام بذلك باستخدام = ، فقط @ .)

باستخدام @ ، ستحتاج إلى استخدام attr.$observe('title', function(value) { ... }) إذا كنت بحاجة إلى استخدام القيمة في وظيفة الارتباط (ing) الخاصة بك. على سبيل المثال ، if(scope.title == "...") لن يعمل كما تتوقع. لاحظ أن هذا يعني أنه لا يمكنك الوصول إلى هذه السمة إلا asynchronously . لست بحاجة إلى استخدام ملاحظة $ () إذا كنت تستخدم القيمة الموجودة في قالب فقط. على سبيل المثال ، template: '<div>{{title}}</div>' .

مع = ، لا تحتاج إلى استخدام مراقبة $.

هل يمكنني أيضًا الوصول إلى النطاق الرئيسي مباشرةً ، بدون تزيين العنصر الخاص بي باستخدام سمة؟

نعم ، ولكن فقط إذا لم تستخدم نطاق عزل. إزالة هذا الخط من التوجيه الخاص بك

scope: { ... }

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

تقول الوثائق "في كثير من الأحيان يكون من المرغوب فيه تمرير البيانات من النطاق المعزول عن طريق تعبير ونطاق الأصل" ، ولكن يبدو أن ذلك يعمل بشكل جيد مع الربط ثنائي الاتجاه أيضًا. لماذا يكون مسار التعبير أفضل؟

نعم ، يتيح الربط ثنائي الاتجاه المجال المحلي / التوجيه والنطاق الرئيسي لمشاركة البيانات. يسمح "الربط بالتعبير" للتوجيه باستدعاء تعبير (أو وظيفة) يتم تعريفه بواسطة سمة DOM - ويمكنك أيضًا تمرير البيانات كوسيطة للتعبير أو الوظيفة. لذلك ، إذا لم تكن بحاجة إلى مشاركة البيانات مع أحد الوالدين - فأنت تريد فقط استدعاء وظيفة محددة في النطاق الرئيسي - يمكنك استخدام & بناء الجملة.

أنظر أيضا


لماذا يتعين علي استخدام "{{title}}" مع "@" و "title" مع "="؟

عند استخدام {{title}} ، سيتم تمرير قيمة نطاق الأصل فقط إلى طريقة العرض الموجهة ويتم تقييمها. هذا يقتصر على طريقة واحدة ، مما يعني أن التغيير لن ينعكس في نطاق الأصل. يمكنك استخدام "=" عندما تريد أن تعكس التغييرات التي تمت في التوجيه التابع للطفل إلى نطاق الأصل أيضًا. هذا طريقان.

هل يمكنني أيضًا الوصول إلى النطاق الرئيسي مباشرةً ، بدون تزيين العنصر الخاص بي باستخدام سمة؟

عندما يحتوي التوجيه على سمة نطاق فيه (النطاق: {}) ، لن تتمكن بعد ذلك من الوصول إلى النطاق الرئيسي مباشرة. ولكن لا يزال من الممكن الوصول إليه عبر النطاق. $ parent parent etc. إذا قمت بإزالة نطاق من التوجيه ، فيمكن الوصول إليه مباشرة.

تقول الوثائق "في كثير من الأحيان يكون من المرغوب فيه تمرير البيانات من النطاق المعزول عن طريق تعبير ونطاق الأصل" ، ولكن يبدو أن ذلك يعمل بشكل جيد مع الربط ثنائي الاتجاه أيضًا. لماذا يكون مسار التعبير أفضل؟

ذلك يعتمد على السياق. إذا كنت ترغب في الاتصال بتعبير أو وظيفة باستخدام البيانات ، فأنت تستخدم & ، وإذا كنت تريد مشاركة البيانات ، فيمكنك استخدام طريقة ما قبل العمليات باستخدام '='

يمكنك العثور على الاختلافات بين طرق متعددة لتمرير البيانات إلى التوجيه في الرابط أدناه:

AngularJS - نطاقات معزولة - @ vs = مقابل &

http://www.codeforeach.com/angularjs/angularjs-isolated-scopes-vs-vs


إذا كنت ترغب في معرفة المزيد عن هذا العمل مع مثال حي. http://jsfiddle.net/juanmendez/k6chmnch/

var app = angular.module('app', []);
app.controller("myController", function ($scope) {
    $scope.title = "binding";
});
app.directive("jmFind", function () {
    return {
        replace: true,
        restrict: 'C',
        transclude: true,
        scope: {
            title1: "=",
            title2: "@"
        },
        template: "<div><p>{{title1}} {{title2}}</p></div>"
    };
});

الطريقة = طريقة الربط ثنائية الاتجاه ، والتي تتيح لك إجراء تغييرات مباشرة داخل التوجيه الخاص بك. عندما يقوم شخص ما بتغيير هذا المتغير خارج التوجيه ، فسيكون لديك البيانات التي تم تغييرها داخل التوجيه الخاص بك ، ولكن @ الطريقة ليست ملزمة من جانبين . وهي تعمل مثل النص . أنت تربط مرة واحدة ، وسيكون لديك فقط قيمته.

للحصول على مزيد من الوضوح ، يمكنك استخدام هذا المقال الرائع:

AngularJS Directive Scope '@' and '='


حتى عندما يكون النطاق محليًا ، كما في المثال الخاص بك ، يمكنك الوصول إلى النطاق الأصل من خلال الموقع $parent property. بافتراض أنك في الكود أدناه ، يتم تعريف هذا title على النطاق الرئيسي. يمكنك بعد ذلك الدخول إلى العنوان كـ $parent.title :

link : function(scope) { console.log(scope.$parent.title) },
template : "the parent has the title {{$parent.title}}"

ولكن في معظم الحالات ، يتم الحصول على نفس التأثير بشكل أفضل باستخدام السمات.

مثال على المكان الذي وجدت فيه "و" الترميز ، الذي يتم استخدامه "لتمرير البيانات من النطاق المعزول عبر تعبير ونطاق الأم" ، كان مفيدًا (ولم يكن من الممكن استخدام ربط البيانات في اتجاهين) في توجيه لتقديم بنية datastructure خاصة داخل ng-repeat.

<render data = "record" deleteFunction = "dataList.splice($index,1)" ng-repeat = "record in dataList" > </render>

جزء واحد من العرض كان زر الحذف وهنا كان من المفيد إرفاق حذف من النطاق الخارجي عبر &. داخل تقديم التقديم يبدو

scope : { data = "=", deleteFunction = "&"},
template : "... <button ng-click = "deleteFunction()"></button>"

لا يمكن استخدام قاعدة البيانات ثنائية الاتجاه أي data = "=" حيث تعمل وظيفة الحذف على كل دورة من $digest ، وهي ليست جيدة ، حيث يتم حذف السجل فورًا ولا يتم عرضه أبدًا.


لقد أنشأت ملف HTML صغيرًا يحتوي على رمز Angular يوضح الاختلافات بينهما:

https://gist.github.com/RobertAKARobin/a02426c375596f0bef89

<!DOCTYPE html>
<html>
  <head>
    <title>Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
  </head>
  <body ng-app="myApp">
    <div ng-controller="myCtrl as VM">
      <a my-dir
        attr1="VM.sayHi('Juan')"
        attr2="VM.sayHi('Juan')"
        attr3="VM.sayHi('Juan')"></a>
    </div>
    <script>
    angular.module("myApp", [])
    .controller("myCtrl", [function(){
      var vm = this;
      vm.sayHi = function(name){
        return ("Hey there, " + name);
      }
    }])
    .directive("myDir", [function(){
      var directive = {
        scope: { attr1: "=", attr2: "@", attr3: "&" },
        link: function(scope){
          console.log(scope.attr1);   // logs "Hey there, Juan"
          console.log(scope.attr2);   // logs "VM.sayHi('Juan')"
          console.log(scope.attr3);   // logs "function (a){return h(c,a)}"
          console.log(scope.attr3()); // logs "Hey there, Juan"
        }
      }
      return directive;
    }]);
    </script>
  </body>
</html>

هناك الكثير من الإجابات الرائعة هنا ، لكني أرغب في تقديم وجهة نظري حول الاختلافات بين @ و = و & ارتباط ثبت أنه مفيد بالنسبة لي.

جميع عمليات الربط الثلاثة هي طرق لتمرير البيانات من نطاقك الرئيسي إلى النطاق المعزول للتوجيه من خلال سمات العنصر:

  1. @ الربط هو لتمرير السلاسل. هذه السلاسل تدعم {{}} تعبيرات للقيم interpolated. فمثلا: . يتم تقييم التعبير interpolated مقابل النطاق الرئيسي للتوجيه.

  2. = الربط لربط نموذج ثنائي الاتجاه. يرتبط النموذج في نطاق الأصل بالنموذج في النطاق المعزول للتوجيه. تؤثر التغييرات على أحد النماذج على الآخر ، والعكس صحيح.

  3. والربط هو تمرير إحدى الطريقتين إلى نطاق توجيهك بحيث يمكن استدعائها ضمن توجيهك. تكون هذه الطريقة مرتبطة مسبقًا بالنطاق الرئيسي الخاص بالتوجيه ، وتدعم الوسيطات. على سبيل المثال ، إذا كانت الطريقة مرحبًا (اسمًا) في نطاق الأصل ، فعندئذٍ من أجل تنفيذ الطريقة من داخل توجيهك ، يجب عليك الاتصال بـ scope.hello $ ({name: 'world'})

أجد أنه من الأسهل تذكر هذه الاختلافات من خلال الإشارة إلى روابط النطاق عن طريق وصف أقصر:

  • @ ربط سلسلة السمة
  • = ربط النموذج ثنائي الاتجاه
  • & أسلوب رد الاتصال ملزم

تجعل الرموز أيضًا أكثر وضوحًا فيما يمثل متغير النطاق داخل تنفيذ توجيهك:

  • @ السلسلة
  • = نموذج
  • وطريقة

من أجل الفائدة (بالنسبة لي على أي حال):

  1. =
  2. @
  3. و

هناك ثلاث طرق يمكن إضافة نطاق في التوجيه:

  1. نطاق الوالدين : هذا هو نطاق الوراة الافتراضي.

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

  1. نطاق الطفل : يؤدي التوجيه إلى إنشاء نطاق فرعي يرث من النطاق الأصلي إذا قمت بتحديد متغير النطاق للتوجيه على أنه true.

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

مثال،

app.directive("myDirective", function(){

    return {
        restrict: "EA",
        scope: true,
        link: function(element, scope, attrs){
            scope.somvar = "new value"; //doesnot reflect in the parent scope
            scope.someObj.someProp = "new value"; //reflects as someObj is of parent, we modified that but did not override.
        }
    };
});
  1. نطاق معزول : يستخدم هذا عندما تريد إنشاء النطاق الذي لا يرث من نطاق وحدة التحكم.

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

الآن ، إذا كنت لا تريد أي تفاعل مع النطاق الرئيسي ، فيمكنك فقط تحديد النطاق ككائن فارغ. مثل،

scope: {} //this does not interact with the parent scope in any way

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

1. "@"   (  Text binding / one-way binding )
2. "="   ( Direct model binding / two-way binding )
3. "&"   ( Behaviour binding / Method binding  )

@ يعني أن التغييرات من نطاق وحدة التحكم سوف تنعكس في نطاق التوجيه ولكن إذا قمت بتعديل القيمة في نطاق التوجيه ، فلن يتأثر متغير نطاق وحدة التحكم.

@ تتوقع دائمًا أن تكون السمة المعنونة تعبيرًا. هذا مهم جدا؛ نظرًا لإمكانية عمل البادئة "@" ، نحتاج إلى لف قيمة السمة داخل {{}}.

= ثنائي الاتجاه بحيث إذا قمت بتغيير المتغير في نطاق التوجيه ، يتأثر متغير نطاق وحدة التحكم أيضًا

& يتم استخدامها لربط طريقة نطاق وحدة التحكم بحيث يمكننا ، إذا لزم الأمر ، استدعائها من التوجيه

الميزة هنا هي أن اسم المتغير لا يجب أن يكون نفسه في نطاق وحدة التحكم ونطاق التوجيه.

على سبيل المثال ، يحتوي نطاق التوجيه على متغير "dirVar" الذي يتزامن مع متغير "contVar" لنطاق وحدة التحكم. هذا يعطي الكثير من الطاقة والتعميم إلى التوجيه كما يمكن مزامنة وحدة تحكم واحدة مع متغير v1 بينما يمكن أن تطلب وحدة تحكم أخرى باستخدام نفس التوجيه dirVar للمزامنة مع متغير v2.

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

التوجيه والتحكم هي:

 var app = angular.module("app", []);
 app.controller("MainCtrl", function( $scope ){
    $scope.name = "Harry";
    $scope.color = "#333333";
    $scope.reverseName = function(){
     $scope.name = $scope.name.split("").reverse().join("");
    };
    $scope.randomColor = function(){
        $scope.color = '#'+Math.floor(Math.random()*16777215).toString(16);
    };
});
app.directive("myDirective", function(){
    return {
        restrict: "EA",
        scope: {
            name: "@",
            color: "=",
            reverse: "&"
        },
        link: function(element, scope, attrs){
           //do something like
           $scope.reverse(); 
          //calling the controllers function
        }
    };
});

و html (لاحظ الاختلاف لـ @ و =):

<div my-directive
  class="directive"
  name="{{name}}"
  reverse="reverseName()"
  color="color" >
</div>

إليك link إلى المدونة يصفه جيدًا.


@ الحصول على كسلسلة

  • هذا لا يخلق أي ارتباطات على الإطلاق. أنت ببساطة تحصل على الكلمة التي مررت بها كسلسلة

= 2 طريقة ملزمة

  • ستنعكس التغييرات التي يتم إجراؤها من وحدة التحكم في المرجع الذي يحمله التوجيه والعكس بالعكس

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

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


هذا الكمان يجب أن يشرح كيف يعمل . إيلاء اهتمام خاص لوظائف النطاق مع get... على اسم في فهم أفضل ما أعنيه &


@ و = انظر الإجابات الأخرى.

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

شرح
& هو عبارة عن مرجع تعبير ، ويعني ذلك أنك إذا <myDirective expr="x==y"></myDirective> شيئًا مثل <myDirective expr="x==y"></myDirective>
في التوجيه ، ستكون هذه الدالة expr دالة تستدعي التعبير ، مثل:
function expr(){return x == y} .
لذلك في html الخاص بتوجيه <button ng-click="expr()"></button> سوف يستدعي التعبير. في شبيبة التوجيه فقط $scope.expr() التعبير أيضا.
سيتم استدعاء التعبير بـ $ scope.x و $ scope.y من الأصل.
لديك القدرة على تجاوز المعلمات!
إذا قمت بتعيينهم عن طريق الاتصال ، على سبيل المثال <button ng-click="expr({x:5})"></button>
ثم سيتم استدعاء التعبير مع المعلمة x والمعلمة y للمعلمة y .
يمكنك تجاوز كلا.
الآن أنت تعرف لماذا يعمل <button ng-click="functionFromParent({x:5})"></button> .
لأنه يقوم فقط باستدعاء تعبير الوالدين (على سبيل المثال <myDirective functionFromParent="function1(x)"></myDirective> ) ويستبدل القيم المحتملة مع المعلمات المحددة ، في هذه الحالة x .
يمكن ان تكون:
<myDirective functionFromParent="function1(x) + 5"></myDirective>
أو
<myDirective functionFromParent="function1(x) + z"></myDirective>
مع مكالمة الأطفال:
<button ng-click="functionFromParent({x:5, z: 4})"></button> .
أو حتى مع استبدال الوظيفة:
<button ng-click="functionFromParent({function1: myfn, x:5, z: 4})"></button> .

انها مجرد تعبير ، لا يهم إذا كانت وظيفة ، أو العديد من الوظائف ، أو مجرد المقارنة. ويمكنك استبدال أي متغير من هذا التعبير.

أمثلة:
القالب التوجيهي دعا الكود:
لقد حدد الأصل نطاق $.x ، $ scope.y:
القالب الأصل: <myDirective expr="x==y"></myDirective>
<button ng-click="expr()"></button> يستدعي $scope.x==$scope.y
<button ng-click="expr({x: 5})"></button> calls 5 == $scope.y
<button ng-click="expr({x:5, y:6})"></button> calls 5 == 6

لقد حدد الطرف الأم $ scope.function1 ، $ scope.x ، $ scope.y:
القالب الأصل: <myDirective expr="function1(x) + y"></myDirective>

<button ng-click="expr()"></button> يستدعي $scope.function1($scope.x) + $scope.y
<button ng-click="expr({x: 5})"></button> يستدعي $scope.function1(5) + $scope.y
<button ng-click="expr({x:5, y:6})"></button> يستدعي $scope.function1(5) + 6
التوجيه لديه $ scope.myFn كدالة:
<button ng-click="expr({function1: myFn, x:5, y:6})"></button> يستدعي $scope.myFn(5) + 6







isolated-scope