AngularJS kontrolörleri arasında veri paylaşma


362

Denetleyiciler arasında veri paylaşmaya çalışıyorum. Kullanım durumu çok adımlı bir formdur, bir girişe girilen veriler daha sonra orijinal denetleyicinin dışındaki birden fazla görüntüleme konumunda kullanılır. Kod aşağıda ve burada 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;
});

Herhangi bir yardım büyük beğeni topluyor.


Yanıtlar:


481

Basit bir çözüm, fabrikanızın bir nesneyi döndürmesini ve denetleyicilerinizin aynı nesneye referansla çalışmasını sağlamaktır:

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>

Demo: http://jsfiddle.net/HEdJF/

Uygulamalar büyüdükçe, daha karmaşık ve test edilmesi zorlaştığında, tüm nesneyi fabrikadan bu şekilde göstermek istemeyebilirsiniz, bunun yerine örneğin alıcılar ve ayarlayıcılar aracılığıyla sınırlı erişim sağlayabilirsiniz:

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

    var data = {
        FirstName: ''
    };

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

Bu yaklaşımla, fabrikayı yeni değerlerle güncellemek ve bunları elde etmek için değişiklikleri izlemek, tüketen denetleyicilere bağlıdır:

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>

Demo: http://jsfiddle.net/27mk1n1o/


2
İlk çözüm benim için en iyi şekilde çalıştı - hizmetin nesneyi korumasını ve denetleyicilerin sadece referansla işlemesini sağladı. Mucho teşekkürler.
johnkeese

5
$ scope. $ watch yöntemi, bir kapsamdan bir dinlenme çağrısı yapmak ve sonuçları başka bir kapsama uygulamak için güzel çalışır
aclave1

Gibi bir nesneyi return { FirstName: '' };döndürmek veya bir nesneyle etkileşimde bulunan yöntemler içeren bir nesneyi döndürmek yerine, bir class ( function) örneğini döndürmek tercih edilmez mi, böylece nesneyi kullandığınızda belirli bir türe sahip olur ve yalnızca bir obje{}" ?
RPDeshaies

8
Sadece bir baş yukarı, dinleyici işlevinin newValue !== oldValuedenetime ihtiyacı yoktur çünkü oldValue ve newValue aynı ise Angular işlevi yürütmez. Bunun tek istisnası, ilk değeri önemsemenizdir. Bakınız: docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
Gautham C.

@tasseKATT, Sayfayı yenilemezsem iyi çalışır. Sayfayı yenilersem bana herhangi bir çözüm önerebilir misiniz ???
MaNn

71

Bunun için kullanmamayı tercih ederim $watch. Hizmetin tamamını denetleyicinin kapsamına atamak yerine yalnızca verileri atayabilirsiniz.

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>

Alternatif olarak, servis verilerini doğrudan bir yöntemle güncelleyebilirsiniz.

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

Açıkça watch () kullanmazsınız, ancak AngularJS dahili olarak görünümü $ scope değişkenlerindeki yeni değerlerle günceller. Performans açısından aynı mı değil mi?
Mart Dominguez

4
Her iki yöntemin performansından da emin değilim. Anladığım kadarıyla Angular zaten kapsamda bir özet döngüsü çalıştırıyor, bu nedenle ek saat ifadeleri ayarlamak muhtemelen Angular için daha fazla iş ekliyor. Gerçekten öğrenmek için bir performans testi yapmanız gerekir. Ben sadece $ watch ifadeleri üzerinden güncelleme üzerinden yukarıdaki cevap üzerinden veri aktarımı ve paylaşımı tercih ediyorum.
bennick

2
Bu $ watch'a bir alternatif değil. Bu, $ watch kullanmak zorunda kalmadan iki Açısal nesne arasında, bu durumda denetleyiciler arasında veri paylaşmanın farklı bir yoludur.
bennick

1
IMO bu çözüm en iyisidir, çünkü biraz daha jQuery-esque hisseden $ watch ifadeleri eklemenin aksine Angular'ın açıklayıcı yaklaşımının ruhundadır.
JamesG

