【问题标题】:AngularJS Informer serviceAngularJS 告密者服务
【发布时间】:2013-01-25 23:46:26
【问题描述】:

我在我的 web 应用程序中使用 Twitter Bootstrap for UI。特别是它的Alert 组件。我想编写一个简单的角度服务来包装 Bootstrap 的警报,以便有可能通知用户任何角度代码的和平。像这样:

Informer.inform("message", "ERROR"); // will result in alerting with `alert-error` class
Informer.inform("message", "INFO"); // will result in alerting with `alert-info` class

我的想法是将模板附加到<body> 的末尾:

<div class="alert {{alertClass}} fade in informer" id="informer">
    <button type="button" class="close" data-dismiss="alert">×</button>
    <div class="valignCenterWrapper">
        <div class="valignCenter" id="informerMessage">
            {{message}}
        </div>
    </div>
</div>

类似这样的:

grfx.factory("Informer", function() {
    return {
        inform : function(message, type) {
            // Here be dragons. How can I compile/append the template.

            $("#inform").alert();
        }
    };
});

我唯一想知道的是:我如何用 Angular 而不是 jQuery 编写这个?上面的代码适合开始吗?互联网上的人说我应该使用指令来操作 DOM。但我不明白:我没有任何现有的标记来应用指令。由于某些计算/用户交互,警报将附加到页面。我应该使用哪些服务($compile$parse$document)来编译模板并将其附加到正文的某个位置?

编辑:是否也可以在控制器之外获取 angularjs 服务。只是在普通的 JS 代码中,所以我可以写 getServiece("Informer").inform("", "")?

编辑 2:好的,我现在拥有的:

grfx.factory("Informer", function($compile, $rootScope) {
    return {
        inform : function(message, type) {
            var scope = $rootScope.$new();

            scope.message = message;
            scope.type = type;

            $(document.body).append($compile("<div class='alert {{type}} fade in informer' id='informer'><button type='button' class='close' data-dismiss='alert'>×</button><div class='valignCenterWrapper'><div class='valignCenter' id='informerMessage'>{{message}}</div></div></div>")(scope));
        }
    };
});

通过此代码,我可以使用来自控制器的注入服务。但是当我尝试在角度代码之外调用服务时出现问题:

angular.element(document).injector().get("Informer").inform("Message", "alert-error");

这显示带有{{message}} 的弹出窗口,例如它没有正确编译模板。

【问题讨论】:

    标签: jquery dom angularjs dom-manipulation angularjs-service


    【解决方案1】:

    在 AngularJS 中,我们应该专注于模型操作,您的 Informer 服务也不例外 - 它应该只保存模型,而不应该关注 DOM 操作。 DOM操作=指令的经验法则是一个非常好的规则,如果您遵循它,它将为您省去很多麻烦。

    回到您手头的问题,解决方案是提供专注于模型操作的服务和显示此模型的指令。让我们从服务开始:

    app.factory('Informer', function(){
    
      var messages = [];  
      var Informer = {};
    
      Informer.inform = function(msg, type) {
        messages.push({
          msg: msg,
          type: type
        });
      };
    
      Informer.allInfos = function() {
        return messages;
      };
    
      Informer.remove = function(info) {
        messages.splice(messages.indexOf(info), 1);
      };  
    
      return Informer;
    });
    

    当此服务准备就绪后,您可以轻松地在控制器中使用它(甚至在其他服务中!):

    app.controller('MainCtrl', function($scope, Informer) {
    
      Informer.inform("error message", "error");
      Informer.inform("info message", "info");
    
      $scope.allInfos = Informer.allInfos;  
      $scope.remove = Informer.remove;
    });
    

    最后,要呈现警报,您可以直接使用引导程序的标记,或者编写一个非常简单的指令来封装它。我在这里,使用来自http://angular-ui.github.com/bootstrap/的警报指令

    <body ng-controller="MainCtrl">
        <alert ng-repeat="alert in allInfos()" type="alert.type" close="remove(alert)">{{alert.msg}}</alert>
      </body>
    

    当然,您不需要使用此 repo 中的指令,如果需要,您可以创建自己的或使用原始标记。

    这是一个演示工作示例的 plunker:http://plnkr.co/edit/VxAcjHFhxXODFB5iAfyX?p=preview

    总结一下:

    • 根据经验,不要在指令之外进行 DOM 操作
    • 处理模型的服务应该与模型的表示分离

    我还建议在学习 AngularJS 的同时从项目中删除 jQuery。这样你会更快地进入 AngularJS-zen 状态!

    【讨论】:

    • 感谢您的回答!我会试试你的代码。不能说我明白了。实际上,我正在从项目中删除 jQuery,但是它有很多,所以我需要它们一起工作一段时间。另外,你能看看我的编辑 2,并建议一些在 Angular 之外使用服务的东西吗?
    • @madhead 你真的,真的应该从你的服务中删除任何 DOM 操作逻辑。此外,使用$compile 毫无意义。只需在您的服务中进行模型操作,并在您的模板/指令中进行 DOM 操作。
    • 好的,我看到你的代码就像一个魅力。有没有可能在角度之外使用它?例如,在现有的 JS/jQuery 回调中?或者在页面上的自定义&lt;script&gt;inform("", "")&lt;/script&gt;(在某些情况下在服务器上生成)标签内?
    【解决方案2】:

    我相信,Angular 的方式是尽可能少地手动操作 dom。即使您必须操纵它 - 也只能在指令中执行。

    所以,与 jQuery 不同,要走的路是拥有一个模型并使用绑定来改变你的 dom。

    出于这个原因,我会选择这种方式 - 将一些 InformerController 绑定在布局模板的某个位置,并遍历当前通知数组,然后直接绘制它们。

    app.controller('InformersController', function($scope, InformerService) {
        $scope.informs = InformerService.get();
        $scope.close = function (index) {
              InformerService.close(index)
        }
    });
    

    在你的模板中:

    <div ng-controller="InformersController">
         <div ng-repeat="inform in informs">
             <div class="alert {{inform.alertClass}} fade in informer">
                <button type="button" ng-click="close($index)" class="close">×</button>
                <div class="valignCenterWrapper">
                    <div class="valignCenter">
                        {{inform.message}}
                    </div>
                </div>
            </div>
         </div>
     </div>
    

    如果您需要从任何地方显示一些警报,请使用它将InformerService 注入您的控制器,并使用它添加数据。

    app.service('InformerService', function () {
    
        var informs = [];
        this.get = function () {
            return informs;   
        };
        this.inform = function (message, type) {
            informs.push({
                alertClass: 'alert-' + type,
                message: message
            });
        }
        this.close = function (index) {
           informs.splice(index, 1);
        }
    
    });
    

    例如:

    app.controller('SomeController', function($scope, InformerService) {
        $scope.doError = function (msg, type) {
            InformerService.inform(msg, type);
        };
    });
    

    在你的模板中:

    <div class="well" ng-controller="SomeController">
        <button class="btn btn-danger" ng-click="doError('Hello', 'error')">Error</button>  
        <button class="btn btn-info" ng-click="doError('Hello', 'info')">Info</button>  
    </div>
    

    你可以在这个小提琴中看到这一切:http://jsfiddle.net/zc3YH/6/

    【讨论】:

    • 感谢您的回答!似乎您与 pkozlowski.opensource 的想法几乎相同。 :) 据我了解,我需要在每个页面上都有一些预定义的标记(是的,我的应用程序不是 angular-one-page-app)才能在上面使用告密者?在角度之外使用它怎么样?在现有的 jQuery 代码中?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    相关资源
    最近更新 更多