AngularJS: Bagaimana saya bisa meneruskan variabel antara pengontrol?

326

Saya memiliki dua pengontrol sudut:

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
}

Saya tidak dapat menggunakan Ctrl1di dalam Ctrl2karena tidak terdefinisi. Namun jika saya mencoba untuk meneruskannya seperti itu ...

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

Saya mendapatkan kesalahan. Adakah yang tahu bagaimana melakukan ini?

Perbuatan

Ctrl2.prototype = new Ctrl1();

Juga gagal.

CATATAN: Kontroler ini tidak saling bersarang.

dopatraman
sumber
Ada banyak cara tetapi cara terbaik adalah menonton sudut. Selalu ketika kita menggunakan kerangka kerja adalah cara terbaik untuk menggunakan metode sendiri untuk melakukan pekerjaan jangan lupa ini
pejman
Saya menemukan blog ini sangat membantu Blog
Black Mamba

Jawaban:

503

Salah satu cara untuk membagikan variabel di beberapa pengontrol adalah dengan membuat layanan dan menyuntikkannya ke pengontrol mana pun yang Anda inginkan.

Contoh layanan sederhana:

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

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

Menggunakan layanan di pengontrol:

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

Ini dijelaskan dengan sangat baik di blog ini (Pelajaran 2 dan khususnya).

Saya telah menemukan bahwa jika Anda ingin mengikat properti ini di banyak pengontrol, ia berfungsi lebih baik jika Anda mengikat ke properti objek daripada tipe primitif (boolean, string, angka) untuk mempertahankan referensi terikat.

Contoh: var property = { Property1: 'First' };bukannya var property = 'First';.


UPDATE: Untuk (mudah-mudahan) membuat semuanya lebih jelas di sini adalah biola yang menunjukkan contoh:

  • Mengikat salinan statis dari nilai bersama (di myController1)
    • Mengikat ke primitif (string)
    • Mengikat ke properti objek (disimpan ke variabel lingkup)
  • Mengikat nilai bersama yang memperbarui UI saat nilai diperbarui (di myController2)
    • Mengikat ke fungsi yang mengembalikan primitif (string)
    • Mengikat ke properti objek
    • Mengikat dua arah ke properti objek
Gloopy
sumber
5
Dalam hal ini - bagaimana ruang lingkup Ctrl2 "tahu" ketika sharedProperties.getProperty () mengubah nilai?
OpherV
5
Jika Anda ingin UI Anda memperbarui setiap kali properti berubah, Anda dapat mengubahnya bothmenjadi fungsi dan itu akan dipanggil / dievaluasi kembali selama proses digest sudut. Lihat biola ini sebagai contoh. Juga jika Anda mengikat properti objek, Anda dapat menggunakannya secara langsung dalam tampilan Anda dan itu akan diperbarui karena data diubah mirip dengan contoh ini .
Gloopy
11
Jika Anda ingin mendeteksi dan bereaksi terhadap perubahan pada controller Anda, opsi adalah menambahkan getProperty()fungsi ke lingkup dan menggunakan $ scope. $ Tonton seperti dalam contoh ini . Semoga contoh-contoh ini membantu!
Gloopy
1
Ada masalah di sini karena layanan harus stateless. Menyimpan properti di dalam layanan adalah salah (tapi nyaman). Saya mulai menggunakan $ cacheFactory untuk membaca dan menulis data. Saya menggunakan layanan yang hampir sama dengan Gloopy tetapi alih-alih menyimpan status dalam layanan, sekarang dalam cache. Pertama-tama buat layanan cache: angular.module ('CacheService', ['ng']) .factory ('CacheService', fungsi ($ cacheFactory) {return $ cacheFactory ('CacheService');}); Sertakan di dalam app.js Anda, masukkan dalam layanan, gunakan seperti ini: return CacheService.get (kunci); atau CacheService.put (kunci, nilai);
Jordan Papaleo
4
Mencoba memahami bagaimana dan mengapa jawaban ini menggunakan .servicealih-alih .factoryseperti yang dijelaskan dalam dokumen Angular. Mengapa jawaban ini dipilih begitu tinggi ketika dokumentasi menggunakan metode yang berbeda?
pspahn
44

Saya suka menggambarkan hal-hal sederhana dengan contoh sederhana :)

Ini adalah contoh yang sangat sederhana Service:


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

Dan di sini jsbin

Dan ini adalah contoh yang sangat sederhana Factory:


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

Dan di sini jsbin


Jika itu terlalu sederhana, berikut ini contoh yang lebih canggih

Lihat juga jawabannya di sini untuk komentar praktik terbaik terkait

