AngularJS 컨트롤러간에 데이터 공유


362

컨트롤러간에 데이터를 공유하려고합니다. 유스 케이스는 다단계 양식이며 한 입력에 입력 된 데이터는 나중에 원래 컨트롤러 외부의 여러 표시 위치에서 사용됩니다. 아래와 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;
});

도움을 주시면 감사하겠습니다.


답변:


481

간단한 해결책은 팩토리가 객체를 반환하고 컨트롤러가 동일한 객체에 대한 참조를 사용하도록하는 것입니다.

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/

응용 프로그램이 커지고 테스트가 더 복잡해지고 더 어려워지면 공장에서 전체 객체를 이런 식으로 노출하지 않고 대신 게터 및 세터를 통해 액세스를 제한 할 수 있습니다.

myApp.factory('Data', function () {

    var data = {
        FirstName: ''
    };

    return {
        getFirstName: function () {
            return data.FirstName;
        },
        setFirstName: function (firstName) {
            data.FirstName = firstName;
        }
    };
});

이 접근 방식을 사용하면 새로운 값으로 공장을 업데이트하고 변경 사항을 감시하는 것은 소비 컨트롤러에 달려 있습니다.

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
첫 번째 솔루션은 나에게 가장 효과적이었습니다. 서비스가 객체를 유지하고 컨트롤러가 참조로 처리하도록했습니다. 감사합니다.
johnkeese

5
. $ 범위 $ 시계 방법은 다른 영역에 결과를 하나 개의 범위에서 휴식 호출을 만들고 적용하기위한 아름답게 작동
aclave1

객체와 return { FirstName: '' };상호 작용하는 메소드를 포함하는 객체를 반환하거나 객체를 반환하는 대신 클래스 ( function) 의 인스턴스를 반환하는 것이 바람직하지 않으므로 객체를 사용할 때 특정 유형을 가지고 있어야합니다. 객체{}" ?
RPDeshaies

8
newValue !== oldValueoldValue와 newValue가 동일하면 Angular가 함수를 실행하지 않기 때문에 리스너 함수는 검사 가 필요 하지 않습니다. 이에 대한 유일한 예외는 초기 값에 관심이있는 경우입니다. 참조 : docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
Gautham C.

@tasseKATT, 페이지를 새로 고치지 않으면 제대로 작동합니다. 페이지를 새로 고치면 어떤 해결책을 제안 할 수 있습니까 ???
MaNn

71

나는 $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's update method
myApp.controller('SecondCtrl', function($scope, MyService){
   $scope.data = MyService.data;

   $scope.updateData = function(first, last) {
     MyService.update(first, last);
   }
});

명시 적으로 watch ()를 사용하지 않지만 내부적으로 AngularJS는 $ scope 변수의 새 값으로 뷰를 업데이트합니다. 성능면에서 동일합니까?
Mart Dominguez

4
두 방법 모두 성능이 확실하지 않습니다. 내가 이해 한 바에 따르면 Angular는 이미 범위에서 다이제스트 루프를 실행 중이므로 추가 시계 식을 설정하면 Angular에 더 많은 작업이 추가 될 수 있습니다. 실제로 확인하려면 성능 테스트를 실행해야합니다. 나는 단지 $ watch 표현을 통한 업데이트보다 위의 답변을 통한 데이터 전송 및 공유를 선호합니다.
bennick

2
이것은 $ watch의 대안이 아닙니다. $ watch를 사용하지 않고 두 개의 Angular 객체 (이 경우 컨트롤러)간에 데이터를 공유하는 다른 방법입니다.
bennick

1
IMO이 솔루션은 jQuery와 같은 느낌을주는 $ watch 문을 추가하는 대신 Angular의 선언적 접근 방식에 더 가깝기 때문에 가장 좋은 솔루션입니다.
JamesG

8
이것이 내가 가장 많이 투표 한 답변이 아닌 이유는 무엇입니까? 모든 $ 시계 후 코드가 더 복잡하게
Shyamal Parikh

11

컨트롤러간에 데이터를 공유 할 수있는 많은 방법이 있습니다

  1. 서비스 사용
  2. $ state.go 서비스 사용
  3. stateparams 사용
  4. 루트 스코프 사용

