AngularJS: Denetleyiciler arasında değişkenleri nasıl iletebilirim?


326

İki Açısal denetleyicim var:

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İçinde kullanamıyorum Ctrl2çünkü tanımsız. Ancak bu şekilde aktarmaya çalışırsam…

function Ctrl2($scope, Ctrl1) {
    $scope.prop2 = "Second";
    $scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}

Bir hata alıyorum. Bunu nasıl yapacağını bilen var mı?

Ctrl2.prototype = new Ctrl1();

Ayrıca başarısız.

NOT: Bu denetleyiciler iç içe yerleştirilmez.


Birçok yol var ama en iyi yol açısal saat. Her zaman bir çerçeve kullandığımızda iş için kendi yöntemlerini kullanmanın en iyi yoludur bunu unutma
pejman

Bu blogu çok faydalı buldum Blog
Black Mamba

Yanıtlar:


503

Değişkenleri birden çok denetleyicide paylaşmanın bir yolu, bir hizmet oluşturmak ve onu kullanmak istediğiniz herhangi bir denetleyiciye enjekte etmektir.

Basit servis örneği:

angular.module('myApp', [])
    .service('sharedProperties', function () {
        var property = 'First';

        return {
            getProperty: function () {
                return property;
            },
            setProperty: function(value) {
                property = value;
            }
        };
    });

Hizmeti bir denetleyicide kullanma:

function Ctrl2($scope, sharedProperties) {
    $scope.prop2 = "Second";
    $scope.both = sharedProperties.getProperty() + $scope.prop2;
}

Bu, bu blogda çok güzel açıklanmıştır (Ders 2 ve özellikle).

Birden çok denetleyicileri arasında bu özelliklere bağlamak istiyorsanız, bağlı başvuruyu korumak için bir ilkel tür (boolean, dize, sayı) yerine bir nesnenin özelliğine bağlarsanız daha iyi çalıştığını gördüm.

Örnek: var property = { Property1: 'First' };yerine var property = 'First';.


GÜNCELLEME: (Umarım) işleri daha açık hale getirmek için aşağıdakilere bir örnek gösteren bir keman budur:

  • Paylaşılan değerin statik kopyalarına bağlanma (myController1 içinde)
    • Bir ilkel (dize) bağlanması
    • Bir nesnenin özelliğine bağlama (kapsam değişkenine kaydedilir)
  • Değerler güncellendiğinde kullanıcı arayüzünü güncelleyen paylaşılan değerlere bağlanma (myController2'de)
    • Bir ilkel (dize) döndüren bir işleve bağlanma
    • Nesnenin özelliğine bağlanma
    • Bir nesnenin özelliğine iki yönlü bağlanma

5
Bu durumda - sharedProperties.getProperty () değeri değiştiğinde Ctrl2 kapsamı nasıl "bilir"?
OpherV

5
Kullanıcı arayüzünüzün özellik her değiştiğinde güncellenmesini istiyorsanız bothbir işlev olarak değiştirebilirsiniz ve açısal özetleme işlemi sırasında bu işlev çağrılır / yeniden değerlendirilir. Bkz bu keman bir örnek için. Ayrıca bir nesnenin özelliğine bağlanırsanız, onu doğrudan görünümünüzde kullanabilirsiniz ve veriler bu örneğe benzer şekilde değiştikçe güncellenecektir .
Gloopy

11
Denetleyicinizdeki değişiklikleri algılamak ve bunlara tepki vermek istiyorsanız, getProperty()işlevi kapsama eklemek ve $ scope. $ Watch işlevini bu örnekteki gibi kullanmaktır . Umarım bu örnekler yardımcı olur!
Gloopy

1
Hizmetlerin vatansız olması gerektiği için burada bir sorun var. Bir özelliği bir hizmetin içinde saklamak yanlıştır (ancak uygun). Verileri okumak ve yazmak için $ cacheFactory kullanmaya başladım. Gloopy olarak neredeyse aynı bir hizmet kullanıyorum, ancak durumu hizmette saklamak yerine şimdi önbellekte. Önce bir önbellek hizmeti oluşturun: angular.module ('CacheService', ['ng']) .factory ('CacheService', function ($ cacheFactory) {return $ cacheFactory ('CacheService');}); App.js'nize ekleyin, servise enjekte edin, şu şekilde kullanın: return CacheService.get (key); veya CacheService.put (anahtar; değer);
Jordan Papaleo

4
Bu cevap kullanır neden nasıl ve grok çalışılıyor .serviceyerine .factoryAçısal docs açıklandığı gibi. Dokümantasyon farklı bir yöntem kullandığında bu cevap neden bu kadar yüksek?
pspahn

44

Basit şeyleri basit örneklerle göstermeyi seviyorum :)

İşte çok basit bir Serviceörnek:


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

