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

У меня есть два угловых контроллера:

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 потому что это не определено. Однако, если я попытаюсь передать это так...

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();

Также не удается.

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

15 ответов

Решение

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

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

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)
    • Привязка к функции, которая возвращает примитив (строку)
    • Привязка к свойству объекта
    • Двухсторонняя привязка к свойству объекта

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

Здесь очень просто 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


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

Также см. Ответ здесь для соответствующих комментариев лучших практик

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

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

Сервисы - ЛУЧШАЯ ПРАКТИКА для обмена данными между не вложенными контроллерами.

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

Из "ng-book: полная книга по AngularJS" я прочитал, что ng-модели AngularJS, созданные в контроллерах как голые данные, НЕПРАВИЛЬНЫ!

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

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-документ) содержать вызовы как

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

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

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

Допустим, у вас есть сервис "Фабрика", и в возвращаемом пространстве есть объект A, который содержит объект B, который содержит объект C.

Если из вашего контроллера вы хотите получить objectC в вашу область, было бы ошибкой сказать:

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

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

$scope.neededObjectInController = Factory.ObjectA;

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

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

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

Предпочтительный способ обмена некоторыми функциями между контроллерами - это службы, для чтения или изменения глобального свойства вы можете использовать $ rootcope.

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 в шаблоне (доступ к свойствам с помощью $root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>

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

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

    var hashtable = {};

    return {
        setValue: function (key, value) {
            hashtable[key] = value;
        },
        getValue: function (key) {
            return hashtable[key];
        }
    }
});

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

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; 

});

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

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

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>

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

Вот рабочий плункер: 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>'
  }
}]);

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

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

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

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

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

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 в свой контроллер.

Я просмотрел ответы выше, я рекомендую предложение Педжмана 29 декабря '16 в 13:31, но он / она не оставил полного ответа. Вот он, я поставлю это как -> (вам нужна служба и слушатель $watch на одной из областей от контроллеров для изменений в области обслуживания)

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

app.service('bridgeService', function () {
    var msg = ""; 
    return msg;
});
app.controller('CTRL_1'
, function ($scope, $http, bridgeService) 
{
    $http.get(_restApi, config)
    .success(
    function (serverdata, status, config) {
        $scope.scope1Box = bridgeService.msg = serverdata;
    });
});
app.controller('CTRL_2'
, function ($scope, $http, bridgeService) 
{
    $scope.$watch( function () {
        return (bridgeService.msg);
    }, function (newVal, oldVal) {
        $scope.scope2Box = newVal;
    }, true
    );
});

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

$scope.$parent.property = somevalue;

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

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

1) Воспользуйтесь сервисом get/set

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

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

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

var scope = angular.element("#another ctrl scope element id.").scope();
scope.plean_assign = some_value;

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

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

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

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

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

Другие вопросы по тегам