วิธีการโทรในตัวควบคุมคำสั่งจากตัวควบคุมอื่น ๆ


118

ฉันมีคำสั่งที่มีตัวควบคุมของตัวเอง ดูรหัสด้านล่าง:

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

});

นี่คือระบบแจ้งเตือนข้อผิดพลาด / การแจ้งเตือน / คำเตือน สิ่งที่ฉันต้องการทำคือจากคอนโทรลเลอร์อื่น (ไม่ใช่คำสั่ง) เพื่อเรียกใช้ฟังก์ชันshowบนคอนโทรลเลอร์นี้ และเมื่อฉันทำเช่นนั้นฉันก็ต้องการให้ฟังก์ชันลิงก์ตรวจพบว่าคุณสมบัติบางอย่างเปลี่ยนแปลงไปและแสดงภาพเคลื่อนไหวบางอย่าง

นี่คือรหัสบางส่วนเพื่อเป็นตัวอย่างสิ่งที่ฉันขอ:

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

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

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

ดังนั้นเมื่อเรียกshowใช้popdownตัวควบคุมคำสั่งควรเรียกใช้ฟังก์ชันลิงก์และแสดงภาพเคลื่อนไหวด้วย ฉันจะบรรลุเป้าหมายนั้นได้อย่างไร


คุณวางสายไปยังpopdownคำสั่งบนหน้าที่ไหน - อยู่ในที่เดียวที่ตัวควบคุมอื่น ๆ ควรจะเข้าถึงทั้งหมดหรือมีป๊อปดาวน์หลายตัวในที่ต่างๆ?
satchmorun

index.html ของฉันมีสิ่งนี้: <div ng-view> </div> <div popdown> </div> โดยทั่วไปมีเพียง 1 อินสแตนซ์แบบป๊อปดาวน์ตามที่ตั้งใจไว้ว่าจะพร้อมใช้งานทั่วโลก
user253530

1
ฉันคิดว่าคุณตั้งใจจะเขียนpopdown.show(...)แทนที่จะpopdown.notify(...)เป็นอย่างนั้นใช่ไหม มิฉะนั้นฟังก์ชันการแจ้งเตือนจะทำให้เกิดความสับสน
lanoxx

มันมาจากpopdown.notifyไหน? .notifiyวิธีการฉันหมายถึง
กรีน

คำตอบ:


167

นี่เป็นคำถามที่น่าสนใจและฉันเริ่มคิดว่าจะนำสิ่งนี้ไปใช้อย่างไร

ฉันมากับสิ่งนี้ (ซอ) ;

โดยทั่วไปแทนที่จะพยายามเรียกคำสั่งจากคอนโทรลเลอร์ฉันได้สร้างโมดูลขึ้นมาเพื่อใช้ลอจิกป๊อปดาวน์ทั้งหมด:

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

ฉันใส่สองสิ่งในโมดูลfactoryสำหรับ API ซึ่งสามารถฉีดได้ทุกที่และไฟล์directiveสำหรับกำหนดลักษณะการทำงานขององค์ประกอบป๊อปดาวน์จริง:

โรงงานเพียงกำหนดฟังก์ชันสองสามอย่างsuccessและerrorติดตามตัวแปรสองสามตัว:

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

คำสั่งทำให้ API ถูกฉีดเข้าไปในคอนโทรลเลอร์และเฝ้าดู api สำหรับการเปลี่ยนแปลง (ฉันใช้ bootstrap css เพื่อความสะดวก):

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

จากนั้นฉันจะกำหนดappโมดูลที่ขึ้นอยู่กับ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); }
});

และ HTML ดูเหมือนว่า:

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

ฉันไม่แน่ใจว่ามันเหมาะอย่างยิ่งหรือไม่ แต่ดูเหมือนเป็นวิธีที่สมเหตุสมผลในการตั้งค่าการสื่อสารด้วยคำสั่ง global-ish popdown

