Daten zwischen Controllern in Angular JS übertragen?


243

Ich habe einen Basis-Controller, der meine Produkte anzeigt.

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

Aus meiner Sicht zeige ich diese Produkte in einer Liste an

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

Ich versuche zu tun, wenn jemand auf den Produktnamen klickt, ich habe eine andere Ansicht namens Warenkorb, in der dieses Produkt hinzugefügt wird.

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

Mein Zweifel hier ist also, wie diese angeklickten Produkte vom ersten Controller zum zweiten weitergegeben werden können. Ich ging davon aus, dass Cart auch ein Controller sein sollte.

Ich behandle Klickereignisse mit Direktiven. Ich denke auch, ich sollte den Service nutzen, um die oben genannten Funktionen zu erreichen. Kannst du nicht herausfinden, wie? Da der Warenkorb vordefiniert ist, kann die Anzahl der hinzugefügten Produkte je nach Seitenbenutzer 5/10 betragen. Also möchte ich dieses Generikum behalten.

Aktualisieren:

Ich habe einen Dienst zum Senden erstellt und im zweiten Controller erhalte ich ihn. Die Frage ist nun, wie ich dom aktualisiere. Da meine Liste zum Löschen von Produkten ziemlich fest codiert ist.


Antworten:


322

Aus der Beschreibung geht hervor, dass Sie einen Dienst verwenden sollten. Unter http://egghead.io/lessons/angularjs-sharing-data-between-controllers und AngularJS Service Passing Data zwischen Controllern finden Sie einige Beispiele.

Sie können Ihren Produktservice (als Fabrik) als solche definieren:

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

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

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

  return {
    addProduct: addProduct,
    getProducts: getProducts
  };

});

Die Abhängigkeit injiziert den Dienst in beide Controller.

ProductControllerDefinieren Sie in Ihrer Aktion eine Aktion, mit der das ausgewählte Objekt dem Array hinzugefügt wird:

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

In Ihrem CartControllererhalten Sie die Produkte vom Service:

app.controller('CartController', function($scope, productService) {
    $scope.products = productService.getProducts();
});

3
Cart Controller wird einmal aktualisiert, wenn getProducts () aufgerufen wird? Oder jedes Mal, wenn ein neues Produkt hinzugefügt wird?
Kishanio

1
Wenn Sie möchten, dass es automatisch aktualisiert wird, können Sie die Sendung aus Maxim Shoustins Antwort hinzufügen und getProducts () innerhalb der Funktion $ on aufrufen, um den Bereich von CartCtrl zu aktualisieren.
Chalise

2
@krillgar Du bist völlig richtig, das wurde anfangs übersehen. Ich habe die Antwort bearbeitet, um eine funktionierende Lösung wiederzugeben.
Chalise

1
@DeveshM Ja, mach was du vorschlägst. Möglicherweise möchten Sie die Methoden erweitern, damit sie nicht nur Daten im Speicher speichern und dauerhafter sind ( $httpz. B. per Aufruf auf dem Server speichern ).
Chalise

1
Was passiert, wenn ich zum Beispiel 2 productController und 2 Wagen habe? Beiden wird das Element hinzugefügt? Wie lösen Sie das in diesem Fall?
Uhr

67

Wie werden diese angeklickten Produkte vom ersten Controller an den zweiten übergeben?

Beim Klicken können Sie eine Methode aufrufen, die Broadcast aufruft :

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

und der zweite Controller hört dieses Tag wie folgt ab:

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

Da wir $ scope nicht in Services einfügen können, gibt es nichts Besseres als einen singleton $ scope.

Aber wir können spritzen $rootScope. Wenn Sie also Wert im Service speichern, können Sie ihn $rootScope.$broadcast('SOME_TAG', 'your value');im Service-Body ausführen . (Siehe @Charx Beschreibung der Dienste)

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

Bitte lesen Sie einen guten Artikel über $ Broadcast , $ Emit


1
Yup das funktioniert wie Charme, den ich Fabrik benutze? Es gibt nur eine letzte Sache, bei der ich nicht weiterkomme. Jedes Mal, wenn ich auf das Produkt klicke, erhalte ich Daten in einem neuen Controller. Wie aktualisiere ich es jetzt in DOM? Weil ich bereits eine Liste von 5 fest codierten Rändern habe, damit jedes Produkt in sie aufgenommen werden muss
Kishanio

1
@KT - Hast du jemals eine Antwort bekommen? Es scheint eine offensichtliche Frage zu sein, aber ich kann die Antwort nirgendwo finden. Ich möchte, dass ein Controller einen Wert ändert. Wenn sich dieser App-Wert ändert, sollten sich alle anderen Listening-Controller bei Bedarf selbst aktualisieren. Kann es nicht finden.
Raddevus