8
Neden merak ettiğim en çok oylanan cevap bu değil. Tüm $ watch sonra kodu daha karmaşık hale getirir
Shyamal Parikh

11

Verileri denetleyiciler arasında paylaşmanın birçok yolu vardır

  1. hizmetleri kullanma
  2. $ state.go hizmetlerini kullanma
  3. stateparams kullanma
  4. anaç kullanma

Her yöntemin açıklaması:

  1. Birisi tarafından zaten açıklandığı gibi açıklamayacağım

  2. kullanma $state.go

      $state.go('book.name', {Name: 'XYZ'}); 
    
      // then get parameter out of URL
      $state.params.Name;
  3. $stateparambenzer şekilde çalışır $state.go, gönderen denetleyicisinden nesne olarak geçirir ve stateparam kullanarak alıcı denetleyicisinde toplarsınız

  4. kullanma $rootscope

    (a) çocuktan ana denetleyiciye veri gönderme

      $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) ebeveynten çocuk denetleyicisine veri gönderme

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

6

Rota yolunun deseni arasındaki paylaşılan kapsamı kontrol eden bir fabrika oluşturdum, böylece kullanıcılar aynı rota üst yolunda gezinirken paylaşılan verileri koruyabilirsiniz.

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

Dolayısıyla, kullanıcı '/ Destek' gibi başka bir yol yoluna giderse, '/ Müşteri' yolu için paylaşılan veriler otomatik olarak imha edilir. Ancak, bunun yerine kullanıcı '/ Customer / 1' veya '/ Customer / list' gibi 'alt' yollara giderse kapsam yok olmaz.

Burada bir örnek görebilirsiniz: http://plnkr.co/edit/OL8of9


$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){ ... })Ui.router'ınız varsa kullanın
Nuno Silva

6

Denetleyiciler arasında veri paylaşmanın birden çok yolu vardır

  • Açısal hizmetler
  • $ broadcast, $ emit yöntemi
  • Ebeveyn-çocuk denetleyicisi iletişimi
  • $ rootscope

Bildiğimiz gibi $rootscope, veri aktarımı veya iletişim için tercih edilen bir yol değildir, çünkü tüm uygulama için mevcut olan küresel bir kapsamdır

Açısal Js denetleyicileri arasında veri paylaşımı için Açısal hizmetler en iyi uygulamalardır. .factory, .service
İçin referans

Çocuk denetleyiciye ebeveynden veri transferi durumda yoluyla çocuk denetleyicisi doğrudan erişim üst veri olabilir $scope
Eğer kullanıyorsanız ui-routero zaman kullanabilirsiniz $stateParmasurl parametreleri gibi geçmesine id, name, keyvb

$broadcastdenetleyiciler arasında veli çocuktan çocuğa $emitveri aktarmanın ve çocuktan ebeveyn denetleyicilere veri aktarmanın iyi bir yoludur

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

Hakkında daha fazla bilgi için verilen bağlantıya bakın$broadcast


4

En Basit Çözüm:

Bir AngularJS hizmeti kullandım .

Adım 1: SharedDataService adlı bir AngularJS hizmeti oluşturduk.

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

    };
    return Person;
});

Adım 2: İki denetleyici oluşturun ve yukarıda oluşturulan hizmeti kullanın.

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

Adım 3: Görünümde oluşturulan denetleyicileri kullanın.

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

Bu sorunun çalışma çözümünü görmek için lütfen aşağıdaki bağlantıya basın

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

.html dosyası:

<!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
bu bize açısalJ kullanan yeni halk için çok açık bir örnek. Bir ebeveyn / çocuk ilişkisinden verileri nasıl paylaşır / iletirsiniz? FirstCtrlebeveyn ve SecondCtrl(çocuk) da ihtiyacı olacak bir söz alır ? İki api çağrısı yapmak istemiyorum. Teşekkürler.
Chris22

1

$ Watch kullanmadan, angular.copy kullanarak başka bir yol var:

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