Dmitri Zaitsev
sumber
1
ya aku setuju denganmu. Selalu berusaha membuat hal-hal sederhana.
Evan Hu
Apa gunanya menyatakan var _dataObj = {};ketika Anda mengembalikan referensi langsung ke sana ..? Itu tidak pribadi . Dalam contoh pertama yang dapat Anda lakukan this.dataObj = {};dan yang kedua return { dataObj: {} };itu adalah variabel deklarasi IMHO tidak berguna.
TJ
@TJ Intinya adalah untuk membagikan variabel ini di antara komponen lainnya. Ini adalah contoh dasar yang menggambarkan konsep berbagi. Variabel IS pribadi di dalam blok, lalu Anda mengeksposnya sebagai variabel publik menggunakan pola pengungkapan. Dengan cara ini ada pemisahan tanggung jawab antara memegang variabel dan menggunakannya.
Dmitri Zaitsev
@DmitriZaitsev Anda mengatakan "contoh sederhana" tetapi kecuali jika Anda menunjukkan dengan benar bagaimana memanfaatkan negara pribadi, Anda hanya membingungkan orang. Tidak ada keadaan pribadi dalam contoh Anda selama Anda mengembalikan referensi langsung.
TJ
@ TT Saya tidak melihat sesuatu yang membingungkan. Variabel pribadi dapat diekspos oleh modul. Jangan ragu untuk menulis jawaban yang lebih baik.
Dmitri Zaitsev
26

--- Saya tahu jawaban ini bukan untuk pertanyaan ini, tetapi saya ingin orang yang membaca pertanyaan ini dan ingin menangani Layanan seperti Pabrik untuk menghindari kesulitan melakukan hal ini ----

Untuk ini, Anda perlu menggunakan Layanan atau Pabrik.

Layanan adalah PRAKTEK TERBAIK untuk berbagi data antara pengontrol yang tidak bersarang.

Anotasi yang sangat bagus tentang topik ini tentang berbagi data adalah cara mendeklarasikan objek. Saya tidak beruntung karena saya jatuh dalam perangkap Angular sebelum saya membacanya, dan saya sangat frustrasi. Jadi izinkan saya membantu Anda menghindari masalah ini.

Saya membaca dari "buku-ng: Buku lengkap tentang AngularJS" bahwa model-AngularJS yang dibuat dalam pengontrol karena data kosong SALAH!

Elemen $ scope harus dibuat seperti ini:

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

Dan tidak seperti ini:

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

Ini karena direkomendasikan (PRAKTEK TERBAIK) agar DOM (dokumen html) mengandung panggilan sebagai

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

Ini sangat membantu bagi pengendali bersarang jika Anda ingin pengendali anak Anda dapat mengubah objek dari pengendali induk ....

Tetapi dalam kasus Anda, Anda tidak ingin cakupan bersarang, tetapi ada aspek serupa untuk mendapatkan objek dari layanan ke pengendali.

Katakanlah Anda memiliki layanan Anda 'Pabrik' dan di ruang kembali ada objectA yang berisi objectB yang berisi objectC.

Jika dari controller Anda ingin MENDAPATKAN objectC ke dalam lingkup Anda, adalah kesalahan untuk mengatakan:

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

Itu tidak akan berhasil ... Alih-alih hanya menggunakan satu titik.

$scope.neededObjectInController = Factory.ObjectA;

Kemudian, di DOM Anda dapat memanggil objectC dari objectA. Ini adalah praktik terbaik terkait dengan pabrik, dan yang paling penting, ini akan membantu untuk menghindari kesalahan yang tidak terduga dan tidak dapat ditangkap.

AFP_555
sumber
2
Saya pikir ini adalah jawaban yang bagus, tetapi cukup sulit untuk dicerna.
pspahn
17

Solusi tanpa membuat Layanan, menggunakan $ rootScope:

Untuk berbagi properti di Pengontrol aplikasi, Anda dapat menggunakan Angular $ rootScope. Ini adalah pilihan lain untuk berbagi data, menaruhnya agar orang tahu tentangnya.

Cara yang disukai untuk berbagi beberapa fungsionalitas di Pengontrol adalah Layanan, untuk membaca atau mengubah properti global yang dapat Anda gunakan $ 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;
}]);

Menggunakan $ rootScope dalam templat (Mengakses properti dengan $ root):

<div ng-controller="Ctrl1">
    <div class="banner" ng-show="$root.showBanner"> </div>