2
@daylight die Antwort auf Ihrem q. basiert auf Ihrer Controller-Struktur: parallel oder untergeordnet. $rootScope.$broadcastbenachrichtigt alle Controller mit paralleler Struktur, auch bekannt als dieselbe Ebene.
Maxim Shoustin

@MS Danke für die Antwort. Gibt es eine Möglichkeit, dies mit $ watch zu tun? Übrigens, meine sind parallele Controller und ich habe es mit Ihrer Methode zum Laufen gebracht. Wenn ich versuche, die $ watch (sogar zu $ ​​rootScope) hinzuzufügen, wird für mich die mit $ watch im 2. Controller verknüpfte Methode nur beim ersten Mal ausgelöst (Initialisierung des 2. Controllers). Vielen Dank.
Raddevus

1
Wir können verwenden, $watchwenn der Wert auf $rootScopeEbene existiert . Andernfalls benachrichtigt möglicherweise nur Broadcast andere Controller. Zu Ihrer Information, wenn andere Programmierer in Ihrem Code sehen broadcast- die Logik ist klar, was Sie versuchen - "Broadcast-Ereignis". Sowieso von meiner exp. Es ist keine gute Praxis rootscopefür watch. Informationen zu "Nur zum ersten Mal feuern": Sehen Sie sich dieses Beispiel an: plnkr.co/edit/5zqkj7iYloeHYkzK1Prt?p=preview Sie haben alte und neue Werte. Stellen Sie sicher, dass jedes Mal, wenn Sie einen neuen Wert erhalten, der nicht dem alten entspricht
Maxim Shoustin

24

Lösung ohne Service zu erstellen, mit $ rootScope:

Um Eigenschaften für App-Controller freizugeben, können Sie Angular $ rootScope verwenden. Dies ist eine weitere Option zum Teilen von Daten, damit die Benutzer davon erfahren.

Die bevorzugte Methode zum Teilen einiger Funktionen zwischen Controllern ist Services, um eine globale Eigenschaft zu lesen oder zu ändern, die Sie mit $ rootcope verwenden können.

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