Bunu yapmanın birçok yolu vardır.

  1. Olaylar - zaten iyi açıklanmıştı.

  2. ui yönlendirici - yukarıda açıklanmıştır.

  3. Hizmet - yukarıda görüntülenen güncelleme yöntemi ile
  4. KÖTÜ - Değişiklikleri izlemek.
  5. Yerine başka ebeveyn çocuk yaklaşım EMIT ve 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();
        }
    }
});

0

Bu modeli nereden aldığımdan emin değilim ama denetleyiciler arasında veri paylaşmak ve $ rootScope ve $ kapsamını azaltmak için bu harika çalışıyor. Yayıncılarınız ve aboneleriniz olduğu bir veri çoğaltmasını hatırlatır. Umarım yardımcı olur.

Hizmet:

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

Yayıncı olan yeni verileri alan Denetleyici böyle bir şey yapacaktır ..

var someData = yourDataService.getSomeData();

sharedDataEventHub.changeData(someData);

Abone olarak adlandırılan bu yeni verileri kullanan Denetleyici böyle bir şey yapacaktır ...

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

Bu, herhangi bir senaryo için işe yarayacaktır. Birincil denetleyici başlatıldığında ve veri aldığında, daha sonra bu verilerin tüm abonelerine yayınlanacak olan changeData yöntemini çağırır. Bu, kontrolörlerimizin birbirine bağlanmasını azaltır.


durumu kontrolörler arasında paylaşan bir 'model' kullanmak, çoğunlukla kullanılan seçenek gibi görünüyor. Maalesef bu, çocuk denetleyicisini yenileyeceğiniz senaryoyu kapsamaz. Verileri sunucudan nasıl 'yeniden alıyorsunuz' ve modeli yeniden oluşturuyorsunuz? Önbellek geçersiz kılma ile nasıl başa çıkacaksınız?
Andrei

Ebeveyn ve çocuk denetleyicileri fikri biraz tehlikeli ve birbirine çok fazla bağımlılığa neden oluyor. Bununla birlikte, UI-Router ile birlikte kullanıldığında, özellikle bu veri durum yapılandırması yoluyla enjekte ediliyorsa, "çocuğunuz" üzerindeki verileri kolayca yenileyebilirsiniz. Önbellek geçersiz kılma yayıncıda gerçekleşir ve böylece verileri almak ve çağırmaktan sorumlu olurlarsharedDataEventHub.changeData(newData)
ewahner

Bence bir yanlış anlama var. Yenileme ile, kullanıcıların tam anlamıyla klavyedeki F5 tuşuna basın ve tarayıcının bir gönderi yapmasına neden olur. Bu durumda alt öğe / ayrıntılar görünümündeyseniz, "var someData = yourDataService.getSomeData ()" öğesini çağıracak kimse yoktur. Soru şu: Bu senaryo ile nasıl başa çıkacaksınız? modeli kim güncellemeli?
Andrei

Denetleyicilerinizi nasıl etkinleştirdiğinize bağlı olarak, bu çok basit olabilir. Etkinleştiren bir yönteminiz varsa, verilerin ilk olarak yüklenmesinden sorumlu olan denetleyici yüklüyor ve daha sharedDataEventHub.changeData(newData)sonra bu verilere bağlı olan herhangi bir alt öğe veya denetleyiciyi denetleyici gövdesinde bir yere sahip olacaktır: sharedDataEventHub.onChangeData($scope, function(obj) { vm.myObject = obj.data; });UI kullandıysanız -Bu yönlendirici durum yapılandırmasından enjekte edilebilir.
ewahner

İşte anlatmaya çalışıyorum sorunu ilustrates bir örnek: plnkr.co/edit/OcI30YX5Jx4oHyxHEkPx?p=preview . Gövdeyi çıkarırsanız, düzenleme sayfasına gidin ve tarayıcıyı yenileyin, çökecektir. Bu durumda modeli kim yeniden oluşturmalı?
Andrei

-3

Sadece basit yapın (v1.3.15 ile test edilmiştir):

<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
Bence bu aşağı oylandı, çünkü modüler değil. dummy$ scope veya controllerAs ile miras yoluyla veya bir hizmet kullanarak daha modüler bir şekilde paylaşmak yerine her iki denetleyici için de globaldir.
Chad Johnson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.