AngularJS: Как я могу передавать переменные между контроллерами?

327

У меня есть два контроллера Angular:

function Ctrl1($scope) {
    $scope.prop1 = "First";
}

function Ctrl2($scope) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

Я не могу использовать Ctrl1 внутри Ctrl2, потому что это undefined. Однако, если я попытаюсь передать его так...

function Ctrl2($scope, Ctrl1) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

Я получаю сообщение об ошибке. Кто-нибудь знает, как это сделать?

Выполнение

Ctrl2.prototype = new Ctrl1();

Также терпит неудачу.

ПРИМЕЧАНИЕ. Эти контроллеры не вложены внутри друг друга.

  • 0
    Есть много способов, но лучший способ - это угловые часы. Всегда, когда мы используем фреймворк, это лучший способ использовать ее собственные методы для работы, не забывайте об этом
  • 0
    Я нашел этот блог очень полезным Блог
Теги:
angularjs-controller

15 ответов

511
Лучший ответ

Один из способов обмена переменными через несколько контроллеров - создать сервис и ввести его на любом контроллере, где вы хотите его использовать.

Пример простого обслуживания:

angular.module('myApp', [])
    .service('sharedProperties', function () {
        var property = 'First';

        return {
            getProperty: function () {
                return property;
            },
            setProperty: function(value) {
                property = value;
            }
        };
    });

Использование службы в контроллере:

function Ctrl2($scope, sharedProperties) {
    $scope.prop2 = "Second";
    $scope.both = sharedProperties.getProperty() + $scope.prop2;
}

Это очень хорошо описано в этом блоге (Урок 2 и в частности).

Я обнаружил, что если вы хотите привязываться к этим свойствам на нескольких контроллерах, он работает лучше, если вы привязываетесь к свойству объекта вместо примитивного типа (логическое, строка, число) для сохранения связанной ссылки.

Пример: var property = { Property1: 'First' }; вместо var property = 'First';.


ОБНОВЛЕНИЕ: Чтобы (надеюсь) сделать вещи более ясными вот скрипка, которая показывает пример:

  • привязка к статическим копиям общего значения (в myController1)
    • Связывание с примитивным (строка)
    • Связывание с объектом (сохранено в переменной области)
  • привязка к общим значениям, которые обновляют пользовательский интерфейс при обновлении значений (в myController2)
    • Связывание с функцией, возвращающей примитив (строка)
    • Привязка к свойству объекта
    • Двусторонняя привязка к свойству объекта
  • 5
    В этом случае - как область действия Ctrl2 «узнает», когда sharedProperties.getProperty () изменяет значение?
  • 5
    Если вы хотите, чтобы ваш пользовательский интерфейс обновлялся каждый раз при изменении свойства, вы можете изменить both функцию, и она будет вызываться / переоцениваться во время процесса углового дайджеста. Смотрите эту скрипку для примера. Также, если вы связываетесь со свойством объекта, вы можете использовать его непосредственно в своем представлении, и оно будет обновляться по мере изменения данных, как в этом примере .
Показать ещё 15 комментариев
43

Мне нравится иллюстрировать простые вещи на простых примерах:)

Вот очень простой пример Service:


angular.module('toDo',[])

.service('dataService', function() {

  // private variable
  var _dataObj = {};

  // public API
  this.dataObj = _dataObj;
})

.controller('One', function($scope, dataService) {
  $scope.data = dataService.dataObj;
})

.controller('Two', function($scope, dataService) {
  $scope.data = dataService.dataObj;
});

И здесь jsbin

И вот очень простой пример Factory:


angular.module('toDo',[])

.factory('dataService', function() {

  // private variable
  var _dataObj = {};

  // public API
  return {
    dataObj: _dataObj
  };
})

.controller('One', function($scope, dataService) {
  $scope.data = dataService.dataObj;
})

.controller('Two', function($scope, dataService) {
  $scope.data = dataService.dataObj;
});

И здесь jsbin


Если это слишком просто, вот более сложный пример