각 방법에 대한 설명 :

  1. 나는 누군가가 이미 설명 한대로 설명하지 않을 것입니다

  2. 사용 $state.go

      $state.go('book.name', {Name: 'XYZ'}); 
    
      // then get parameter out of URL
      $state.params.Name;
  3. $stateparam와 비슷한 방식으로 작동하며 $state.go송신자 컨트롤러에서 객체로 객체를 전달하고 stateparam을 사용하여 수신기 컨트롤러에서 수집합니다.

  4. 사용 $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);
      });

6

경로 경로 패턴 사이의 공유 범위를 제어하는 ​​팩토리를 만들었으므로 사용자가 동일한 경로 상위 경로를 탐색 할 때만 공유 데이터를 유지할 수 있습니다.

.controller('CadastroController', ['$scope', 'RouteSharedScope',
    function($scope, routeSharedScope) {
      var customerScope = routeSharedScope.scopeFor('/Customer');
      //var indexScope = routeSharedScope.scopeFor('/');
    }
 ])

따라서 사용자가 다른 경로 경로 (예 : '/ Support')로 이동하면 경로 '/ Customer'에 대한 공유 데이터가 자동으로 삭제됩니다. 그러나이 대신 사용자가 '/ Customer / 1'또는 '/ Customer / list'와 같은 'child'경로로 이동하면 범위가 손상되지 않습니다.

여기에서 샘플을 볼 수 있습니다 : http://plnkr.co/edit/OL8of9


$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){ ... })ui.router가있는 경우 사용
Nuno Silva

6

컨트롤러간에 데이터를 공유하는 방법에는 여러 가지가 있습니다

  • 각도 서비스
  • 브로드 캐스트, $ emit 방법
  • 부모-자식 컨트롤러 통신
  • $ rootscope

우리가 알고 있듯이 $rootscope데이터 전송 또는 통신에 바람직하지 않은 방법은 전체 응용 프로그램에서 사용할 수있는 전역 범위이므로

Angular Js 컨트롤러 간의 데이터 공유를 위해 Angular 서비스는 모범 사례입니다. .factory, .service
레퍼런스

아이 컨트롤러에 부모로부터의 데이터 전송의 경우 당신은을 통해 아이 컨트롤러에 직접 액세스 상위 데이터 수 있습니다 $scope
당신이 사용하는 경우 ui-router당신은 사용할 수있는 $stateParmasURL 매개 변수처럼 전달하는 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


4

가장 간단한 해결책 :

AngularJS 서비스를 사용했습니다 .

1 단계 : SharedDataService라는 AngularJS 서비스를 만들었습니다.

myApp.service('SharedDataService', function () {
     var Person = {
        name: ''

    };
    return Person;
});

2 단계 : 두 개의 컨트롤러를 생성하고 위에 생성 된 서비스를 사용하십시오.

//First Controller
myApp.controller("FirstCtrl", ['$scope', 'SharedDataService',
   function ($scope, SharedDataService) {
   $scope.Person = SharedDataService;
   }]);

//Second Controller
myApp.controller("SecondCtrl", ['$scope', 'SharedDataService',
   function ($scope, SharedDataService) {
   $scope.Person = SharedDataService;
   }]);

3 단계 : 보기에서 생성 된 컨트롤러를 사용하십시오.

<body ng-app="myApp">

<div ng-controller="FirstCtrl">
<input type="text" ng-model="Person.name">
<br>Input is : <strong>{{Person.name}}</strong>
</div>

<hr>

<div ng-controller="SecondCtrl">
Input should also be here: {{Person.name}}
</div>

</body>

이 문제에 대한 해결책을 보려면 아래 링크를 누르십시오

https://codepen.io/wins/pen/bmoYLr

.html 파일 :

<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>

<body ng-app="myApp">

  <div ng-controller="FirstCtrl">
    <input type="text" ng-model="Person.name">
    <br>Input is : <strong>{{Person.name}}</strong>
   </div>

<hr>

  <div ng-controller="SecondCtrl">
    Input should also be here: {{Person.name}}
  </div>

//Script starts from here

<script>

var myApp = angular.module("myApp",[]);
//create SharedDataService
myApp.service('SharedDataService', function () {
     var Person = {
        name: ''

    };
    return Person;
});

//First Controller
myApp.controller("FirstCtrl", ['$scope', 'SharedDataService',
    function ($scope, SharedDataService) {
    $scope.Person = SharedDataService;
    }]);

//Second Controller
myApp.controller("SecondCtrl", ['$scope', 'SharedDataService',
    function ($scope, SharedDataService) {
    $scope.Person = SharedDataService;
}]);

</script>


</body>
</html>