อีกครั้งสำหรับการอ้างอิง, ซอ


10
+1 ไม่ควรเรียกใช้ฟังก์ชันในคำสั่งจากภายนอกคำสั่ง - เป็นการปฏิบัติที่ไม่ดี การใช้บริการเพื่อจัดการสถานะทั่วโลกที่คำสั่งอ่านเป็นเรื่องธรรมดามากและนี่เป็นแนวทางที่ถูกต้อง แอปพลิเคชั่นเพิ่มเติม ได้แก่ คิวการแจ้งเตือนและกล่องโต้ตอบโมดอล
Josh David Miller

7
คำตอบที่ยอดเยี่ยมจริงๆ! ตัวอย่างที่เป็นประโยชน์สำหรับพวกเราที่มาจาก jQuery และ Backbone
Brandon

11
ด้วยวิธีนี้เป็นไปได้หรือไม่ที่จะใช้โมดูลนี้เพื่อสร้างอินสแตนซ์คำสั่งหลายคำสั่งในมุมมองเดียวกัน ฉันจะเรียกฟังก์ชันความสำเร็จหรือข้อผิดพลาดของอินสแตนซ์เฉพาะของคำสั่งนี้ได้อย่างไร
ira

3
@ira คุณอาจเปลี่ยนโรงงานเพื่อเก็บแผนที่ (หรือรายการ) ของสถานะและวัตถุข้อความจากนั้นใช้แอตทริบิวต์ชื่อในคำสั่งเพื่อระบุว่ารายการใดในรายการที่คุณต้องการ ดังนั้นแทนที่จะเรียกsuccess(msg)ใน html คุณจะเรียกsucess(name, msg)เพื่อเลือกคำสั่งด้วยชื่อที่ถูกต้อง
lanoxx

5
@JoshDavidMiller ทำไมคุณถึงคิดว่ามันเป็นวิธีปฏิบัติที่ไม่ดีในการเรียกเมธอดในคำสั่ง? หากคำสั่งห่อหุ้มลอจิก DOM บางอย่างตามที่ตั้งใจไว้แน่นอนว่ามันเป็นเรื่องธรรมดาที่จะเปิดเผย API เพื่อให้คอนโทรลเลอร์ที่ใช้มันสามารถเรียกใช้เมธอดได้ตามต้องการ?
Paul Taylor

27

คุณยังสามารถใช้เหตุการณ์เพื่อเรียกป๊อปดาวน์

นี่คือซอที่ใช้วิธีแก้ปัญหาของ satchmorun มันจ่ายด้วย PopdownAPI และตัวควบคุมระดับบนสุดจะแทนที่$broadcastเหตุการณ์ 'ความสำเร็จ' และ 'ข้อผิดพลาด' ในห่วงโซ่ขอบเขต:

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

โมดูล Popdown จะลงทะเบียนฟังก์ชันตัวจัดการสำหรับเหตุการณ์เหล่านี้เช่น:

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

อย่างน้อยก็ใช้งานได้และดูเหมือนว่าฉันจะเป็นโซลูชันที่แยกออกจากกันได้อย่างดี ฉันจะปล่อยให้คนอื่นตีระฆังถ้านี่ถือเป็นการปฏิบัติที่ไม่ดีด้วยเหตุผลบางประการ


1
ข้อเสียเปรียบอย่างหนึ่งที่ฉันคิดได้คือในคำตอบที่เลือกคุณต้องใช้ PopdownAPI เท่านั้น (ใช้ได้อย่างง่ายดายกับ DI) ในสิ่งนี้คุณต้องเข้าถึงขอบเขตของคอนโทรลเลอร์เพื่อถ่ายทอดข้อความ ยังไงก็ดูรวบรัดดี
Julian

ฉันชอบวิธีนี้ดีกว่าวิธีการบริการสำหรับกรณีการใช้งานที่เรียบง่ายเนื่องจากช่วยลดความซับซ้อนและยังคงอยู่คู่กันอย่างหลวม ๆ
Patrick Favre