Также см. ответ здесь для комментариев соответствующих рекомендаций

  • 1
    Да, я согласен с вами. Всегда старайтесь сделать вещи простыми.
  • 0
    Какой смысл декларировать var _dataObj = {}; когда вы возвращаете прямую ссылку на это ..? Это не личное . В первом примере вы можете сделать this.dataObj = {}; и во втором return { dataObj: {} }; это бесполезное объявление переменных IMHO.
Показать ещё 5 комментариев
25

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

Для этого вам нужно будет использовать Сервис или Factory.

Услуги - это BEST PRACTICE для обмена данными между не вложенными контроллерами.

Очень хорошая аннотация на эту тему о совместном использовании данных - это объявление объектов. Мне не повезло, потому что я упал в ловушку AngularJS, прежде чем прочитал об этом, и я был очень расстроен. Поэтому позвольте мне помочь вам избежать этой проблемы.

Я прочитал из "ng-book: Полная книга по AngularJS", что AnggleJS ng-модели, созданные в контроллерах как голые данные, WRONG!

Элемент области $A должен быть создан следующим образом:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // best practice, always use a model
  $scope.someModel = {
    someValue: 'hello computer'
  });

И не так:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // anti-pattern, bare value
  $scope.someBareValue = 'hello computer';
  };
});

Это потому, что для DOM (html document) рекомендуется использовать (BEST PRACTICE), чтобы содержать вызовы как

<div ng-model="someModel.someValue"></div>  //NOTICE THE DOT.

Это очень полезно для вложенных контроллеров, если вы хотите, чтобы ваш дочерний контроллер мог изменять объект с родительского контроллера....

Но в вашем случае вы не хотите вложенных областей, но есть аналогичный аспект для получения объектов от служб к контроллерам.

Предположим, что у вас есть ваша служба "Factory", а в обратном пространстве есть объект A, содержащий объект B, содержащий объект C.

Если с вашего контроллера вы хотите ПОЛУЧИТЬ objectC в свою область действия, это ошибка, чтобы сказать:

$scope.neededObjectInController = Factory.objectA.objectB.objectC;

Это не работает... Вместо этого используйте только одну точку.

$scope.neededObjectInController = Factory.ObjectA;

Затем в DOM вы можете вызывать objectC из объекта A. Это наилучшая практика, связанная с заводами, и самое главное, , это поможет избежать неожиданных и неустранимых ошибок.

  • 2
    Я думаю, что это хороший ответ, но его довольно сложно переварить.
14

Решение без создания Сервиса с использованием $rootScope:

Чтобы совместно использовать свойства в приложениях Controllers, вы можете использовать Angular $rootScope. Это еще один вариант для обмена данными, поэтому, чтобы люди знали об этом.

Предпочтительным способом обмена некоторыми функциями в контроллерах является "Сервис", чтобы читать или изменять глобальную собственность, вы можете использовать $anchcope.

var app = angular.module('mymodule',[]);
app.controller('Ctrl1', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = true;
}]);

app.controller('Ctrl2', ['$scope','$rootScope',
  function($scope, $rootScope) {
    $rootScope.showBanner = false;
}]);

