Panggil metode dalam pengontrol direktif dari pengontrol lain

118

Saya memiliki arahan yang memiliki pengontrolnya sendiri. Lihat kode di bawah ini:

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

popdown.directive('popdown', function () {
    var PopdownController = function ($scope) {
        this.scope = $scope;
    }

    PopdownController.prototype = {
        show:function (message, type) {
            this.scope.message = message;
            this.scope.type = type;
        },

        hide:function () {
            this.scope.message = '';
            this.scope.type = '';
        }
    }

    var linkFn = function (scope, lElement, attrs, controller) {

    };

    return {
        controller: PopdownController,
        link: linkFn,
        replace: true,
        templateUrl: './partials/modules/popdown.html'
    }

});

Ini dimaksudkan sebagai sistem pemberitahuan untuk kesalahan / pemberitahuan / peringatan. Yang ingin saya lakukan adalah dari pengontrol lain (bukan pengarah) untuk memanggil fungsi showpada pengontrol ini. Dan ketika saya melakukannya, saya juga ingin fungsi tautan saya mendeteksi bahwa beberapa properti berubah dan melakukan beberapa animasi.

Berikut beberapa kode untuk menunjukkan apa yang saya minta:

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

app.controller('IndexController', function($scope, RestService) {
    var result = RestService.query();

    if(result.error) {
        popdown.notify(error.message, 'error');
    }
});

Jadi saat memanggil showpada popdownkontroler direktif, fungsi link yang juga harus dipicu dan melakukan animasi. Bagaimana saya bisa mencapai itu?

pengguna253530
sumber
Di mana Anda menempatkan panggilan ke popdownarahan di halaman - apakah hanya di satu tempat di mana pengontrol lain seharusnya memiliki akses ke sana, atau apakah ada beberapa munculan di tempat yang berbeda?
satchmorun
index.html saya memiliki ini: <div ng-view> </div> <div popdown> </div> pada dasarnya hanya ada 1 contoh popdown yang dimaksudkan untuk tersedia secara global.
pengguna253530
1
Saya pikir Anda bermaksud menulis, popdown.show(...)bukankah popdown.notify(...)itu benar? Jika tidak, fungsi notify agak membingungkan.
lanoxx
mana asalnya popdown.notify? .notifiymetode, maksud saya
Hijau

Jawaban:

167

Ini adalah pertanyaan yang menarik, dan saya mulai berpikir tentang bagaimana saya akan menerapkan sesuatu seperti ini.

Saya datang dengan ini (biola) ;

Pada dasarnya, alih-alih mencoba memanggil arahan dari pengontrol, saya membuat modul untuk menampung semua logika popdown:

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

Saya meletakkan dua hal di modul, factoryuntuk API yang dapat disuntikkan di mana saja, dan directiveuntuk menentukan perilaku elemen popdown yang sebenarnya:

Pabrik hanya menetapkan beberapa fungsi successdan errordan melacak beberapa variabel:

PopdownModule.factory('PopdownAPI', function() {
    return {
        status: null,
        message: null,
        success: function(msg) {
            this.status = 'success';
            this.message = msg;
        },
        error: function(msg) {
            this.status = 'error';
            this.message = msg;
        },
        clear: function() {
            this.status = null;
            this.message = null;
        }
    }
});

Direktif memasukkan API ke dalam pengontrolnya, dan mengawasi api untuk perubahan (saya menggunakan bootstrap css untuk kenyamanan):

PopdownModule.directive('popdown', function() {
    return {
        restrict: 'E',
        scope: {},
        replace: true,
        controller: function($scope, PopdownAPI) {
            $scope.show = false;
            $scope.api = PopdownAPI;

            $scope.$watch('api.status', toggledisplay)
            $scope.$watch('api.message', toggledisplay)

            $scope.hide = function() {
                $scope.show = false;
                $scope.api.clear();
            };

            function toggledisplay() {
                $scope.show = !!($scope.api.status && $scope.api.message);               
            }
        },
        template: '<div class="alert alert-{{api.status}}" ng-show="show">' +
                  '  <button type="button" class="close" ng-click="hide()">&times;</button>' +
                  '  {{api.message}}' +
                  '</div>'
    }
})

