Passer des données entre les contrôleurs dans Angular JS?

243

J'ai un contrôleur de base qui affiche mes produits,

App.controller('ProductCtrl',function($scope,$productFactory){
     $productFactory.get().success(function(data){
           $scope.products = data;
     });
});

À mon avis, j'affiche ces produits dans une liste

<ul>
    <li ng-repeat="product as products">
        {{product.name}}
    </li>
</ul

Ce que j'essaie de faire, c'est quand quelqu'un clique sur le nom du produit, j'ai une autre vue nommée panier où ce produit est ajouté.

 <ul class="cart">
      <li>
          //click one added here
      </li>
      <li>
          //click two added here
      </li>
 </ul>

Donc, mon doute ici est, comment faire passer ces produits cliqués du premier contrôleur au second? J'ai supposé que le chariot devrait également être un contrôleur.

Je gère l'événement click en utilisant la directive. De plus, je pense que je devrais utiliser le service pour atteindre les fonctionnalités ci-dessus, je ne sais pas comment? parce que le panier sera prédéfini, le nombre de produits ajoutés pourrait être de 5/10 en fonction de l'utilisateur de la page. Je voudrais donc garder ce générique.

Mettre à jour:

J'ai créé un service pour diffuser et dans le deuxième contrôleur je le reçois. Maintenant, la question est de savoir comment mettre à jour dom? Étant donné que ma liste de produits à déposer est assez codée en dur.

kishanio
la source

Réponses:

322

D'après la description, il semble que vous devriez utiliser un service. Consultez http://egghead.io/lessons/angularjs-sharing-data-between-controllers et AngularJS Service Passing Data Between Controllers pour voir quelques exemples.