Использование $rootScope в шаблоне (свойства Access с $root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>
  • 5
    В этот момент вы используете глобальные переменные, которые отличаются от идеи AngularJS о локальной области видимости всего внутри ее различных структур. Добавление файла глобальной переменной позволит добиться того же результата и упростит поиск места, где переменная была первоначально определена. В любом случае, не предлагается.
  • 4
    @ Organiccat - я понимаю вашу озабоченность, и поэтому я уже упоминал, что предпочтительным способом будут услуги, без сомнения в этом. Но у меня есть и угловатый способ. Это зависит от вас, как вы хотите управлять своими глобальными. У меня был сценарий, где этот подход работал лучше всего для меня.
7

Образец выше работал как шарм. Я просто сделал модификацию на случай, если мне нужно управлять несколькими значениями. Надеюсь, это поможет!

app.service('sharedProperties', function () {

    var hashtable = {};

    return {
        setValue: function (key, value) {
            hashtable[key] = value;
        },
        getValue: function (key) {
            return hashtable[key];
        }
    }
});
  • 0
    Я также создал образец, используя сервис для обмена данными между различными контроллерами. Надеюсь, вам это нравится. jsfiddle.net/juazammo/du53553a/1
  • 1
    Хотя это работает, обычно это синтаксис для .factory . .service следует использовать, «если вы определяете службу как тип / класс» в соответствии с docs.angularjs.org/api/auto/service/$provide#service.
Показать ещё 4 комментария
5

Я склонен использовать ценности, довольные тем, кто может обсудить, почему это плохая идея.

var myApp = angular.module('myApp', []);

myApp.value('sharedProperties', {}); //set to empty object - 

Затем введите значение в соответствии с сервисом.

Установить в ctrl1:

myApp.controller('ctrl1', function DemoController(sharedProperties) {
  sharedProperties.carModel = "Galaxy";
  sharedProperties.carMake = "Ford";
});

и доступ из ctrl2:

myApp.controller('ctrl2', function DemoController(sharedProperties) {
  this.car = sharedProperties.carModel + sharedProperties.carMake; 

});
  • 0
    чем это отличается от использования сервиса?
  • 0
    Это на самом деле не так, но проще кодировать таким образом
3

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

Пример использования примера: у вас есть фильтр на боковой панели, который изменяет содержимое другого вида.

angular.module('myApp', [])

  .factory('MyService', function() {

    // private
    var value = 0;

    // public
    return {
      
      getValue: function() {
        return value;
      },
      
      setValue: function(val) {
        value = val;
      }
      
    };
  })
  
  .controller('Ctrl1', function($scope, $rootScope, MyService) {

    $scope.update = function() {
      MyService.setValue($scope.value);
      $rootScope.$broadcast('increment-value-event');
    };
  })
  
  .controller('Ctrl2', function($scope, MyService) {

    $scope.value = MyService.getValue();

    $scope.$on('increment-value-event', function() {    
      $scope.value = MyService.getValue();
    });
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp">
  
  <h3>Controller 1 Scope</h3>
  <div ng-controller="Ctrl1">
    <input type="text" ng-model="value"/>
    <button ng-click="update()">Update</button>
  </div>
  
  <hr>
  
  <h3>Controller 2 Scope</h3>
  <div ng-controller="Ctrl2">
    Value: {{ value }}
  </div>  

</div>
3

Я хотел бы внести свой вклад в этот вопрос, указав, что рекомендуемый способ обмена данными между контроллерами и даже директивами - это использование сервисов (фабрик), как уже указывалось, но также я хотел бы чтобы представить практический пример того, как это сделать.

Вот рабочий плункер: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info

Сначала создайте службу , которая будет иметь ваши общие данные:

app.factory('SharedService', function() {
  return {
    sharedObject: {
      value: '',
      value2: ''
    }
  };
});

Затем просто добавьте его на свои контроллеры и возьмите общие данные в своей области:

app.controller('FirstCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.controller('SecondCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.controller('MainCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

Вы также можете сделать это для своих директив, он работает одинаково:

app.directive('myDirective',['SharedService', function(SharedService){
  return{
    restrict: 'E',
    link: function(scope){
      scope.model = SharedService.sharedObject;
    },
    template: '<div><input type="text" ng-model="model.value"/></div>'
  }
}]);

Надеюсь, что этот практичный и чистый ответ может быть полезен кому-то.

2

А, у меня есть немного нового материала в качестве другой альтернативы. Это localstorage, и работает там, где работает angular. Пожалуйста. (Но на самом деле, спасибо парню)

https://github.com/gsklee/ngStorage

Определите свои значения по умолчанию:

$scope.$storage = $localStorage.$default({
    prop1: 'First',
    prop2: 'Second'
});

Доступ к значениям:

$scope.prop1 = $localStorage.prop1;
$scope.prop2 = $localStorage.prop2;

Сохраните значения

$localStorage.prop1 = $scope.prop1;
$localStorage.prop2 = $scope.prop2;

Не забудьте ввести ngStorage в ваше приложение и $localStorage в вашем контроллере.

  • 1
    Это решает другую проблему - постоянное хранение. Это не масштабируемое решение для рассматриваемой проблемы, поскольку оно приводит к утечке кода из-за побочных эффектов, таких как изменение объекта локального хранилища с уязвимостью конфликта имен среди других.
2

Вы можете сделать это с помощью сервисов или заводов. Они по существу одинаковы для некоторых основных различий. Я нашел это объяснение на thinkster.io, чтобы это было проще всего следовать. Простой, точный и эффективный.

  • 0
    «Вы могли бы сделать это с помощью служб или заводов» - как ..? Как это сделать - это то, о чем просит ОП ... пожалуйста, отправляйте полный ответ в самом стеке, вместо того, чтобы ссылаться на внешние ресурсы, ссылки могут перестать работать со временем.
2

Не могли бы вы также сделать свойство частью родительского объекта?

$scope.$parent.property = somevalue;

Я не говорю это правильно, но он работает.

  • 3
    Автор заявил, что NOTE: These controllers are not nested inside each other. , Если бы это были вложенные контроллеры или контроллеры, которые совместно использовали одного и того же родителя, это работало бы, но мы не можем этого ожидать.
  • 2
    Обычно плохая практика полагаться на $parent если этого можно избежать. Хорошо разработанный многократно используемый компонент не должен знать о своих родителях.
1

Второй подход:

angular.module('myApp', [])
  .controller('Ctrl1', ['$scope',
    function($scope) {

    $scope.prop1 = "First";

    $scope.clickFunction = function() {
      $scope.$broadcast('update_Ctrl2_controller', $scope.prop1);
    };
   }
])
.controller('Ctrl2', ['$scope',
    function($scope) {
      $scope.prop2 = "Second";

        $scope.$on("update_Ctrl2_controller", function(event, prop) {
        $scope.prop = prop;

        $scope.both = prop + $scope.prop2; 
    });
  }
])

Html:

<div ng-controller="Ctrl2">
  <p>{{both}}</p>
</div>

<button ng-click="clickFunction()">Click</button>

Подробнее см. plunker:

http://plnkr.co/edit/cKVsPcfs1A1Wwlud2jtO?p=preview

  • 0
    Работает, только если Ctrl2 (слушатель) является дочерним контроллером Ctrl1 . Контроллеры- $rootScope должны общаться через $rootScope .
1

Есть два способа сделать это

1) Используйте услугу get/set

2) $scope.$emit('key', {data: value}); //to set the value

 $rootScope.$on('key', function (event, data) {}); // to get the value
0

Если вы не хотите делать услугу, вы можете сделать это.

var scope = angular.element("#another ctrl scope element id.").scope();
scope.plean_assign = some_value;
  • 37
    Я не сомневаюсь, что этот ответ работает, но я хочу отметить, что это противоречит философии AngularJS, согласно которой в коде модели / контроллера никогда не должно быть объектов DOM.
  • 3
    -1 потому что связь контроллера через DOM - плохая практика, на мой взгляд.
Показать ещё 2 комментария
-2

Помимо $rootScope и сервисов, существует простое и простое альтернативное решение для расширения angular для добавления общих данных:

в контроллерах:

angular.sharedProperties = angular.sharedProperties 
    || angular.extend(the-properties-objects);

Эти свойства относятся к объекту 'angular', отделенному от областей, и могут использоваться совместно с областями и службами.

1 преимущество, которое вам не нужно вводить в объект: они доступны в любом месте сразу после вашей защиты!

  • 2
    Это похоже на наличие глобальных переменных по всему объекту window ... Если вы собираетесь загрязнять angular, почему бы просто не пойти дальше и не загрязнить объект окна ...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню