it-swarm.com.ru

'this' vs $ scope в контроллерах AngularJS

В раздел "Создание компонентов" на домашней странице AngularJS есть следующий пример:

controller: function($scope, $element) {
  var panes = $scope.panes = [];
  $scope.select = function(pane) {
    angular.forEach(panes, function(pane) {
      pane.selected = false;
    });
    pane.selected = true;
  }
  this.addPane = function(pane) {
    if (panes.length == 0) $scope.select(pane);
    panes.Push(pane);
  }
}

Обратите внимание, как метод select добавляется к $scope, а метод addPane добавляется к this. Если я изменю его на $scope.addPane, код сломается.

Документация говорит, что на самом деле есть разница, но она не упоминает, в чем разница:

Предыдущие версии Angular (до 1.0 RC) позволяли вам использовать this взаимозаменяемо с методом $scope, но это уже не так. Внутри методов, определенных в области видимости, this и $scope являются взаимозаменяемыми (угловые наборы от this до $scope), но не иначе внутри конструктора вашего контроллера.

Как this и $scope работают в контроллерах AngularJS?

1002
Alexei Boronine

«Как работают this и $scope в контроллерах AngularJS?»

Короткий ответ:

  • this
    • Когда вызывается функция конструктора контроллера, this является контроллером.
    • Когда вызывается функция, определенная для объекта $scope, this является «областью действия, действующей при вызове функции». Это может (или не может!) Быть $scope, для которого определена функция. Таким образом, внутри функции this и $scope могут not быть одинаковыми.
  • $scope
    • Каждый контроллер имеет связанный объект $scope.
    • Функция контроллера (конструктора) отвечает за установку свойств и функций/поведения модели для связанного с ней $scope.
    • Только методы, определенные в этом объекте $scope (и родительские объекты области, если прототипное наследование находится в игре) доступны из HTML/представления. Например, из ng-click, фильтров и т.д.

Длинный ответ:

Функция контроллера - это функция конструктора JavaScript. Когда выполняется функция конструктора (например, когда загружается представление), this (то есть «контекст функции») устанавливается для объекта контроллера. Так в функции конструктора контроллера «вкладки», когда создается функция addPane

this.addPane = function(pane) { ... }

он создается на объекте контроллера, а не на $ scope. Представления не могут видеть функцию addPane - они имеют доступ только к функциям, определенным в $ scope. Другими словами, в HTML это не будет работать:

<a ng-click="addPane(newPane)">won't work</a>

После выполнения функции конструктора контроллера «tabs» имеем следующее:

after tabs controller constructor function

Пунктирная черная линия указывает на наследование прототипа - область выделения прототипа наследуется от Scope . (Он не наследуется по прототипу от области действия, в которой директива встречалась в HTML.)

Теперь функция link директивы pane хочет связаться с директивой tabs (что на самом деле означает, что она должна каким-то образом воздействовать на вкладки, изолируя $ scope). Можно использовать события, но другой механизм должен иметь директиву панели require контроллера вкладок. (Кажется, нет никакого механизма для директивы панели require области $ tabs.)

Итак, напрашивается вопрос: если у нас есть доступ только к контроллеру вкладок, как мы можем получить доступ к вкладкам, изолирующим $ scope (что мы действительно хотим)?

Ну, красная пунктирная линия - это ответ. «Область» функции addPane () (я имею в виду область/замыкания функции JavaScript здесь) дает функции доступ к вкладкам, изолирующим область $. То есть, addPane () имеет доступ к «вкладкам IsolateScope» на диаграмме выше из-за замыкания, которое было создано при определении addPane (). (Если бы мы вместо этого определили addPane () на объекте $ scope вкладок, директива pane не будет иметь доступа к этой функции, и, следовательно, не сможет связаться с вкладками $ scope.)

Чтобы ответить на другую часть вашего вопроса: how does $scope work in controllers?:

В функциях, определенных в $ scope, this устанавливается на «действительный $ scope, где/когда была вызвана функция». Предположим, у нас есть следующий HTML:

<div ng-controller="ParentCtrl">
   <a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
   <div ng-controller="ChildCtrl">
      <a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
   </div>
</div>

И ParentCtrl (исключительно) имеет

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}

Нажатие на первую ссылку покажет, что this и $scope одинаковы, поскольку «область действия, когда функция была вызвана» - это область действия, связанная с ParentCtrl.

Щелкнув по второй ссылке, вы увидите, что this и $scope являются не одинаковыми, поскольку «область действия при вызове функции» - это область действия, связанная с ChildCtrl. Итак, здесь this установлен на ChildCtrl's $scope. Внутри метода $scope по-прежнему является областью $ ParentCtrl.

Fiddle

Я стараюсь не использовать this внутри функции, определенной в $ scope, так как становится непонятно, какие $ scope затрагиваются, особенно учитывая, что директивы ng-repeat, ng-include, ng-switch и все могут создавать свои собственные дочерние области ,.