Vous pouvez définir votre service produit (en tant qu'usine) comme tel:

app.factory('productService', function() {
  var productList = [];

  var addProduct = function(newObj) {
      productList.push(newObj);
  };

  var getProducts = function(){
      return productList;
  };

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

La dépendance injecte le service dans les deux contrôleurs.

Dans votre ProductController, définissez une action qui ajoute l'objet sélectionné au tableau:

app.controller('ProductController', function($scope, productService) {
    $scope.callToAddToProductList = function(currObj){
        productService.addProduct(currObj);
    };
});

Dans votre CartController, obtenez les produits du service:

app.controller('CartController', function($scope, productService) {
    $scope.products = productService.getProducts();
});
Chalise
la source
3
Le contrôleur de panier sera mis à jour une fois lorsque getProducts () sera appelé? Ou chaque fois qu'un nouveau produit est ajouté?
kishanio
1
Si vous souhaitez qu'il se mette à jour automatiquement, vous pouvez ajouter la diffusion à partir de la réponse de Maxim Shoustin et appeler getProducts () dans la fonction $ on pour mettre à jour la portée de CartCtrl.
Chalise
2
@krillgar Vous avez tout à fait raison, cela a été ignoré au départ. J'ai édité la réponse pour refléter une solution de travail.
Chalise
1
@DeveshM Oui, faites comme vous le suggérez. Vous voudrez peut-être envisager d'étendre les méthodes afin qu'elles ne conservent pas seulement des données en mémoire et soient plus persistantes (enregistrez-les sur le serveur via un $httpappel, par exemple).
Chalise
1
Que se passe-t-il si j'ai 2 contrôleurs de produits et 2 chariots par exemple? Les deux auront l'élément ajouté? Comment résolvez-vous cela dans ce cas?
atoth
67

comment passer ces produits cliqués du premier contrôleur au second?

Au clic, vous pouvez appeler la méthode qui appelle la diffusion :

$rootScope.$broadcast('SOME_TAG', 'your value');

et le deuxième contrôleur écoutera sur cette balise comme:

$scope.$on('SOME_TAG', function(response) {
      // ....
})

Comme nous ne pouvons pas injecter $ scope dans les services, rien de tel qu'un $ singleton scope.

Mais nous pouvons injecter $rootScope. Donc, si vous stockez de la valeur dans le service, vous pouvez l'exécuter $rootScope.$broadcast('SOME_TAG', 'your value');dans le corps du service. (Voir la description de @Charx sur les services)

app.service('productService',  function($rootScope) {/*....*/}

Veuillez consulter un bon article sur $ broadcast , $ emit

Maxim Shoustin
la source
1
Ouais cela fonctionne comme un charme que j'utilise en usine? Il y a juste une dernière chose où je suis bloqué, j'obtiens des données dans le nouveau contrôleur chaque fois que je clique sur le produit. Maintenant, comment puis-je le mettre à jour dans DOM? Parce que j'ai déjà disons une liste de 5 codés en dur avec des bordures, donc chaque produit doit aller à l'intérieur
kishanio
1
@KT - Avez-vous déjà obtenu une réponse? Cela semble être une question évidente, mais je ne trouve la réponse nulle part. Je veux qu'un contrôleur change une valeur. Lorsque cette valeur d'application change, tous les autres contrôleurs d'écoute doivent se mettre à jour si nécessaire. Je ne le trouve pas.
raddevus
2
@daylight la réponse sur votre q. est basé sur la structure de votre contrôleur: parallèle ou parent-enfant. $rootScope.$broadcastavertit tous les contrôleurs qui ont une structure parallèle aka le même niveau.
Maxim Shoustin
@MS Merci pour la réponse. Existe-t-il un moyen de le faire en utilisant $ watch? BTW, Mine sont des contrôleurs parallèles et je l'ai fait fonctionner en utilisant votre méthode. Pour moi, chaque fois que j'essaie d'ajouter la $ watch (même à $ rootScope), la méthode associée à la $ watch dans le 2ème contrôleur ne se déclenche que la première fois (initialisation du 2ème contrôleur). Merci.
raddevus
1
nous pouvons utiliser $watchsi la valeur existe au $rootScopeniveau. Sinon, seule la diffusion risque d'avertir les autres contrôleurs. Pour info, si un autre programmeur voit dans votre code broadcast- la logique est claire ce que vous essayez de faire - "événement diffusé". Quoi qu'il en soit, de mon exp. son pas une bonne pratique à utiliser rootscopepour watch. A propos de "Fire 1st time only": jetez un œil à cet exemple: plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=preview vous avez d'anciennes et de nouvelles valeurs. assurez-vous que chaque fois que vous obtenez une nouvelle valeur qui ne correspond pas à l'ancienne
Maxim Shoustin
24

Solution sans créer de service, en utilisant $ rootScope:

Pour partager des propriétés entre les contrôleurs d'application, vous pouvez utiliser Angular $ rootScope. Il s'agit d'une autre option pour partager des données, en les mettant à la disposition des utilisateurs.

Les services préférés pour partager certaines fonctionnalités entre les contrôleurs sont les services, pour lire ou modifier une propriété globale, vous pouvez utiliser $ 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;
}]);

Utilisation de $ rootScope dans un modèle (Accéder aux propriétés avec $ root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>
Sanjeev
la source
27
$rootScopedoit être évité autant que possible.
Shaz
1
@Shaz pourriez-vous expliquer pourquoi?
Mirko
5
@Mirko Vous devriez essayer d'éviter l'état global autant que possible car tout le monde peut le changer, ce qui rend l'état de votre programme imprévisible.
Umut Seven
4
$ rootScope a une portée globale, donc comme les variables globales natives, nous devons les utiliser avec précaution. Si un contrôleur modifie ses valeurs, il sera modifié pour l'étendue globale.
Sanjeev
1
Les services et les usines sont tous deux Singleton, mais vous pouvez choisir d'injecter ou non n'importe quel service dans votre contrôleur. Mais toute la portée enfant dérive de rootscope et suit la chaîne prototypique. Donc, si vous essayez d'accéder à une propriété sur votre portée et qu'elle n'est pas présente, elle recherchera la chaîne. Il est possible que vous changiez de manière indésirable la propriété rootscope.
Sanjeev
16

Vous pouvez le faire par deux méthodes.

  1. En utilisant $rootscope, mais je ne le recommande pas. C'est $rootScopela portée la plus élevée. Une application ne peut en avoir qu'une $rootScopequi sera partagée entre tous les composants d'une application. Il agit donc comme une variable globale.

  2. Utiliser des services. Vous pouvez le faire en partageant un service entre deux contrôleurs. Le code de service peut ressembler à ceci:

    app.service('shareDataService', function() {
        var myList = [];
    
        var addList = function(newObj) {
            myList.push(newObj);
        }
    
        var getList = function(){
            return myList;
        }
    
        return {
            addList: addList,
            getList: getList
        };
    });

    Vous pouvez voir mon violon ici .

Nisham Mahsin
la source
et une 3ème option, vous pouvez envoyer un événement, juste pour ajouter sur la liste
DanteTheSmith
ET SI L'UTILISATEUR RAFRAÎCHIT LA PAGE? ALORS getProducts () NE VOUS RENDRA RIEN! (U ne peut pas dire à tous les utilisateurs de ne pas se rafraîchir, n'est-ce pas?)
shreedhar bhat
2
@shreedharbhat La question ne concerne pas la persistance des données lors des rechargements de page.
Jack Vial
9

Un moyen encore plus simple de partager les données entre les contrôleurs consiste à utiliser des structures de données imbriquées. Au lieu de, par exemple

$scope.customer = {};

on peut utiliser

$scope.data = { customer: {} };

La datapropriété sera héritée de la portée parent afin que nous puissions remplacer ses champs, en gardant l'accès aux autres contrôleurs.

Bartłomiej Zalewski
la source
1
Les contrôleurs héritent des propriétés de portée des contrôleurs parents vers sa propre portée. Nettoyer! la simplicité le rend magnifique
Carlos R Balebona
ne peux pas le faire fonctionner. sur le deuxième contrôleur, j'utilise $ scope.data et ce n'est pas défini.
Bart Calixto du
Il parle de contrôleurs imbriqués je pense. Pas très utile pour moi non plus.
Norbert Norbertson
8
angular.module('testAppControllers', [])
    .controller('ctrlOne', function ($scope) {
        $scope.$broadcast('test');
    })
    .controller('ctrlTwo', function ($scope) {
        $scope.$on('test', function() {
        });
    });
Jijo Paulose
la source
il utilise du niether
Leathan
5

nous pouvons stocker des données en session et les utiliser n'importe où dans notre programme.

$window.sessionStorage.setItem("Mydata",data);

Autre endroit

$scope.data = $window.sessionStorage.getItem("Mydata");
Alex Kumbhani
la source
4

J'ai vu les réponses ici, et cela répond à la question du partage des données entre les contrôleurs, mais que dois-je faire si je veux qu'un contrôleur informe l'autre du fait que les données ont été modifiées (sans utiliser la diffusion)? FACILE! En utilisant simplement le célèbre modèle de visiteur:

myApp.service('myService', function() {

    var visitors = [];

    var registerVisitor = function (visitor) {
        visitors.push(visitor);
    }

    var notifyAll = function() {
        for (var index = 0; index < visitors.length; ++index)
            visitors[index].visit();
    }

    var myData = ["some", "list", "of", "data"];

    var setData = function (newData) {
        myData = newData;
        notifyAll();
    }

    var getData = function () {
        return myData;
    }

    return {
        registerVisitor: registerVisitor,
        setData: setData,
        getData: getData
    };
}

myApp.controller('firstController', ['$scope', 'myService',
    function firstController($scope, myService) {

        var setData = function (data) {
            myService.setData(data);
        }

    }
]);

myApp.controller('secondController', ['$scope', 'myService',
    function secondController($scope, myService) {

        myService.registerVisitor(this);

        this.visit = function () {
            $scope.data = myService.getData();
        }

        $scope.data = myService.getData();
    }
]);

De cette manière simple, un contrôleur peut mettre à jour un autre contrôleur que certaines données ont été mises à jour.

Omri Lahav
la source
2
Le modèle d'observation ( en.wikipedia.org/wiki/Observer_pattern ) ne serait-il pas plus pertinent ici? Le modèle de visiteur est autre chose ... fr.wikipedia.org/wiki/Visitor_pattern
Ant Kutschera
3

J'ai créé une fabrique qui contrôle la portée partagée entre le modèle du chemin de l'itinéraire, afin que vous puissiez conserver les données partagées juste lorsque les utilisateurs naviguent dans le même chemin parent de l'itinéraire.

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

Ainsi, si l'utilisateur accède à un autre chemin de l'itinéraire, par exemple «/ Support», les données partagées pour le chemin «/ Client» seront automatiquement détruites. Mais, si au lieu de cela, l'utilisateur va sur les chemins «enfants», comme «/ Customer / 1» ou «/ Customer / list», la portée ne sera pas détruite.

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

Oberdan Nunes
la source
3

1

using $localStorage

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

2

Au clic, vous pouvez appeler la méthode qui appelle la diffusion:

$rootScope.$broadcast('SOME_TAG', 'your value');

and the second controller will listen on this tag like:
$scope.$on('SOME_TAG', function(response) {
      // ....
})

3

using $rootScope:

4

window.sessionStorage.setItem("Mydata",data);
$scope.data = $window.sessionStorage.getItem("Mydata");

5

Une façon d'utiliser le service angulaire:

var app = angular.module("home", []);

app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});