Kemudian saya mendefinisikan appmodul yang bergantung pada Popdown:

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

app.controller('main', function($scope, PopdownAPI) {
    $scope.success = function(msg) { PopdownAPI.success(msg); }
    $scope.error   = function(msg) { PopdownAPI.error(msg); }
});

Dan HTML-nya terlihat seperti:

<html ng-app="app">
    <body ng-controller="main">
        <popdown></popdown>
        <a class="btn" ng-click="success('I am a success!')">Succeed</a>
        <a class="btn" ng-click="error('Alas, I am a failure!')">Fail</a>
    </body>
</html>

Saya tidak yakin apakah itu benar-benar ideal, tetapi sepertinya cara yang masuk akal untuk mengatur komunikasi dengan arahan popdown global-ish.

Sekali lagi, untuk referensi, biola .

satchmorun
sumber
10
+1 Seseorang tidak boleh memanggil fungsi dalam arahan dari luar arahan - itu praktik yang buruk. Menggunakan layanan untuk mengelola status global yang membaca arahan sangat umum dan ini adalah pendekatan yang benar. Lebih banyak aplikasi termasuk antrian notifikasi dan dialog modal.
Josh David Miller
7
Jawaban yang benar-benar luar biasa! Contoh yang sangat berguna bagi kita yang berasal dari jQuery dan Backbone
Brandon
11
Dengan cara ini, apakah mungkin menggunakan modul ini untuk membuat instance beberapa direktif dalam tampilan yang sama? Bagaimana saya dapat memanggil fungsi sukses atau kesalahan dari contoh tertentu dari arahan ini?
ira
3
@ira Anda mungkin bisa mengubah pabrik untuk menyimpan peta (atau daftar) status dan objek pesan dan kemudian menggunakan atribut nama pada arahan untuk mengidentifikasi item mana dalam daftar yang Anda butuhkan. Jadi, alih-alih memanggil success(msg)html Anda akan memanggil sucess(name, msg)untuk memilih direktif dengan nama yang benar.
lanoxx
5
@JoshDavidMiller mengapa Anda menganggapnya sebagai praktik yang buruk untuk memanggil metode pada direktif? Jika sebuah direktif merangkum beberapa logika DOM sebagaimana dimaksud, tentunya wajar untuk mengekspos API sehingga pengontrol yang menggunakannya dapat menjalankan metodenya sesuai kebutuhan?
Paul Taylor
27

Anda juga dapat menggunakan acara untuk memicu Popdown.

Ini biola berdasarkan solusi satchmorun. Ini membagi dengan PopdownAPI, dan pengontrol tingkat atas sebagai gantinya $broadcast'sukses' dan 'kesalahan' kejadian di rantai lingkup:

$scope.success = function(msg) { $scope.$broadcast('success', msg); };
$scope.error   = function(msg) { $scope.$broadcast('error', msg); };

Modul Popdown kemudian mendaftarkan fungsi penangan untuk kejadian ini, misalnya:

$scope.$on('success', function(event, msg) {
    $scope.status = 'success';
    $scope.message = msg;
    $scope.toggleDisplay();
});

Ini berfungsi, setidaknya, dan menurut saya merupakan solusi yang dipisahkan dengan baik. Saya akan membiarkan orang lain ikut campur jika ini dianggap praktik yang buruk karena alasan tertentu.

Aron
sumber
1
Satu kelemahan yang dapat saya pikirkan adalah bahwa dalam jawaban yang dipilih Anda hanya perlu PopdownAPI (mudah tersedia dengan DI). Dalam hal ini Anda memerlukan akses ke cakupan pengontrol untuk menyiarkan pesan. Bagaimanapun, ini terlihat sangat ringkas.
Julian
Saya suka ini lebih baik daripada pendekatan layanan untuk kasus penggunaan sederhana karena menjaga kompleksitas tetap rendah dan masih digabungkan secara longgar
Patrick Favre
11

