Обмен данными между контроллерами AngularJS
Я пытаюсь обмениваться данными между контроллерами. Вариант использования - многоступенчатая форма, данные, введенные на одном входе, позже используются в нескольких местах отображения вне исходного контроллера. Код ниже и в jsfiddle здесь.
HTML
<div ng-controller="FirstCtrl">
<input type="text" ng-model="FirstName"><!-- Input entered here -->
<br>Input is : <strong>{{FirstName}}</strong><!-- Successfully updates here -->
</div>
<hr>
<div ng-controller="SecondCtrl">
Input should also be here: {{FirstName}}<!-- How do I automatically updated it here? -->
</div>
JS
// declare the app with no dependencies
var myApp = angular.module('myApp', []);
// make a factory to share data between controllers
myApp.factory('Data', function(){
// I know this doesn't work, but what will?
var FirstName = '';
return FirstName;
});
// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){
});
// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
$scope.FirstName = Data.FirstName;
});
Любая помощь очень ценится.
Ответы
Ответ 1
Простое решение - вернуть ваш factory объект и позволить вашим контроллерам работать со ссылкой на тот же объект:
JS:
// declare the app with no dependencies
var myApp = angular.module('myApp', []);
// Create the factory that share the Fact
myApp.factory('Fact', function(){
return { Field: '' };
});
// Two controllers sharing an object that has a string in it
myApp.controller('FirstCtrl', function( $scope, Fact ){
$scope.Alpha = Fact;
});
myApp.controller('SecondCtrl', function( $scope, Fact ){
$scope.Beta = Fact;
});
HTML:
<div ng-controller="FirstCtrl">
<input type="text" ng-model="Alpha.Field">
First {{Alpha.Field}}
</div>
<div ng-controller="SecondCtrl">
<input type="text" ng-model="Beta.Field">
Second {{Beta.Field}}
</div>
Демо: http://jsfiddle.net/HEdJF/
Когда приложения становятся больше, сложнее и сложнее тестировать, вы, возможно, не захотите выставлять весь объект из factory таким образом, но вместо этого предоставляете ограниченный доступ, например, через getters и seters:
myApp.factory('Data', function () {
var data = {
FirstName: ''
};
return {
getFirstName: function () {
return data.FirstName;
},
setFirstName: function (firstName) {
data.FirstName = firstName;
}
};
});
При таком подходе контроллеры-потребители должны обновлять factory новыми значениями и следить за изменениями для их получения:
myApp.controller('FirstCtrl', function ($scope, Data) {
$scope.firstName = '';
$scope.$watch('firstName', function (newValue, oldValue) {
if (newValue !== oldValue) Data.setFirstName(newValue);
});
});
myApp.controller('SecondCtrl', function ($scope, Data) {
$scope.$watch(function () { return Data.getFirstName(); }, function (newValue, oldValue) {
if (newValue !== oldValue) $scope.firstName = newValue;
});
});
HTML:
<div ng-controller="FirstCtrl">
<input type="text" ng-model="FirstName">
<br>Input is : <strong>{{FirstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
Input should also be here: {{FirstName}}
</div>
Демо: http://jsfiddle.net/27mk1n1o/
Ответ 2
Я предпочитаю не использовать $watch
для этого. Вместо того, чтобы назначать всю службу для области контроллера, вы можете назначить только данные.
JS:
var myApp = angular.module('myApp', []);
myApp.factory('MyService', function(){
return {
data: {
firstName: '',
lastName: ''
}
// Other methods or objects can go here
};
});
myApp.controller('FirstCtrl', function($scope, MyService){
$scope.data = MyService.data;
});
myApp.controller('SecondCtrl', function($scope, MyService){
$scope.data = MyService.data;
});
HTML:
<div ng-controller="FirstCtrl">
<input type="text" ng-model="data.firstName">
<br>Input is : <strong>{{data.firstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
Input should also be here: {{data.firstName}}
</div>
В качестве альтернативы вы можете обновить служебные данные прямым методом.
JS:
// A new factory with an update method
myApp.factory('MyService', function(){
return {
data: {
firstName: '',
lastName: ''
},
update: function(first, last) {
// Improve this method as needed
this.data.firstName = first;
this.data.lastName = last;
}
};
});
// Your controller can use the service update method
myApp.controller('SecondCtrl', function($scope, MyService){
$scope.data = MyService.data;
$scope.updateData = function(first, last) {
MyService.update(first, last);
}
});
Ответ 3
Существует множество способов совместного использования данных между контроллерами
- Использование служб
- используя службы $state.go
- с использованием stateparams
- Использование корнеплодов
Объяснение каждого метода:
-
Я не буду объяснять, как его уже объяснил кто-то
-
с помощью $state.go
$state.go('book.name', {Name: 'XYZ'});
// then get parameter out of URL
$state.params.Name;
-
$stateparam
работает аналогично $state.go
, вы передаете его как объект из контроллера отправителя и собираете в контроллере получателя с помощью stateparam
-
с помощью $rootscope
(a) отправка данных с дочернего на родительский контроллер
$scope.Save(Obj,function(data) {
$scope.$emit('savedata',data);
//pass the data as the second parameter
});
$scope.$on('savedata',function(event,data) {
//receive the data as second parameter
});
(b) отправка данных из родительского контроллера в дочерний контроллер
$scope.SaveDB(Obj,function(data){
$scope.$broadcast('savedata',data);
});
$scope.SaveDB(Obj,function(data){`enter code here`
$rootScope.$broadcast('saveCallback',data);
});
Ответ 4
Я создал factory, который управляет разделяемой областью между шаблоном маршрута маршрута, поэтому вы можете поддерживать общие данные только тогда, когда пользователи перемещаются по тому же пути родительского пути.
.controller('CadastroController', ['$scope', 'RouteSharedScope',
function($scope, routeSharedScope) {
var customerScope = routeSharedScope.scopeFor('/Customer');
//var indexScope = routeSharedScope.scopeFor('/');
}
])
Итак, если пользователь переходит на другой маршрут маршрута, например '/Support', общие данные для пути '/Customer' будут автоматически уничтожены. Но если вместо этого пользователь переходит к "дочерним" путям, например "/Клиент/1" или "Клиент/список", область не будет уничтожена.
Здесь вы можете увидеть образец: http://plnkr.co/edit/OL8of9
Ответ 5
Существует несколько способов обмена данными между контроллерами
- Angular услуги
- $broadcast, метод $emit
- Передача родительского контроллера.
- $rootscope
Как известно, $rootscope
не является предпочтительным способом передачи данных или связи, поскольку это глобальная область, доступная для всего приложения
Для обмена данными между Angular Js-контроллерами Angular являются лучшими практиками, например. .factory
, .service
Для справки
В случае передачи данных от родителя к дочернему контроллеру вы можете напрямую обращаться к родительским данным в дочернем контроллере через $scope
Если вы используете ui-router
, вы можете использовать $stateParmas
для передачи параметров URL, таких как id
, name
, key
и т.д.
$broadcast
также является хорошим способом передачи данных между контроллерами от родителя к дочерней и $emit
для передачи данных от дочерних к родительским контроллерам.
HTML
<div ng-controller="FirstCtrl">
<input type="text" ng-model="FirstName">
<br>Input is : <strong>{{FirstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
Input should also be here: {{FirstName}}
</div>
JS
myApp.controller('FirstCtrl', function( $rootScope, Data ){
$rootScope.$broadcast('myData', {'FirstName': 'Peter'})
});
myApp.controller('SecondCtrl', function( $rootScope, Data ){
$rootScope.$on('myData', function(event, data) {
$scope.FirstName = data;
console.log(data); // Check in console how data is coming
});
});
Обратитесь к ссылке, чтобы узнать больше о $broadcast
Ответ 6
Существует другой способ без использования $watch, используя angular.copy:
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){
var service = {
FirstName: '',
setFirstName: function(name) {
// this is the trick to sync the data
// so no need for a $watch function
// call this from anywhere when you need to update FirstName
angular.copy(name, service.FirstName);
}
};
return service;
});
// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){
});
// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
$scope.FirstName = Data.FirstName;
});
Ответ 7
Существует несколько способов сделать это.
*
<superhero flight speed strength> Superman is here! </superhero>
<superhero speed> Flash is here! </superhero>
*
app.directive('superhero', function(){
return {
restrict: 'E',
scope:{}, // IMPORTANT - to make the scope isolated else we will pollute it in case of a multiple components.
controller: function($scope){
$scope.abilities = [];
this.addStrength = function(){
$scope.abilities.push("strength");
}
this.addSpeed = function(){
$scope.abilities.push("speed");
}
this.addFlight = function(){
$scope.abilities.push("flight");
}
},
link: function(scope, element, attrs){
element.addClass('button');
element.on('mouseenter', function(){
console.log(scope.abilities);
})
}
}
});
app.directive('strength', function(){
return{
require:'superhero',
link: function(scope, element, attrs, superHeroCtrl){
superHeroCtrl.addStrength();
}
}
});
app.directive('speed', function(){
return{
require:'superhero',
link: function(scope, element, attrs, superHeroCtrl){
superHeroCtrl.addSpeed();
}
}
});
app.directive('flight', function(){
return{
require:'superhero',
link: function(scope, element, attrs, superHeroCtrl){
superHeroCtrl.addFlight();
}
}
});
Ответ 8
Не знаю, где я взял этот шаблон, но для обмена данными между контроллерами и сокращения $rootScope и $scope это отлично работает. Это напоминает репликацию данных, где у вас есть издатели и подписчики. Надеюсь, что это поможет.
Сервис:
(function(app) {
"use strict";
app.factory("sharedDataEventHub", sharedDataEventHub);
sharedDataEventHub.$inject = ["$rootScope"];
function sharedDataEventHub($rootScope) {
var DATA_CHANGE = "DATA_CHANGE_EVENT";
var service = {
changeData: changeData,
onChangeData: onChangeData
};
return service;
function changeData(obj) {
$rootScope.$broadcast(DATA_CHANGE, obj);
}
function onChangeData($scope, handler) {
$scope.$on(DATA_CHANGE, function(event, obj) {
handler(obj);
});
}
}
}(app));
Контроллер, который получает новые данные, который является издателем, сделает что-то вроде этого.
var someData = yourDataService.getSomeData();
sharedDataEventHub.changeData(someData);
Контроллер, который также использует эти новые данные, который называется Абонент, сделает что-то вроде этого...
sharedDataEventHub.onChangeData($scope, function(data) {
vm.localData.Property1 = data.Property1;
vm.localData.Property2 = data.Property2;
});
Это будет работать для любого сценария. Поэтому, когда первичный контроллер инициализируется и он получает данные, он будет вызывать метод changeData, который затем передавал бы это всем подписчикам этих данных. Это уменьшает сцепление наших контроллеров друг с другом.
Ответ 9
Просто сделайте это просто (протестировано с v1.3.15):
<article ng-controller="ctrl1 as c1">
<label>Change name here:</label>
<input ng-model="c1.sData.name" />
<h1>Control 1: {{c1.sData.name}}, {{c1.sData.age}}</h1>
</article>
<article ng-controller="ctrl2 as c2">
<label>Change age here:</label>
<input ng-model="c2.sData.age" />
<h1>Control 2: {{c2.sData.name}}, {{c2.sData.age}}</h1>
</article>
<script>
var app = angular.module("MyApp", []);
var dummy = {name: "Joe", age: 25};
app.controller("ctrl1", function () {
this.sData = dummy;
});
app.controller("ctrl2", function () {
this.sData = dummy;
});
</script>