</div>
Sanjeev
sumber
5
Anda menggunakan variabel cakupan global pada titik itu yang menyimpang dari gagasan AngularJS untuk melakukan pelingkupan secara lokal segala sesuatu dalam berbagai strukturnya. Menambahkan file variabel global akan mencapai hal yang sama dan membuatnya lebih mudah untuk menemukan di mana variabel tersebut awalnya didefinisikan. Either way, tidak disarankan.
Organiccat
4
@Organiccat - Saya mengerti kekhawatiran Anda dan itulah mengapa saya telah menyebutkan bahwa cara yang disukai adalah layanan, tidak diragukan lagi. Tapi ya sudut menyediakan cara ini juga. Terserah Anda bagaimana Anda ingin mengelola global Anda. Saya memiliki skenario di mana pendekatan ini bekerja paling baik untuk saya.
Sanjeev
8

Sampel di atas bekerja seperti pesona. Saya baru saja melakukan modifikasi kalau-kalau saya perlu mengelola beberapa nilai. Saya harap ini membantu!

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

    var hashtable = {};

    return {
        setValue: function (key, value) {
            hashtable[key] = value;
        },
        getValue: function (key) {
            return hashtable[key];
        }
    }
});
Juan Zamora
sumber
1
Saya juga membuat sampel menggunakan layanan untuk berbagi data di berbagai pengontrol yang berbeda. Saya harap kalian menyukainya. jsfiddle.net/juazammo/du53553a/1
Juan Zamora
1
Meskipun berfungsi, ini biasanya sintaks untuk .factory. A .serviceharus digunakan "jika Anda mendefinisikan layanan Anda sebagai tipe / kelas" sesuai docs.angularjs.org/api/auto/service/$provide#service
Dmitri Zaitsev
1
Dmitri, Anda benar, namun orang-orang Angular dari sudut pandang saya, hanya mengubah sedikit konsep yang saya miliki antara layanan (fasad) dan pabrik .... oh well ....
Juan Zamora
1
Dan benar saya jika saya salah, layanan dimaksudkan untuk mengembalikan sesuatu yang bisa menjadi objek atau nilai. Pabrik dimaksudkan untuk membuat objek. Wajah yang sebenarnya adalah kumpulan fungsi yang mengembalikan sesuatu, adalah apa yang saya pikir layanan mana. Termasuk memohon fungsi dari pabrik. Sekali lagi, saya masuk ke gagasan dasar tentang apa ini untuk saya dan bukan apa sebenarnya dari sudut pandang Angular. (Abstrak Factory dofactory.com/net/abstract-factory-design-pattern ) dan pendekatan Adaptor adalah apa yang akan saya paparkan sebagai layanan
Juan Zamora
1
Periksa Pola Adaptor di sini dofactory.com/net/adapter-design-pattern
Juan Zamora
6

Saya cenderung menggunakan nilai-nilai, senang bagi siapa saja untuk membahas mengapa ini adalah ide yang buruk ..

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

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

Kemudian menyuntikkan nilai sesuai layanan.

Diatur dalam ctrl1:

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

dan akses dari ctrl2:

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

});
Api dingin
sumber
apa bedanya dengan menggunakan layanan?
dopatraman
5

Contoh berikut menunjukkan bagaimana cara mengirimkan variabel antara pengontrol saudara kandung dan mengambil tindakan saat nilainya berubah.

Contoh penggunaan kasus: Anda memiliki filter di bilah sisi yang mengubah konten tampilan lain.

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>

Zanon
sumber
4

Saya ingin berkontribusi pada pertanyaan ini dengan menunjukkan bahwa cara yang disarankan untuk berbagi data antara pengontrol, dan bahkan arahan, adalah dengan menggunakan layanan (pabrik) seperti yang telah ditunjukkan, tetapi juga saya ingin memberikan Contoh praktis bagaimana cara kerja yang harus dilakukan.

Berikut adalah plunker yang berfungsi: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m?p=info

Pertama, buat layanan Anda , yang akan memiliki data Anda bersama :

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

Kemudian, cukup suntikkan pada pengontrol Anda dan ambil data yang dibagikan di lingkup Anda:

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

Anda juga dapat melakukan itu untuk arahan Anda , itu bekerja dengan cara yang sama:

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

Semoga jawaban yang praktis dan bersih ini dapat bermanfaat bagi seseorang.

Fedaykin
sumber
3

Anda dapat melakukannya dengan layanan atau pabrik. Mereka pada dasarnya sama untuk beberapa perbedaan inti. Saya menemukan penjelasan ini di thinkster.io sebagai yang paling mudah diikuti. Sederhana, to the point dan efektif.