Verwenden von $ rootScope in einer Vorlage (Zugriffseigenschaften mit $ root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>

27
$rootScopesollte so weit wie möglich vermieden werden.
Shaz

1
@Shaz könntest du näher erläutern warum?
Mirko

5
@Mirko Sie sollten versuchen, den globalen Status so weit wie möglich zu vermeiden, da jeder ihn ändern kann, wodurch Ihr Programmstatus unvorhersehbar wird.
Umut Seven

4
$ rootScope hat einen globalen Gültigkeitsbereich, daher müssen wir sie wie native globale Variablen sorgfältig verwenden. Wenn ein Controller seine Werte ändert, wird er für den globalen Bereich geändert.
Sanjeev

1
Services & Factories sind beide Singleton, aber Sie können wählen, ob Sie einen Service in Ihren Controller einspeisen oder nicht. Alle untergeordneten Bereiche stammen jedoch aus dem Rootscope und folgen der prototypischen Kette. Wenn Sie also versuchen, auf eine Eigenschaft in Ihrem Bereich zuzugreifen, die nicht vorhanden ist, wird die Kette nachgeschlagen. Es besteht die Möglichkeit, dass Sie die Rootscope-Eigenschaft unerwünscht ändern.
Sanjeev

16

Sie können dies auf zwei Arten tun.

  1. Durch die Verwendung $rootscope, aber ich empfehle dies nicht. Das $rootScopeist der oberste Bereich. Eine App kann nur eine $rootScopehaben, die von allen Komponenten einer App gemeinsam genutzt wird. Daher verhält es sich wie eine globale Variable.

  2. Dienste nutzen. Sie können dies tun, indem Sie einen Dienst zwischen zwei Controllern teilen. Der Code für den Service kann folgendermaßen aussehen:

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

    Sie können meine Geige hier sehen .


und eine dritte Option, können Sie Ereignis senden, nur um die Liste hinzuzufügen
DanteTheSmith

WAS, WENN DER BENUTZER DIE SEITE ERNEUERT? DANN GEBEN getProducts () IHNEN NICHTS! (Sie können nicht allen Benutzern sagen, dass sie nicht
aktualisieren sollen

2
@shreedharbhat Die Frage bezieht sich nicht auf das Fortbestehen von Daten beim erneuten Laden von Seiten.
Jack Vial

9

Eine noch einfachere Möglichkeit, die Daten zwischen Controllern auszutauschen, ist die Verwendung verschachtelter Datenstrukturen. Anstelle von zum Beispiel

$scope.customer = {};

wir können benutzen

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

Die dataEigenschaft wird vom übergeordneten Bereich geerbt, damit wir ihre Felder überschreiben und den Zugriff von anderen Controllern beibehalten können.


1
Controller erben Bereichseigenschaften von übergeordneten Controllern an ihren eigenen Bereich. Reinigen! Einfachheit macht es schön
Carlos R Balebona

Ich kann es nicht zum Laufen bringen. Auf dem zweiten Controller verwende ich $ scope.data und es ist undefiniert.
Bart Calixto

Er spricht von verschachtelten Controllern, denke ich. Auch mir nützt es nicht viel.
Norbert Norbertson

8
angular.module('testAppControllers', [])
    .controller('ctrlOne', function ($scope) {
        $scope.$broadcast('test');
    })
    .controller('ctrlTwo', function ($scope) {
        $scope.$on('test', function() {
        });
    });

es verwendet niether
Leathan

5

Wir können Daten in einer Sitzung speichern und sie überall in unserem Programm verwenden.

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

Anderer Ort

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

4

Ich habe die Antworten hier gesehen und es beantwortet die Frage des Datenaustauschs zwischen Controllern. Aber was soll ich tun, wenn ein Controller den anderen über die Tatsache informieren soll, dass die Daten geändert wurden (ohne Broadcast zu verwenden)? EINFACH! Nur mit dem berühmten Besuchermuster:

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

Auf diese einfache Weise kann ein Controller einen anderen Controller aktualisieren, wenn einige Daten aktualisiert wurden.


2
Wäre das Beobachtermuster ( en.wikipedia.org/wiki/Observer_pattern ) hier nicht relevanter? Besuchermuster ist etwas anderes ... en.wikipedia.org/wiki/Visitor_pattern
Ant Kutschera

3

Ich habe eine Factory erstellt, die den gemeinsamen Bereich zwischen dem Muster des Routenpfads steuert, sodass Sie die freigegebenen Daten nur dann verwalten können, wenn Benutzer im gleichen übergeordneten Routenpfad navigieren.

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

Wenn der Benutzer zu einem anderen Routenpfad wechselt, z. B. '/ Support', werden die freigegebenen Daten für den Pfad '/ Kunde' automatisch zerstört. Wenn der Benutzer stattdessen zu "untergeordneten" Pfaden wie "/ Customer / 1" oder "/ Customer / list" wechselt, wird der Bereich nicht zerstört.

Ein Beispiel finden Sie hier: http://plnkr.co/edit/OL8of9


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

Beim Klicken können Sie eine Methode aufrufen, die Broadcast aufruft:

$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

Ein Weg mit Winkelservice:

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

2
Geben Sie Ihrer Antwort eine weitere Erklärung.
Gerhard Barnard

2

Erstellen Sie eine Factory in Ihrem Modul und fügen Sie eine Referenz der Factory in der Steuerung hinzu. Verwenden Sie deren Variablen in der Steuerung. Ermitteln Sie jetzt den Wert der Daten in einer anderen Steuerung, indem Sie eine Referenz hinzufügen, wo immer Sie möchten


2

Ich weiß nicht, ob es jemandem helfen wird, aber basierend auf der Antwort von Charx (danke!) Habe ich einen einfachen Cache-Service erstellt. Fühlen Sie sich frei zu verwenden, zu remixen und zu teilen:

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

2

Ein Weg mit Winkelservice:

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/


1

Zu Ihrer Information Das $ scope-Objekt hat die $ emit, $ Broadcast, $ on UND Das $ rootScope-Objekt hat die identische $ emit, $ Broadcast, $ on

Lesen Sie mehr über Publish / Subscribe - Musterentwurf in Winkel hier


1

Um die von @Maxim vorgeschlagene Lösung mithilfe von $ Broadcast zu verbessern, ändern sich die Sendedaten nicht

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

aber auf Daten zu hören

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

1

Es gibt drei Möglichkeiten, dies zu tun:

a) Nutzung eines Dienstes

b) Ausnutzen der abhängigen Eltern / Kind-Beziehung zwischen Controller-Bereichen.

c) In Angular 2.0 werden die Daten mit dem Schlüsselwort "As" von einem Controller an einen anderen übergeben.

Weitere Informationen mit Beispiel finden Sie unter folgendem Link:

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


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

Erster Controller

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

Zweiter Controller

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

-1

Ich denke, die

bester Weg

ist $ localStorage zu verwenden. (Funktioniert die ganze Zeit)

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

Ihr cardController wird sein

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

Sie können auch hinzufügen

if($localStorage.selectedObj){
    $scope.selectedProducts = $localStorage.selectedObj;
}else{
    //redirect to select product using $location.url('/select-product')
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.