Ve burada jsbin

Ve işte çok basit bir Factoryörnek:


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

Ve burada jsbin


Bu çok basitse, burada daha sofistike bir örnek var

Ayrıca ilgili en iyi uygulamalar yorumları için buradaki cevaba bakın


1
Evet ben size katılıyorum. Her zaman işleri basitleştirmeye çalışın.
Evan Hu

var _dataObj = {};Doğrudan bir referans döndüğünüzde beyan etmenin anlamı nedir ? Bu özel değil . İlk örnekte bunu yapabilirsiniz this.dataObj = {};ve ikincisinde return { dataObj: {} };bu işe yaramaz bir değişken bildirimi IMHO'dur.
TJ

@TJ Mesele bu değişkeni diğer bileşenler arasında paylaşmaktır. Paylaşım kavramını gösteren temel bir örnektir. IS değişkeni blok içinde özeldir, sonra açıklayıcı deseni kullanarak genel değişken olarak ortaya koyarsınız. Bu şekilde, değişkeni tutmak ve kullanmak arasında sorumluluklar ayrılır.
Dmitri Zaitsev

@DmitriZaitsev "basit örnekler" diyorsunuz, ancak özel devletten nasıl yararlanacağınızı düzgün bir şekilde göstermezseniz, sadece insanları karıştırıyorsunuz demektir. Doğrudan bir referans döndürdüğünüz sürece, örneğinizde hiçbir özel durum yoktur.
TJ

@TJ Kafa karıştırıcı bir şey görmüyorum. Özel bir değişken bir modül tarafından gösterilebilir. Daha iyi bir cevap yazmaktan çekinmeyin.
Dmitri Zaitsev

26

--- Bu cevabın bu soru için olmadığını biliyorum, ama bu soruyu okuyan ve Fabrikalar gibi Hizmetleri ele almak isteyen insanların bunu yapmaktan kaçınmasını istiyorum ----

Bunun için bir Servis veya Fabrika kullanmanız gerekecektir.

Hizmetler, iç içe olmayan denetleyiciler arasında veri paylaşmak için EN İYİ UYGULAMADIR .

Bu konuda veri paylaşımı ile ilgili çok iyi bir açıklama nesnelerin nasıl bildirileceği. Şanssızdım çünkü okumadan önce bir AngularJS tuzağına düştüm ve çok sinirliydim. Bu sorunu önlemenize yardımcı olalım.

"Ng-book: AngularJS ile ilgili eksiksiz kitap" ı kontrolörlerde çıplak veri olarak oluşturulan AngularJS ng modellerinin YANLIŞ olduğunu okudum!

Bir $ scope öğesi şu şekilde oluşturulmalıdır:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // best practice, always use a model
  $scope.someModel = {
    someValue: 'hello computer'
  });

Ve böyle değil:

angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
  // anti-pattern, bare value
  $scope.someBareValue = 'hello computer';
  };
});

Bunun nedeni, DOM (html belgesi) için çağrıları şu şekilde içermesi önerilir (EN İYİ UYGULAMA)

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

Alt denetleyicinizin bir nesneyi üst denetleyiciden değiştirebilmesini istiyorsanız, iç içe denetleyiciler için çok yararlıdır ....

Ancak sizin durumunuzda iç içe kapsamlar istemezsiniz, ancak nesneleri hizmetlerden denetleyicilere almak için benzer bir yön vardır.

Diyelim ki hizmetiniz 'Fabrika' ve dönüş alanında objectC içeren objectB içeren bir objectA var.

Denetleyicinizden objectC'yi kapsamınıza almak istiyorsanız, aşağıdakileri söylemek yanlıştır:

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

Bu işe yaramaz ... Bunun yerine sadece bir nokta kullanın.

$scope.neededObjectInController = Factory.ObjectA;

Daha sonra DOM'da objectC'den objectC'yi çağırabilirsiniz. Bu, fabrikalarla ilgili en iyi uygulamadır ve en önemlisi, beklenmedik ve yakalanamayan hatalardan kaçınmaya yardımcı olacaktır.


2
Bunun iyi bir cevap olduğunu düşünüyorum, ancak sindirimi oldukça zor.
pspahn

17

$ RootScope kullanarak Servis oluşturmadan çözüm:

Özellikleri Uygulama Denetleyicileri arasında paylaşmak için Angular $ rootScope'u kullanabilirsiniz. Bu, verileri paylaşmak için başka bir seçenektir ve insanların bunu bilmesini sağlar.

Bazı işlevleri Kontrolörler arasında paylaşmanın tercih edilen yolu Hizmetler'dir, global bir özelliği okumak veya değiştirmek için $ rootscope kullanabilirsiniz.

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

Şablonda $ rootScope kullanma ($ root ile Access özellikleri):

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