Noahdecoco
sumber
1
"Anda bisa melakukannya dengan layanan atau pabrik" - Bagaimana ..? Bagaimana melakukannya adalah apa yang diminta OP ... tolong kirimkan jawaban lengkap di stackoverflow itu sendiri daripada menautkan ke sumber daya eksternal, tautan mungkin turun lembur.
TJ
2

Tidak bisakah Anda juga menjadikan properti bagian dari lingkup induk?

$scope.$parent.property = somevalue;

Saya tidak mengatakan itu benar tetapi berhasil.

SideFX
sumber
3
Penulis menyatakan itu NOTE: These controllers are not nested inside each other.. Jika ini adalah pengontrol bersarang atau pengontrol yang berbagi induk yang sama ini akan berfungsi, tetapi kami tidak dapat mengharapkan itu.
Chris Foster
2
Biasanya merupakan praktik yang buruk $parentjika hal itu dapat dihindari. Komponen yang dapat digunakan kembali yang dirancang dengan baik tidak boleh tahu tentang orang tuanya.
Dmitri Zaitsev
2

Ah, punya sedikit barang baru ini sebagai alternatif lain. Ini penyimpanan lokal, dan bekerja di mana sudut bekerja. Sama-sama. (Tapi sungguh, terima kasih padanya)

https://github.com/gsklee/ngStorage

Tetapkan standar Anda:

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

Akses nilai-nilai:

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

Simpan nilainya

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

Ingatlah untuk menyuntikkan ngStorage di aplikasi Anda dan $ localStorage di controller Anda.

kJamesy
sumber
1
Ini memecahkan masalah yang berbeda - penyimpanan persisten. Ini bukan solusi scalable untuk masalah yang bersangkutan karena membuat kode Anda bocor dengan efek samping seperti memodifikasi objek penyimpanan lokal dengan kerentanan nama bentrokan antara lain.
Dmitri Zaitsev
1

Ada dua cara untuk melakukan ini

1) Gunakan layanan get / set

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

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

Pendekatan Kedua:

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>

Untuk lebih jelasnya lihat plunker:

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

Codiee
sumber
1
Hanya berfungsi jika Ctrl2(pendengar) adalah pengendali anak dari Ctrl1. Pengendali saudara harus berkomunikasi via $rootScope.
herzbube
0

Jika Anda tidak ingin membuat layanan maka Anda dapat melakukannya seperti ini.

var scope = angular.element("#another ctrl scope element id.").scope();
scope.plean_assign = some_value;
terima kasihcatatan
sumber
37
Saya tidak meragukan jawaban ini berhasil, tetapi saya ingin mencatat bahwa ini bertentangan dengan filosofi AngularJS untuk tidak pernah memiliki objek DOM dalam model / kode pengontrol Anda.
JoeCool
3
-1 karena komunikasi pengontrol melalui DOM adalah praktik yang buruk, menurut pendapat saya.
Chris Foster
3
@ ChrisFoster, hanya karena palu dijual sebagai "alat", itu tidak berarti itu tidak dapat digunakan sebagai berat kertas. Saya yakin bahwa untuk setiap kerangka kerja atau alat di luar sana Anda akan selalu menemukan pengembang yang perlu "membengkokkan" daftar "praktik terbaik".
Andrei V
5
@AndreiV - Analogi yang buruk, tidak ada kerugian untuk menggunakan palu sebagai berat kertas. Melakukan praktik buruk seperti ini memiliki kelemahan yang jelas dan dapat dengan mudah menyebabkan kode spageti. Kode di atas rapuh karena sekarang tergantung di mana controller Anda berada di DOM dan sangat sulit untuk diuji. Menggunakan layanan adalah praktik yang lebih baik karena suatu alasan, karena itu tidak mengikat implementasi Anda dengan templat Anda. Saya setuju pengembang sering perlu membengkokkan daftar praktik terbaik, tetapi tidak ketika ada praktik terbaik yang jelas, umum, lebih modular yang bekerja lebih baik.
Chris Foster
-1

Selain $ rootScope dan layanan, ada solusi alternatif yang bersih dan mudah untuk memperluas sudut untuk menambahkan data bersama:

di controller:

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

Properti ini milik objek 'sudut', dipisahkan dari ruang lingkup, dan dapat dibagi dalam ruang lingkup dan layanan.

Satu manfaat dari itu adalah Anda tidak harus menyuntikkan objek: mereka dapat diakses di mana saja segera setelah defination Anda!

williamjxj
sumber
2
Ini seperti memiliki variabel global di seluruh windowobjek ... Jika Anda akan mencemari sudut, mengapa tidak pergi saja dan mencemari objek jendela ...
TJ