web-dev-qa-db-fra.com

Partage de données entre contrôleurs AngularJS

J'essaie de partager des données entre contrôleurs. Le cas d'utilisation est un formulaire en plusieurs étapes. Les données saisies dans une entrée sont ensuite utilisées dans plusieurs emplacements d'affichage en dehors du contrôleur d'origine. Code ci-dessous et en jsfiddle ici .

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

Toute aide est grandement appréciée.

353
johnkeese

Une solution simple consiste à demander à votre usine de retourner un objet et à vos contrôleurs de travailler avec une référence au même objet:

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>

Démo: http://jsfiddle.net/HEdJF/

Lorsque les applications deviennent plus volumineuses, plus complexes et plus difficiles à tester, vous ne souhaitez peut-être pas exposer l'intégralité de l'objet de cette manière, mais vous donnez plutôt un accès limité, par exemple, via des accesseurs et des contrôleurs:

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

    var data = {
        FirstName: ''
    };

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

Avec cette approche, il incombe aux contrôleurs consommateurs de mettre à jour l’usine avec de nouvelles valeurs et d’observer les modifications pour les obtenir:

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>

Démo: http://jsfiddle.net/27mk1n1o/

470
tasseKATT

Je préfère ne pas utiliser $watch pour cela. Au lieu d'attribuer l'intégralité du service à la portée d'un contrôleur, vous pouvez affecter uniquement les données.

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>

Vous pouvez également mettre à jour les données de service avec une méthode directe.

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

Vous pouvez partager les données entre contrôleurs de plusieurs manières.

  1. en utilisant des services
  2. en utilisant les services $ state.go
  3. en utilisant stateparam
  4. en utilisant rootscope

Explication de chaque méthode:

  1. Je ne vais pas expliquer comme c'est déjà expliqué par quelqu'un

  2. en utilisant $state.go

      $state.go('book.name', {Name: 'XYZ'}); 
    
      // then get parameter out of URL
      $state.params.Name;
    
  3. $stateparam fonctionne de manière similaire à $state.go, vous le transmettez sous forme d'objet à partir du contrôleur émetteur et le récupérez dans le contrôleur récepteur à l'aide de stateparam.

  4. en utilisant $rootscope

    (a) envoi de données du contrôleur enfant au contrôleur parent

      $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) envoi de données du contrôleur parent au contrôleur enfant

      $scope.SaveDB(Obj,function(data){
          $scope.$broadcast('savedata',data);
      });
    
      $scope.SaveDB(Obj,function(data){`enter code here`
          $rootScope.$broadcast('saveCallback',data);
      });
    
10
Ayush Mishra

J'ai créé une fabrique qui contrôle l'étendue partagée entre les modèles du chemin d'accès afin que vous puissiez conserver les données partagées au moment même où les utilisateurs naviguent dans le même chemin d'accès parent.

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

Ainsi, si l'utilisateur se connecte à un autre chemin, par exemple "/ Support", les données partagées pour le chemin "/ Client" seront automatiquement détruites. Mais si, au lieu de cela, l'utilisateur choisit des chemins "enfants", tels que "/ Client/1" ou "/ Client/liste", la portée ne sera pas détruite.

Vous pouvez voir un exemple ici: http://plnkr.co/edit/OL8of9

6
Oberdan Nunes

Il existe plusieurs façons de partager des données entre contrôleurs.

  • Services angulaires
  • $ broadcast, méthode $ emit
  • Communication du contrôleur parent à enfant
  • $ rootscope

Comme nous le savons, $rootscope n'est pas une méthode préférable pour le transfert de données ou la communication, car il s'agit d'une étendue globale disponible pour toute l'application.

Pour le partage de données entre Angular contrôleurs Js Angular services sont les meilleures pratiques, par exemple. .factory, .service
Pour référence

En cas de transfert de données du contrôleur parent au contrôleur enfant, vous pouvez accéder directement aux données parent dans le contrôleur enfant via $scope
Si vous utilisez ui-router, vous pouvez utiliser $stateParmas pour transmettre des paramètres d'URL tels que id, name, key, etc.

$broadcast est également un bon moyen de transférer des données entre les contrôleurs d'un parent à l'enfant et $emit de transférer des données d'un enfant à des contrôleurs parents.

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

Référez-vous lien pour en savoir plus sur $broadcast

4
ojus kulkarni

Solution la plus simple:

J'ai utilisé un service AngularJS .

Étape 1: J'ai créé un service AngularJS nommé SharedDataService.

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

    };
    return Person;
});

Étape 2: Créez deux contrôleurs et utilisez le service créé ci-dessus.

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

Étape 3: Utilisez simplement les contrôleurs créés dans la vue.

<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>

Pour voir une solution de travail à ce problème, cliquez sur le lien ci-dessous

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

.html fichier:

<!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>
2
wins999

Il existe un autre moyen de ne pas utiliser $ watch, en utilisant 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;
});
1
Hinrich

Il y a plusieurs façons de le faire.

  1. Événements - déjà bien expliqué.

  2. routeur ui - expliqué ci-dessus.

  3. Service - avec la méthode de mise à jour affichée ci-dessus
  4. BAD - Surveiller les modifications.
  5. Une autre approche parent-enfant plutôt que emit et brodcast -

*

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

Je ne sais pas où j'ai trouvé ce modèle, mais pour le partage de données entre contrôleurs et la réduction de $ rootScope et de $ scope, cela fonctionne très bien. Cela rappelle une réplication de données dans laquelle vous avez des éditeurs et des abonnés. J'espère que ça aide.

Le service:

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

Le contrôleur qui reçoit les nouvelles données, qui est l'éditeur, ferait quelque chose comme ceci.

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);

Le contrôleur qui utilise également ces nouvelles données, appelé l'abonné, ferait quelque chose comme ceci ...

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

Cela fonctionnera pour n'importe quel scénario. Ainsi, lorsque le contrôleur principal est initialisé et qu'il récupère des données, il appelle la méthode changeData, qui le diffuse ensuite à tous les abonnés de ces données. Cela réduit le couplage de nos contrôleurs les uns aux autres.

0
ewahner