app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});

app.factory('ser1', function(){
return {o: ''};
});
Subhransu
la source
2
mettez plus d'explications dans votre réponse.
Gerhard Barnard
2

Créez une usine dans votre module et ajoutez une référence de l'usine dans le contrôleur et utilisez ses variables dans le contrôleur et obtenez maintenant la valeur des données dans un autre contrôleur en ajoutant une référence où vous le souhaitez

Resham Kadel
la source
2

Je ne sais pas si cela aidera quelqu'un, mais sur la base de la réponse de Charx (merci!), J'ai créé un service de cache simple. N'hésitez pas à utiliser, remixer et partager:

angular.service('cache', function() {
    var _cache, _store, _get, _set, _clear;
    _cache = {};

    _store = function(data) {
        angular.merge(_cache, data);
    };

    _set = function(data) {
        _cache = angular.extend({}, data);
    };

    _get = function(key) {
        if(key == null) {
            return _cache;
        } else {
            return _cache[key];
        }
    };

    _clear = function() {
        _cache = {};
    };

    return {
        get: _get,
        set: _set,
        store: _store,
        clear: _clear
    };
});
marverix
la source
2

Une façon d'utiliser le service angulaire:

var app = angular.module("home", []);

app.controller('one', function($scope, ser1){
$scope.inputText = ser1;
});


