AngularJS : 컨트롤러간에 변수를 전달하는 방법은 무엇입니까?


326

두 개의 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. 그러나 내가 그렇게 전달하려고하면 ...

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

또한 실패합니다.

참고 : 이 컨트롤러는 서로 중첩되어 있지 않습니다.


여러 가지 방법이 있지만 가장 좋은 방법은 각도 시계입니다. 우리가 항상 프레임 워크를 사용할 때 작업을 위해 자신의 방법을 사용하는 가장 좋은 방법 은 이것을 잊지
pejman

나는이 블로그에게 매우 도움이되었다고 블로그
블랙 맘바에게

답변:


503

여러 컨트롤러에서 변수를 공유하는 한 가지 방법 은 서비스만들고 사용하려는 컨트롤러에 서비스 를 삽입하는 것입니다.

간단한 서비스 예 :

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에서)
    • 프리미티브 (문자열)에 바인딩
    • 개체 속성에 바인딩 (범위 변수에 저장)
  • 값이 업데이트 될 때 UI를 업데이트하는 공유 값에 바인딩 (myController2에서)
    • 기본 (문자열)을 반환하는 함수에 바인딩
    • 객체의 속성에 바인딩
    • 객체의 속성에 대한 양방향 바인딩

5
이 경우-sharedProperties.getProperty ()가 값을 변경할 때 Ctrl2의 범위가 어떻게 "알고"있습니까?
OpherV

5
속성이 변경 될 때마다 UI를 업데이트하려면 both함수로 변경할 수 있으며 각도 다이제스트 프로세스 중에 호출 / 재평가됩니다. 예제는 이 바이올린 을 참조하십시오 . 또한 객체의 속성에 바인딩하면 뷰에서 직접 사용할 수 있으며이 예제 와 비슷하게 데이터가 변경되면 업데이트됩니다 .
Gloopy

11
컨트롤러의 변경 사항을 감지하고 이에 대응하려면 옵션 getProperty()을 범위 에 추가 하고이 예 와 같이 $ scope. $ watch를 사용 하는 것이 옵션입니다 . 이 예제들이 도움이 되길 바랍니다!
Gloopy

1
여기에는 서비스가 무국적이어야하므로 문제가 있습니다. 서비스 내부의 속성을 저장하는 것은 잘못되었지만 편리합니다. $ cacheFactory를 사용하여 데이터를 읽고 쓰기 시작했습니다. Gloopy와 거의 동일한 서비스를 사용하지만 서비스에 상태를 저장하는 대신 이제 캐시에 있습니다. 먼저 캐시 서비스를 작성하십시오. angular.module ( 'CacheService', [ 'ng']) .factory ( 'CacheService', function ($ cacheFactory) {return $ cacheFactory ( 'CacheService');}); app.js에 포함시키고 서비스에 삽입하여 다음과 같이 사용하십시오. return CacheService.get (key); 또는 CacheService.put (key, value);
Jordan Papaleo

4
Angular 문서에 설명 된 .service대신 이 대답이 어떻게 어떻게 사용되는지 파악하려고했습니다 .factory. 문서가 다른 방법을 사용할 때 왜이 답변이 그렇게 높은 투표를합니까?
pspahn

44

나는 간단한 예제를 통해 간단한 것들을 설명하고 싶습니다 :)

다음은 매우 간단한 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
그래 나도 너와 같은 생각이야. 항상 일을 단순하게 만드십시오.
Evan Hu

var _dataObj = {};직접 참조를 반환 할 때 선언 할 때 의 요점은 무엇입니까 ? 그것은 사적인 것이 아닙니다 . 첫 번째 예에서는 할 수 this.dataObj = {};있고 두 번째 예 return { dataObj: {} };는 쓸모없는 변수 선언 IMHO입니다.
TJ

@TJ 요점은이 변수를 다른 컴포넌트들과 공유하는 것입니다. 공유 개념을 설명하는 기본 예입니다. 블록 내부의 변수 IS private은 공개 패턴을 ​​사용하여 공개 변수로 노출합니다. 이렇게하면 변수를 잡고 사용하는 것 사이에 책임이 분리됩니다.
Dmitri Zaitsev

@DmitriZaitsev는 "간단한 예"라고 말하지만 개인 상태를 사용하는 방법을 제대로 보여주지 않으면 사람들을 혼란스럽게합니다. 직접 참조를 반환하는 한 예제에는 개인 상태가 없습니다.
TJ

@ TJ 혼란스럽지 않습니다. 개인 변수는 모듈에 의해 노출 될 수 있습니다. 더 나은 답변을 자유롭게 작성하십시오.
Dmitri Zaitsev

26

--- 나는이 답변이이 질문에 대한 것이 아니라는 것을 알고 있지만,이 질문을 읽고 문제를 피하기 위해 공장과 같은 서비스를 처리하려는 사람들을 원합니다 ----

이를 위해서는 서비스 또는 팩토리를 사용해야합니다.

서비스는 중첩되지 않은 컨트롤러간에 데이터를 공유 하는 최선의 방법 입니다.