5
AngularJS'nin çeşitli yapıları içindeki her şeyi yerel olarak kapsaması fikrinden sapan bu noktada global olarak kapsamlı değişkenler kullanıyorsunuz. Genel bir değişken dosyası eklemek aynı şeyi başarır ve değişkenin başlangıçta nerede tanımlandığını bulmayı kolaylaştırır. Her iki durumda da, önerilmedi.
Organiccat

4
@Organiccat - Endişenizi anlıyorum ve bu yüzden tercih edilen yolun hizmet olacağını zaten belirtmiştim, şüphesiz. Ama açısal da bu yolu sağlıyor. Global'inizi nasıl yönetmek istediğiniz üzerindedir. Bu yaklaşımın benim için en iyi çalıştığı bir senaryom vardı.
Sanjeev

8

Yukarıdaki örnek bir cazibe gibi çalıştı. Birden fazla değeri yönetmem gerektiğinde bir değişiklik yaptım. Umarım bu yardımcı olur!

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

    var hashtable = {};

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

1
Ayrıca, verileri farklı denetleyiciler arasında paylaşmak için bir hizmet kullanarak bir örnek oluşturdum. Umarım beğenirsiniz. jsfiddle.net/juazammo/du53553a/1
Juan Zamora

1
Çalışmasına rağmen, bu genellikle sözdizimidir .factory. .service" Docs.angularjs.org/api/auto/service/$provide#service
Dmitri Zaitsev

1
Dmitri, haklısın, ancak Açısal çocuklar benim bakış açımdan, hizmetler (cepheler) ve fabrikalar arasındaki konseptimi biraz değiştirdi .... oh iyi ....
Juan Zamora

1
Ve eğer yanlışsam beni düzeltin, hizmetler bir nesne veya değer olabilecek bir şey döndürmek için tasarlanmıştır. Fabrikalar nesne yaratmaya yöneliktir. Aslında bir şey döndüren işlevselliklerin bir koleksiyonu olan bir cephesi, hizmetleri nerede düşündüğümdür. Fabrikalardan işlevsellik çağırma dahil. Yine, bunun benim için ne olduğunu ve aslında Açısal perspektiften ne olduğunu temel kavramına giriyorum. (Abstract Factory dofactory.com/net/abstract-factory-design-pattern ) ve Adaptör yaklaşımı hizmet olarak ortaya koyacağım şey
Juan Zamora

1
Adaptör Deseni'ni
Juan Zamora

6

Değerleri kullanma eğilimindeyim, bunun neden kötü bir fikir olduğunu tartışması herkes için mutlu ..

var myApp = angular.module('myApp', []);

myApp.value('sharedProperties', {}); //set to empty object - 

Sonra değeri bir hizmete göre enjekte edin.

Ctrl1'de ayarla:

myApp.controller('ctrl1', function DemoController(sharedProperties) {
  sharedProperties.carModel = "Galaxy";
  sharedProperties.carMake = "Ford";
});

ve ctrl2'den erişim:

myApp.controller('ctrl2', function DemoController(sharedProperties) {
  this.car = sharedProperties.carModel + sharedProperties.carMake; 

});

Bunun bir hizmet kullanmaktan farkı nedir?
dopatraman

5

Aşağıdaki örnekte, kardeş denetleyicileri ve değeri değiştirdiğinde bir işlem.

Örnek olayı kullanın: kenar çubuğunda başka bir görünümün içeriğini değiştiren bir filtreniz var.

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

Denetleyiciler ve hatta direktifler arasında veri paylaşmanın önerilen yolunun, daha önce belirtildiği gibi hizmetleri (fabrikalar) kullanmak olduğunu belirterek bu soruya katkıda bulunmak istiyorum, ancak aynı zamanda bir bunun nasıl yapılacağına dair pratik bir örnek çalışma.

İşte çalışan dalgıç: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info

Öncelikle oluşturmak için hizmet senin olacak paylaşılan verileri :

app.factory('SharedService', function() {
  return {
    sharedObject: {
      value: '',
      value2: ''
    }
  };
});

Ardından, kontrol cihazlarınıza enjekte edin ve paylaşılan verileri kapsamınıza alın:

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

Bunu direktifleriniz için de yapabilirsiniz , aynı şekilde çalışır:

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

Umarım bu pratik ve temiz cevap birisine yardımcı olabilir.


3

Bunu servisler veya fabrikalarla yapabilirsiniz. Bazı temel farklılıklar için esasen aynıdırlar. Bu açıklamayı thinkster.io'da takip etmenin en kolay yolu olarak buldum . Basit, noktaya ve etkili.


1
"Bunu hizmet veya fabrikalarla yapabilirsin" - Nasıl ..? Nasıl yapılır OP OP sorduğu şeydir ... dış kaynaklara bağlantı vermek yerine stackoverflow kendisi tam bir cevap gönderin, bağlantılar fazla mesai aşağı gidebilir.
TJ