1
이것은 angularJ를 사용하는 새로운 사람들에게 매우 명확한 예입니다. 부모 / 자식 관계의 데이터를 어떻게 공유 / 전달합니까? FirstCtrl부모이며 SecondCtrl(자식)도 필요하다는 약속을 얻 습니까? 두 번의 API 호출을하고 싶지 않습니다. 감사.
Chris22

1

angular.copy를 사용하여 $ watch를 사용하지 않는 다른 방법이 있습니다.

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

1

여러 가지 방법이 있습니다.

  1. 이벤트-이미 잘 설명했습니다.

  2. UI 라우터-위에서 설명했습니다.

  3. 서비스-위에 표시된 업데이트 방법
  4. BAD- 변경 사항 감시
  5. 보다는 또 다른 부모 자식 접근 개의 발광의 브로드 캐스트 -

*

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

0

이 패턴을 어디에서 선택했는지 확실하지 않지만 컨트롤러간에 데이터를 공유하고 $ 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));

새 데이터를 가져 오는 Controller (Publisher)는 이와 같은 작업을 수행합니다.

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);

구독자라고하는이 새로운 데이터를 사용하는 컨트롤러는 다음과 같은 작업을 수행합니다.

sharedDataEventHub.onChangeData($scope, function(data) {
    vm.localData.Property1 = data.Property1;
    vm.localData.Property2 = data.Property2;
});

이것은 모든 시나리오에서 작동합니다. 따라서 기본 컨트롤러가 초기화되고 데이터를 가져 오면 changeData 메서드를 호출하여 해당 데이터의 모든 가입자에게 브로드 캐스트합니다. 이를 통해 컨트롤러의 커플 링이 줄어 듭니다.


컨트롤러 사이의 상태를 공유하는 '모델'을 사용하는 것은 주로 사용되는 옵션처럼 보입니다. 불행히도 이것은 자식 컨트롤러를 새로 고치는 시나리오에는 적용되지 않습니다. 서버에서 데이터를 '반입'하고 모델을 다시 작성하는 방법은 무엇입니까? 캐시 무효화를 어떻게 처리할까요?
Andrei

부모와 자식 컨트롤러의 아이디어는 약간 위험하며 서로 너무 많은 의존성을 유발한다고 생각합니다. 그러나 UI- 라우터와 함께 사용하면 특히 해당 데이터가 상태 구성을 통해 주입 된 경우 "자식"의 데이터를 쉽게 새로 고칠 수 있습니다. 캐시 무효화는 게시자가 데이터를 검색하고 호출 할 책임이있는 게시자에서 발생합니다.sharedDataEventHub.changeData(newData)
ewahner

오해가 있다고 생각합니다. 새로 고침으로 말하면 사용자가 문자 그대로 키보드에서 F5 키를 누르고 브라우저가 다시 게시하도록합니다. 이런 일이 발생할 때 자식 / 세부 사항보기에있는 경우 "var someData = yourDataService.getSomeData ()"를 호출 할 사람이 없습니다. 문제는이 시나리오를 어떻게 다루겠습니까? 누가 모델을 업데이트해야합니까?
Andrei

컨트롤러를 활성화하는 방법에 따라 매우 간단 할 수 있습니다. 활성화하는 메소드가있는 경우 처음에 데이터로드를 담당하는 컨트롤러는로드 한 다음 sharedDataEventHub.changeData(newData)해당 데이터에 의존하는 모든 하위 또는 컨트롤러를 컨트롤러 본문에 다음과 같이 sharedDataEventHub.onChangeData($scope, function(obj) { vm.myObject = obj.data; });사용합니다. UI를 사용하는 경우 -라우터 이것은 상태 설정에서 주입 될 수 있습니다.
ewahner

다음은 내가 설명하려는 문제를 설명하는 예입니다. plnkr.co/edit/OcI30YX5Jx4oHyxHEkPx?p=preview . 플 런크를 꺼내면 편집 페이지로 이동하여 브라우저를 새로 고치면 작동이 중단됩니다. 이 경우 누가 모델을 다시 만들어야합니까?
Andrei

-3

간단하게하십시오 (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>


5
모듈식이 아니기 때문에 이것이 다운 투표 된 것 같습니다. dummy$ scope 또는 controllerA와의 상속 또는 서비스 사용을 통해보다 모듈 방식으로 공유되는 것이 아니라 두 컨트롤러 모두에 대해 전역 적입니다.
채드 존슨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.