데이터 공유에 대한이 주제에 대한 아주 좋은 주석은 객체를 선언하는 방법입니다. 나는 그것에 대해 읽기 전에 AngularJS 함정에 빠졌기 때문에 운이 좋지 않았고 매우 좌절했습니다. 이 문제를 피할 수 있도록 도와 드리겠습니다.

나는 "ng-book : AngularJS에 관한 완전한 책"에서 읽은 데이터로 컨트롤러에서 생성 된 AngularJS ng 모델이 잘못되었다는 것을 읽었습니다!

$ 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 document)이 다음과 같이 호출을 포함하도록 권장 (BEST PRACTICE)되기 때문입니다.

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

자식 컨트롤러가 부모 컨트롤러에서 개체를 변경할 수있게하려면 중첩 컨트롤러에 매우 유용합니다.

그러나 귀하의 경우 중첩 범위를 원하지 않지만 서비스에서 컨트롤러로 객체를 가져 오는 것과 비슷한 측면이 있습니다.

서비스 'Factory'가 있고 리턴 공간에 objectC를 포함하는 objectB를 포함하는 objectA가 있다고 가정 해 봅시다.

컨트롤러에서 objectC를 범위로 가져 오려는 경우 실수입니다.

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

작동하지 않습니다 ... 대신 점 하나만 사용하십시오.

$scope.neededObjectInController = Factory.ObjectA;

그런 다음 DOM에서 objectA에서 objectC를 호출 할 수 있습니다. 이것은 공장과 관련된 모범 사례이며, 가장 중요한 것은 예기치 않은 캐치 불가능 오류를 피하는 데 도움이됩니다.


2
나는 이것이 좋은 대답이라고 생각하지만 소화하기는 꽤 어렵습니다.
pspahn

17

$ rootScope를 사용하여 서비스를 작성하지 않은 솔루션 :

앱 컨트롤러간에 속성을 공유하려면 Angular $ rootScope를 사용할 수 있습니다. 이것은 사람들이 정보를 알 수 있도록 데이터를 공유하는 또 다른 옵션입니다.

Controllers에서 일부 기능을 공유하는 선호되는 방법은 Services, $ rootscope를 사용할 수있는 전역 속성을 읽거나 변경하는 것입니다.

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>

5
이 시점에서 전역 범위 변수를 사용하여 다양한 구조 내에서 모든 것을 로컬로 범위 지정한다는 AngularJS 아이디어에서 벗어납니다. 전역 변수 파일을 추가하면 동일한 결과를 얻을 수 있으며 변수가 원래 정의 된 위치를 쉽게 찾을 수 있습니다. 어느 쪽이든, 제안되지 않았습니다.
Organiccat

4
@Organiccat-귀하의 우려를 이해하고 있으므로 이미 선호하는 방법이 서비스가 될 것이라고 이미 언급 한 이유는 의심의 여지가 없습니다. 그러나 나중에 각도 도이 방법을 제공합니다. 당신이 당신의 글로벌을 관리하는 방법에 당신에게 달려 있습니다. 이 접근법이 나에게 가장 적합한 시나리오가있었습니다.
Sanjeev

8

위의 샘플은 매력처럼 작동했습니다. 여러 값을 관리 해야하는 경우를 대비하여 수정했습니다. 이게 도움이 되길 바란다!

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

    var hashtable = {};

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

1
또한 서비스를 사용하여 다른 컨트롤러간에 데이터를 공유하는 샘플을 만들었습니다. 나는 당신이 그것을 좋아 바랍니다. jsfiddle.net/juazammo/du53553a/1
Juan Zamora

1
작동하지만 일반적으로의 구문입니다 .factory. A를 .service따라 "당신이 타입 / 클래스로 서비스를 정의하면"사용되어야한다 docs.angularjs.org/api/auto/service/$provide#service
드미트리 Zaitsev을

1
드미트리, 내 관점에서 그러나 각도 얘들 아, 맞아, 단지 .... 아 잘 있습니다 .... 내가 서비스 (외관)과 공장 사이에 있었다 개념을 비트 변경
후안 자모라

1
그리고 잘못된 것이면 서비스를 수정하여 객체 또는 값이 될 수있는 것을 반환하십시오. 팩토리는 객체를 생성하기위한 것입니다. 실제로 무언가를 돌려주는 기능들의 집합 인 facace는 내가 어디에서 서비스를 생각 했는가입니다. 공장에서 기능 호출 포함. 다시 말하지만, 이것이 실제로 Angular의 관점이 아닌 나를위한 기본 개념에 빠져 들었습니다. (Abstract Factory dofactory.com/net/abstract-factory-design-pattern ) 및 어댑터 접근 방식은 내가 서비스로 제공 할 것입니다
Juan Zamora

1
여기서 어댑터 패턴을 확인하십시오. dofactory.com/net/adapter-design-pattern
Juan Zamora

6

나는 이것이 왜 나쁜 생각인지 토론 할 수있는 값을 사용하는 경향이있다.

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; 

});

이것은 서비스 사용과 어떻게 다릅니 까?
dopatraman