app.controller('two',function($scope, ser1){
$scope.inputTextTwo = ser1;
});

app.factory('ser1', function(){
return {o: ''};
});



<div ng-app='home'>

<div ng-controller='one'>
  Type in text: 
  <input type='text' ng-model="inputText.o"/>
</div>
<br />

<div ng-controller='two'>
  Type in text:
  <input type='text' ng-model="inputTextTwo.o"/>
</div>

</div>

https://jsfiddle.net/1w64222q/

Siva ganesh
la source
1

FYI L'objet $ scope a le $ emit, $ broadcast, $ on ET l'objet $ rootScope a le $ emit, $ broadcast, $ on identique

en savoir plus sur le modèle de conception de publication / abonnement en angulaire ici

Anja Ishmukhametova
la source
1

Pour améliorer la solution proposée par @Maxim en utilisant $ broadcast, envoyer des données ne change pas

$rootScope.$broadcast('SOME_TAG', 'my variable');

mais aux données d'écoute

$scope.$on('SOME_TAG', function(event, args) {
    console.log("My variable is", args);// args is value of your variable
})
Cedriga
la source
1

Il y a trois façons de le faire,

a) utiliser un service

b) Exploiter la relation parent / enfant dépendante entre les portées du contrôleur.

c) Dans Angular 2.0, le mot clé "As" transmettra les données d'un contrôleur à un autre.

Pour plus d'informations avec exemple, veuillez vérifier le lien ci-dessous:

http://www.tutespace.com/2016/03/angular-js.html

Shrinivas Kalangutkar
la source
0
var custApp = angular.module("custApp", [])
.controller('FirstController', FirstController)
.controller('SecondController',SecondController)
.service('sharedData', SharedData);

FirstController.$inject = ['sharedData'];
function FirstController(sharedData) {
this.data = sharedData.data;
}

SecondController.$inject['sharedData'];
function SecondController(sharedData) {
this.data = sharedData.data;
}

function SharedData() {
this.data = {
    value: 'default Value'
}
}

Premier contrôleur

<div ng-controller="FirstController as vm">
<input type=text ng-model="vm.data.value" />
</div>

Deuxième contrôleur

 <div ng-controller="SecondController as vm">
    Second Controller<br>
    {{vm.data.value}}
</div>
testmeon
la source
-1

Je pense que le

meilleur moyen

est d'utiliser $ localStorage. (Fonctionne tout le temps)

app.controller('ProductController', function($scope, $localStorage) {
    $scope.setSelectedProduct = function(selectedObj){
        $localStorage.selectedObj= selectedObj;
    };
});

Votre cardController sera

app.controller('CartController', function($scope,$localStorage) { 
    $scope.selectedProducts = $localStorage.selectedObj;
    $localStorage.$reset();//to remove
});

Vous pouvez également ajouter

if($localStorage.selectedObj){
    $scope.selectedProducts = $localStorage.selectedObj;
}else{
    //redirect to select product using $location.url('/select-product')
}
shreedhar bhat
la source