11

คุณยังสามารถเปิดเผยตัวควบคุมของคำสั่งไปยังขอบเขตหลักได้เช่นเดียวngFormกับnameแอตทริบิวต์: http://docs.angularjs.org/api/ng.directive:ngForm

คุณสามารถดูตัวอย่างพื้นฐานได้ที่นี่http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

ในตัวอย่างนี้ฉันมีmyDirectiveตัวควบคุมเฉพาะพร้อม$clearวิธีการ (ประเภทของ API สาธารณะที่ง่ายมากสำหรับคำสั่ง) ฉันสามารถเผยแพร่ตัวควบคุมนี้ไปยังขอบเขตหลักและใช้เรียกวิธีนี้นอกคำสั่ง


สิ่งนี้ต้องการความสัมพันธ์ระหว่างคอนโทรลเลอร์ใช่ไหม? เนื่องจาก OP ต้องการศูนย์ข้อความสิ่งนี้อาจไม่เหมาะสำหรับเขา แต่ก็ดีมากที่ได้เรียนรู้แนวทางของคุณเช่นกัน มันมีประโยชน์ในหลาย ๆ สถานการณ์และอย่างที่คุณพูดเชิงมุมเองก็ใช้มัน
fasfsfgs

ฉันพยายามทำตามตัวอย่างที่ให้ไว้โดย satchmorun ฉันกำลังสร้าง html ที่รันไทม์ แต่ฉันไม่ได้ใช้เทมเพลตของคำสั่ง ฉันใช้ตัวควบคุมของคำสั่งเพื่อระบุฟังก์ชันที่จะเรียกจาก html ที่เพิ่มเข้ามา แต่ฟังก์ชันไม่ได้รับการเรียกใช้ โดยทั่วไปฉันมีคำสั่งนี้: directives.directive ('abcXyz', function ($ compile {return {restrict: 'AE', require: 'ngModel', controller: function ($ scope) {$ scope.function1 = function () {.. };} html ของฉันคือ: "<a href="" ng-click="function1('itemtype')">
ทำเครื่องหมาย

นี่เป็นวิธีแก้ปัญหาที่สวยงามเพียงอย่างเดียวที่สามารถเปิดเผย api คำสั่งได้หากคำสั่งไม่ใช่ซิงเกิลตัน! ฉันยังไม่ชอบใช้$scope.$parent[alias]เพราะมันมีกลิ่นสำหรับฉันเหมือนใช้ angular api ภายใน แต่ยังไม่สามารถหาทางออกที่หรูหรากว่านี้สำหรับคำสั่งที่ไม่ใช่คนโสด ตัวแปรอื่น ๆ เช่นการออกอากาศเหตุการณ์หรือกำหนดอ็อบเจ็กต์ว่างในตัวควบคุมหลักเพื่อให้ API มีกลิ่นมากยิ่งขึ้น
Ruslan Stelmachenko

3

ฉันมีทางออกที่ดีกว่ามาก

นี่คือคำสั่งของฉันฉันได้ฉีดในการอ้างอิงวัตถุในคำสั่งและขยายโดยการเพิ่มฟังก์ชันเรียกใช้ในรหัสคำสั่ง

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

การประกาศคำสั่งใน HTML ด้วยพารามิเตอร์:

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

ตัวควบคุมของฉัน:

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

โดยพื้นฐานแล้วสิ่งนี้ขัดต่อหลักการแยกข้อกังวล คุณให้คำสั่งวัตถุที่สร้างอินสแตนซ์ในคอนโทรลเลอร์และคุณมอบหมายความรับผิดชอบในการจัดการวัตถุนั้น (เช่นการสร้างฟังก์ชันเรียกใช้) ให้กับคำสั่ง ในความคิดของฉันไม่ใช่ทางออกที่ดีกว่า
Florin Vistig
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.