971
Mark Rajcok

Причина, по которой этому назначается addPane, связана с директивой <pane>.

Директива pane выполняет require: '^tabs', который помещает объект контроллера tabs из родительской директивы в функцию link.

addPane назначается this, чтобы функция связи pane могла его видеть. Затем в функции ссылки paneaddPane является просто свойством контроллера tabs, и это просто tabsControllerObject.addPane. Таким образом, функция связывания директивы панели может обращаться к объекту контроллера вкладок и, следовательно, к методу addPane.

Надеюсь, мои объяснения достаточно ясны ... это сложно объяснить.

53
Andrew Joslin

Я только что прочитал довольно интересное объяснение различий между ними и растущего предпочтения присоединять модели к контроллеру и псевдоним контроллера для привязки моделей к представлению. http://toddmotto.com/digging-into-angulars-controller-as-syntax/ это статья. Он не упоминает об этом, но при определении директив, если вам нужно разделить что-то между несколькими директивами и не хотите, чтобы сервис (есть законные случаи, когда сервисы доставляют хлопоты), тогда присоедините данные к контроллеру родительской директивы. Служба $ scope предоставляет множество полезных вещей, наиболее очевидным является $ watch, но если все, что вам нужно для привязки данных к представлению, использование простого контроллера и «контроллер как» в шаблоне - это хорошо, и, возможно, предпочтительнее.

26
Derek

Я рекомендую вам прочитать следующий пост: AngularJS: "Controller as" или "$ scope"?

Он очень хорошо описывает преимущества использования «Controller as» для выставления переменных над «$ scope».

Я знаю, что вы спрашивали конкретно о методах, а не о переменных, но я думаю, что лучше придерживаться одной методики и придерживаться ее. 

Поэтому, на мой взгляд, из-за проблемы с переменными, обсуждавшейся в посте, лучше просто использовать технику «Контроллер как», а также применить ее к методам.

18
Liran Brimer

В этом курсе ( https://www.codeschool.com/courses/shaping-up-with-angular-js ) они объясняют, как использовать «это» и многие другие вещи.

Если вы добавляете метод к контроллеру с помощью метода «this», вы должны вызывать его в представлении с именем контроллера «dot» вашего свойства или метода.

Например, используя ваш контроллер в представлении, вы можете иметь такой код:

    <div data-ng-controller="YourController as aliasOfYourController">

       Your first pane is {{aliasOfYourController.panes[0]}}

    </div>
15
Sandro

Предыдущие версии Angular (до 1.0 RC) позволяли вам использовать это взаимозаменяемо с методом $ scope, но это больше не дело. Внутри методов, определенных в области видимости this и $ scope, являются взаимозаменяемы (angular устанавливает это в $ scope), но не иначе внутри вашего контроллера конструктора.

Чтобы вернуть это поведение (кто-нибудь знает, почему оно было изменено?), Вы можете добавить:

return angular.extend($scope, this);

в конце вашей функции контроллера (при условии, что $ scope был введен в эту функцию контроллера).

Это имеет приятный эффект, когда у вас есть доступ к родительской области через объект контроллера, который вы можете получить в child с помощью require: '^myParentDirective'

3
Kamil Szot

$ scope имеет другое «this», чем контроллер «this». Таким образом, если вы поместите console.log (this) внутри контроллера, он даст вам объект (controller), а this.addPane () добавляет метод addPane к объекту контроллера. Но $ scope имеет различную область видимости, и метод $ scope.methodName () .this.methodName() внутри контроллера должен принимать все методы в его области означает, что для добавления методов внутри объекта контроллера .$scope.functionName() находится в HTML и внутри 

$scope.functionName(){
    this.name="Name";
    //or
    $scope.myname="myname"//are same}

Вставьте этот код в ваш редактор и откройте консоль, чтобы увидеть ...

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=Edge">
    <title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
    <script>
        var app=angular.module("myApp",[]);
app.controller("ctrlExample",function($scope){
          console.log("ctrl 'this'",this);
          //this(object) of controller different then $scope
          $scope.firstName="Andy";
          $scope.lastName="Bot";
          this.nickName="ABot";
          this.controllerMethod=function(){

            console.log("controllerMethod ",this);
          }
          $scope.show=function(){
              console.log("$scope 'this",this);
              //this of $scope
              $scope.message="Welcome User";
          }

        });
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
       Comming From $SCOPE :{{firstName}}
       <br><br>
       Comming from $SCOPE:{{lastName}}
       <br><br>
       Should Come From Controller:{{nickName}}
       <p>
            Blank nickName is because nickName is attached to 
           'this' of controller.
       </p>

       <br><br>
       <button ng-click="controllerMethod()">Controller Method</button>

       <br><br>
       <button ng-click="show()">Show</button>
       <p>{{message}}</p>

   </div>

</body>
</html>
0
Aniket Jha