2

Mülkü, kapsamların üst öğesinin bir parçası da yapamaz mısınız?

$scope.$parent.property = somevalue;

Doğru demiyorum ama işe yarıyor.


3
Yazar bunu söyledi NOTE: These controllers are not nested inside each other.. Bunlar aynı üst öğeyi paylaşan iç içe denetleyiciler veya denetleyiciler olsaydı bu işe yarardı, ancak bunu bekleyemeyiz.
Chris Foster

2
$parentBundan kaçınılabilecekse güvenmek genellikle kötü bir uygulamadır . İyi tasarlanmış yeniden kullanılabilir bir bileşen ebeveynleri hakkında bilgi sahibi olmamalıdır.
Dmitri Zaitsev

2

Ah, başka bir alternatif olarak bu yeni şeylerden biraz var. Bu yerel depolama ve açısal çalışır. Rica ederim. (Ama gerçekten, adama teşekkürler)

https://github.com/gsklee/ngStorage

Varsayılanlarınızı tanımlayın:

$scope.$storage = $localStorage.$default({
    prop1: 'First',
    prop2: 'Second'
});

Değerlere erişin:

$scope.prop1 = $localStorage.prop1;
$scope.prop2 = $localStorage.prop2;

Değerleri saklayın

$localStorage.prop1 = $scope.prop1;
$localStorage.prop2 = $scope.prop2;

Uygulamanıza ngStorage ve kumandanıza $ localStorage enjekte etmeyi unutmayın.


1
Bu, farklı bir sorun - kalıcı depolama sorununu çözer. Söz konusu sorun için ölçeklenebilir bir çözüm değildir, çünkü yerel depolama nesnesini diğerleri arasında ad çakışması güvenlik açığıyla değiştirmek gibi yan etkilerle sızdırmaz hale getirir.
Dmitri Zaitsev

1

Bunu yapmanın iki yolu var

1) get / set servisini kullanın

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

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

1

İkinci Yaklaşım:

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>

Daha fazla ayrıntı için dalma pistonuna bakınız:

http://plnkr.co/edit/cKVsPcfs1A1Wwlud2jtO?p=preview


1
Yalnızca Ctrl2(dinleyici) öğesinin alt denetleyicisi ise çalışır Ctrl1. Kardeş kontrolörler üzerinden iletişim kurmak zorundalar $rootScope.
herzbube

0

Servis yapmak istemiyorsanız, bunu yapabilirsiniz.

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

37
Bu cevabın işe yaradığından şüphe etmiyorum, ancak bunun AngularJS'nin felsefesine karşı model / denetleyici kodunuzda hiç DOM nesnesi olmamasına dikkat etmek istiyorum.
JoeCool

3
-1 çünkü DOM üzerinden denetleyici iletişimi kötü bir uygulama.
Chris Foster

3
@ChrisFoster, bir çekiç bir "alet" olarak satıldığı için kağıt ağırlığı olarak kullanılamayacağı anlamına gelmez. Eminim orada her çerçeve veya araç için "en iyi uygulamalar" listesini "bükmek" gereken geliştiriciler bulacaksınız.
Andrei V

5
@AndreiV - Kötü benzetme, bir kırıcının kağıt ağırlığı olarak kullanılmasının bir dezavantajı yoktur. Bunun gibi kötü uygulamalar yapmak belirgin dezavantajlara sahiptir ve kolayca spagetti koduna yol açabilir. Yukarıdaki kod kırılgandır çünkü artık denetleyicinizin DOM'da nerede olduğuna bağlıdır ve test edilmesi çok zordur. Bir hizmeti kullanmak, daha iyi bir nedendir, çünkü uygulamanızı şablonunuzla ilişkilendirmez. Geliştiricilerin genellikle en iyi uygulamalar listesini bükmeleri gerektiğini kabul ediyorum, ancak daha iyi çalışan net, ortak, daha modüler bir en iyi uygulama olduğunda değil.
Chris Foster

-1

$ RootScope ve hizmetlerinin yanı sıra, paylaşılan verileri eklemek için açısalı genişletmek için temiz ve kolay bir alternatif çözüm vardır:

kontrolörlerde:

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

Bu özellikler, kapsamlardan ayrılan 'açısal' nesneye aittir ve kapsamlarda ve hizmetlerde paylaşılabilir.

Nesneyi enjekte etmek zorunda olmamanızın 1 faydası: tanımlamanızdan hemen sonra her yerden erişilebilir!


2
Bu, windownesnenin her yerinde küresel değişkenlere sahip olmak gibi ... Açısal kirletecekseniz , neden sadece devam edip pencere nesnesini kirletmeyeceksiniz ...
TJ
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.