5

다음 예에 도시 한 방법과 변수 통과 형제의 컨트롤러 값이 변경 될 때 동작을 수행.

사용 사례 : 사이드 바에 다른보기의 내용을 변경하는 필터가 있습니다.

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>


4

컨트롤러와 지시어 사이에서 데이터를 공유하는 권장 방법은 이미 지적했듯이 서비스 (공장)를 사용하는 것임을 지적 함으로써이 질문에 기여하고 싶습니다. 어떻게해야 하는가에 대한 실제적인 사례

작동하는 플 런커는 다음과 같습니다. 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>'
  }
}]);

이 실용적이고 깨끗한 답변이 누군가에게 도움이되기를 바랍니다.


3

서비스 나 공장에서 그렇게 할 수 있습니다. 몇 가지 핵심 차이점은 본질적으로 동일합니다. thinkster.io 에서이 설명이 가장 쉬운 것으로 나타났습니다 . 간단하고 요점까지 효과적입니다.


1
"서비스 나 공장에서 그렇게 할 수 있습니다" -How ..? 그것을하는 방법은 OP가 요구하는 것입니다 ... 외부 리소스에 연결하는 대신 스택 오버 플로우 자체에 완전한 답변을 게시하십시오. 링크가 초과 작동 할 수 있습니다.
TJ

2

속성을 범위 부모의 일부로 만들 수 없습니까?

$scope.$parent.property = somevalue;

나는 그것이 옳다고 말하지는 않지만 작동합니다.


3
저자는 NOTE: These controllers are not nested inside each other.. 이들이 동일한 부모를 공유하는 중첩 된 컨트롤러 또는 컨트롤러 인 경우 작동하지만 예상 할 수는 없습니다.
Chris Foster

2
$parent피할 수 있는지 에 의존하는 것은 일반적으로 나쁜 습관 입니다. 잘 설계된 재사용 가능한 구성 요소는 부모에 대해 알 수 없습니다.
Dmitri Zaitsev

2

아, 다른 대안 으로이 새로운 것들을 조금 가져보십시오. 로컬 스토리지이며 각도가 작동하는 곳에서 작동합니다. 천만에요. (하지만 정말로, 고마워)

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
이것은 다른 문제인 영구 저장소를 해결합니다. 이름 충돌의 취약성으로 로컬 스토리지 객체를 수정하는 등의 부작용으로 코드가 누출되는 문제의 확장 가능한 솔루션은 아닙니다.
Dmitri Zaitsev

1

이를 수행하는 두 가지 방법이 있습니다

1) get / set 서비스 이용

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

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

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


1
Ctrl2(리스너)가의 하위 컨트롤러 인 경우에만 작동합니다 Ctrl1. 형제 컨트롤러는를 통해 통신해야합니다 $rootScope.
herzbube

0

서비스를 원하지 않으면 다음과 같이하십시오.

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

37
이 답변이 작동하는 것은 의심의 여지가 없지만 AngularJS의 철학에 위배되어 모델 / 컨트롤러 코드에 DOM 객체를 절대로 사용하지 않는다는 점에 유의하고 싶습니다.
JoeCool

3
내 의견으로는 DOM을 통한 컨트롤러 통신이 좋지 않기 때문에 -1입니다.
Chris Foster

3
@ChrisFoster, 망치가 "도구"로 판매되었다고해서 그것이 종이 무게로 사용될 수 없다는 것을 의미하지는 않습니다. 모든 프레임 워크 나 툴에 대해 항상 "모범 사례"목록을 "구부리"어야하는 개발자를 찾을 수있을 것입니다.
Andrei V

5
@AndreiV-유추가 나쁘면 해머를 종이 무게로 사용하는 데 아무런 불리가 없습니다. 이와 같이 나쁜 연습을하면 분명한 단점이 있으며 스파게티 코드로 쉽게 이어질 수 있습니다. 위의 코드는 컨트롤러가 DOM의 위치에 따라 달라지기 때문에 취약하므로 테스트하기가 매우 어렵습니다. 서비스를 사용하는 것은 구현을 템플릿에 연결하지 않기 때문에 더 좋은 방법입니다. 개발자가 모범 사례 목록을 구부려 야하는 경우가 많지만보다 효과적이고 명확하고 일반적이며 모듈화 된 모범 사례가있는 경우에는 그렇지 않습니다.
Chris Foster

-1

$ rootScope 및 서비스 외에도 공유 데이터를 추가하기 위해 각도를 확장하는 깨끗하고 쉬운 대안 솔루션이 있습니다.

컨트롤러에서 :

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

이 속성은 범위와 분리 된 '각도'개체에 속하며 범위와 서비스에서 공유 할 수 있습니다.

객체를 주입 할 필요가 없다는 이점이 있습니다. 정의 후 즉시 어디서나 액세스 할 수 있습니다!


2
이것은 window객체 전체에 전역 변수를 갖는 것과 같습니다 ... 각도 를 오염 시키려면 왜 윈도우 객체를 오염시키지 않겠습니까 ?
TJ
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.