Angularjs 如何在Angular controller外部删除Cordova特定事件?

Angularjs 如何在Angular controller外部删除Cordova特定事件?,angularjs,cordova,angularjs-scope,angularjs-controller,angularjs-controlleras,Angularjs,Cordova,Angularjs Scope,Angularjs Controller,Angularjs Controlleras,假设我有一个控制器,可以处理视图更改: function Controller($scope){ var viewModel = this; viewModel.goBack= function(){ viewModel.visible = visibleLinks.pop(); //get last visible link viewModel.swipeDirection = 'left';// for view change animati

假设我有一个控制器,可以处理视图更改:

function Controller($scope){
    var viewModel = this;
    viewModel.goBack= function(){
        viewModel.visible = visibleLinks.pop(); //get last visible link 
        viewModel.swipeDirection = 'left';// for view change animation
    }
}
但我不仅想用
中的HTML按钮来处理它,还想用设备上的后退按钮来处理它。因此,我必须为
deviceready
事件添加事件监听器,并显式调用
$scope.$apply()
,以便在AngularJS上下文之外调用它,如下所示:

document.addEventListener("deviceready", function(){
        document.addEventListener("backbutton", function(){
             viewModel.goBack();
             $scope.$apply();
         }, false);
    }, false);
 }

但我还想遵循(相对:)新的
controllerAs
语法,因为这是现在推荐的,例如Todd的座右铭:当不使用
$emit
$on
时,它允许从控制器中删除
$scope
。但我不能这样做,如果我必须调用
$apply()
,因为当用户单击设备后退按钮时,我的上下文不是角度上下文。我想创建一个
服务
,它可以作为cordova的包装外观,并将
$scope
注入该服务,但正如我在这里读到的:这是不可能的。我看到了这一点:接受的解决方案还包含
$apply()
,这使得
$scope
不可移动。有谁知道在Angular controller之外删除Cordova特定事件的解决方案,以便在不需要明确说明时从控制器中删除
$scope
?提前谢谢。

在我的例子中,我只是在
$rootScope
上通过角度触发来转发Cordova事件。基本上,任何应用程序控制器都将接收此自定义事件。在初始化任何控制器之前,侦听器都会连接到配置阶段的
run
块中。以下是一个例子:

angular
.module('app', [])
.run(function ($rootScope, $document) {

    $document.on('backbutton', function (e) {
        // block original system back button behavior for the entire application
        e.preventDefault();
        e.stopPropagation();

        // forward the event
        $rootScope.$broadcast('SYSTEM_BACKBUTTON', e);
    });

})
.controller('AppCtrl', function ($scope) {

    $scope.$on('SYSTEM_BACKBUTTON', function () {
        // do stuff
       viewModel.goBack();
    });

});
显然,在
$scope.$处理程序中,您不必调用
$scope.$apply()

此解决方案的优点是:

  • 在事件广播到所有控制器之前,您可以修改事件或对整个应用程序执行其他操作
  • 每次实例化控制器时使用
    $document.on()
    时,事件处理程序将保留在内存中,除非手动从该事件中取消sibscribe;使用
    $scope.$on
    自动关心它
  • 如果系统分派Cordova事件的方式发生变化,您必须在一个地方进行更改
缺点:

  • 在继承已在初始化阶段附加了事件处理程序的控制器时,如果希望在子级中包含自己的处理程序,则必须小心
在哪里放置侦听器和转发器取决于您,这在很大程度上取决于您的应用程序结构。如果你的应用程序允许,你甚至可以在
run
块中保留
backbutton
事件的所有逻辑,并在控制器中删除它。另一种组织方法是指定一个附加到
$rootScope
的全局回调,例如,如果控制器中的“后退”按钮具有不同的行为,则可以在控制器内重写该回调,以避免干扰事件

不过,我不确定是否有
devicerady
事件,它从一开始就触发一次。在我的例子中,我首先等待触发
deviceready
事件,然后手动引导AngularJS应用程序,以提供应用程序的顺序加载并防止任何冲突:

document.addEventListener('deviceready', function onDeviceReady() {
    angular.element(document).ready(function () {
        angular.bootstrap(document.body, ['app']);
    });
}, false);

从我的观点来看,应用程序的逻辑和引导方式应该彼此分离。这就是为什么我将
backbutton
的侦听器移动到
run
块。

我看不出为什么要从控制器中删除$scope。遵循最佳实践并在不需要时删除它是可以的,但正如您所说,您仍然需要$emit、$on、$watch。。您可以在列表中添加$apply()

我在这里建议的另一种解决方案是实现一个helper函数来处理这个问题。我们可以将它放在一个服务中,并使用$rootScope服务,它是可注入的

app.factory('utilService', function ($rootScope) {

    return {
        justApply: function () {
            $rootScope.$apply();
        },
        createNgAware: function (fnCallback) {
            return function () {
                fnCallback.apply(this, arguments);
                $rootScope.$apply();
            };
        }
    };
}); 
// use it   
app.controller('SampleCtrl', function(utilService) {

    var backBtnHandler1 = function () {
        viewModel.goBack();
        utilService.justApply(); // instead of $scope.$apply();
    }
    // or
    var backBtnHandler2 = utilService.createNgAware(function(){ 
        viewModel.goBack();
    });
    document.addEventListener("backbutton", backBtnHandler2, false);
});