Anda juga dapat mengekspos controller direktif ke lingkup induk, seperti ngFormdengan nameatribut: http://docs.angularjs.org/api/ng.directive:ngForm

Di sini Anda dapat menemukan contoh yang sangat mendasar bagaimana hal itu dapat dicapai http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

Dalam contoh ini saya memiliki myDirectivepengontrol khusus dengan $clearmetode (semacam API publik yang sangat sederhana untuk arahan). Saya dapat mempublikasikan pengontrol ini ke lingkup induk dan menggunakan panggilan metode ini di luar arahan.

luacassus
sumber
Ini membutuhkan hubungan antara pengontrol, bukan? Karena OP menginginkan pusat pesan, ini mungkin tidak ideal baginya. Tapi sangat menyenangkan mempelajari pendekatan Anda juga. Ini berguna dalam banyak situasi dan, seperti yang Anda katakan, angular sendiri menggunakannya.
fasfsfgs
Saya mencoba mengikuti contoh yang diberikan oleh satchmorun. Saya membuat beberapa html pada saat runtime, tetapi saya tidak menggunakan template direktif. Saya menggunakan pengontrol direktif untuk menentukan fungsi yang akan dipanggil dari html yang ditambahkan tetapi fungsi tersebut tidak dipanggil. Pada dasarnya, saya memiliki direktif ini: directives.directive ('abcXyz', function ($ compile {return {pembatasan: 'AE', require: 'ngModel', controller: function ($ scope) {$ scope.function1 = function () {..};}, html saya adalah: "<a href="" ng-click="function1('itemtype')">
Tandai
Ini adalah satu-satunya solusi elegan yang dapat mengekspos api direktif jika direktif bukan tunggal! Saya masih kurang suka pake $scope.$parent[alias]karena baunya buat saya suka pake internal angular api. Tetapi masih tidak dapat menemukan solusi yang lebih elegan untuk perintah non-tunggal. Varian lain seperti acara penyiaran atau mendefinisikan objek kosong di pengontrol induk untuk arahan api lebih berbau.
Ruslan Stelmachenko
3

Saya mendapat solusi yang jauh lebih baik.

di sini adalah direktif saya, saya telah menyuntikkan referensi objek dalam direktif dan telah memperluasnya dengan menambahkan fungsi pemanggilan dalam kode direktif.

app.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
        /*The object that passed from the cntroller*/
        objectToInject: '=',
        },
        templateUrl: 'templates/myTemplate.html',

        link: function ($scope, element, attrs) {
            /*This method will be called whet the 'objectToInject' value is changes*/
            $scope.$watch('objectToInject', function (value) {
                /*Checking if the given value is not undefined*/
                if(value){
                $scope.Obj = value;
                    /*Injecting the Method*/
                    $scope.Obj.invoke = function(){
                        //Do something
                    }
                }    
            });
        }
    };
});

Mendeklarasikan direktif di HTML dengan parameter:

<my-directive object-to-inject="injectedObject"></ my-directive>

Kontroler saya:

app.controller("myController", ['$scope', function ($scope) {
   // object must be empty initialize,so it can be appended
    $scope.injectedObject = {};

    // now i can directly calling invoke function from here 
     $scope.injectedObject.invoke();
}];
Ashwini Jindal
sumber
Ini pada dasarnya bertentangan dengan prinsip pemisahan kepentingan. Anda memberikan ke direktif sebuah objek yang dipakai dalam sebuah controller, dan Anda mendelegasikan tanggung jawab untuk mengelola objek itu (yaitu pembuatan fungsi pemanggilan) ke direktif. Menurut saya, BUKAN solusi yang lebih baik.
Florin Vistig