How do I access Object.keys(object).length from a directive template?

拜拜、爱过 提交于 2020-01-23 05:00:10

问题


I'm trying to create a directive that will list all the errors contained in an errors object ({phone: ["is required"]}, but only if the object is non-empty. (It doesn't make sense to say "The following errors…" when there were none.)

I figured out how to check if an object is empty by testing Object.keys(errors).length. The problem is that I can't figure out how to access Object.keys from my directive template. Is this possible?

Since Angular expressions are "evaluated" (using $parse, not eval() ) in the context of a scope instead of in the context of window, we don't have access to things like Object from the directives template, because Object isn't a property of the scope. (Docs: Expressions)

Makes sense so far. It goes on to say that "Unlike JavaScript, where names default to global window properties, Angular expressions must use $window explicitly to refer to the global window object. For example, if you want to call alert() in an expression you must use $window.alert().

But I can't seem to access Object even if I do $window.Object. What am I missing?


Here's the code for the directive I'm debugging (and here's a jsfiddle):

app.js.coffee:

…
.directive 'displayErrorsAllKeys', ->
  {
    restrict: 'A',
    scope: {
      errors: '='
      debug: '@debug'
    }
    templateUrl: 'templates/display-errors-all-keys'
  }

.run(['$rootScope', ($rootScope) ->
  $rootScope.nonEmpty = (object) ->
    !! Object.keys(object).length
])

.controller('TestDisplayErrorsAllKeys',
['$scope',
( $scope ) ->

  $scope.other_errors = {}

  $scope.add_errors = ->
    $scope.other_errors = {"surname":["is required"],"phone":["is required"]}

  $scope.clear_errors = ->
    $scope.other_errors = {}
])

display-errors-all-keys.ngt.haml:

.errors(ng-show="$window.Object.keys(errors).length > 0")
  %p The following errors prevented this from saving:
  %div(ng-repeat="(key, error_messages) in errors") {{key}}: {{error_messages | toSentence}}

test_ng_display-errors-all-keys.html.haml

:scss
  .errors {
    color: red;
  }

%div(ng-app='MyApp')
  %form(ng-controller="TestDisplayErrorsAllKeys")
    %p errors: {{ other_errors }}
    %p nonEmpty(other_errors): {{ nonEmpty(other_errors) }}
    %hr/

    %div(display-errors-all-keys errors="other_errors")

    %input(type="button" value="Add errors" ng-click="add_errors()")/
    %input(type="button" value="Clear errors" ng-click="clear_errors()")/

I finally got it to work by defining a helper method in my scope and calling that instead ($root.nonEmpty(errors)) (see jsfiddle for working demo).

This is probably a pretty good solution, but:

  1. Is there an even better way to solve this? How would you have done it (written the ng-show expression)?

  2. How would I get it to work using Object.keys(errors).length directly in the ng-show??


回答1:


I would provide the helper function in the directives scope (which is isolated). Typically by providing a link function for the directive:

.directive('displayErrorsAllKeys', function() {
    return {
        restrict: 'A',
        scope: {
            errors: '=',
            debug: '@debug'
        },
        link: function (scope) {
            scope.errorsExists = function(object) {
                return object && Object.keys(object).length;
            };
        },
        templateUrl: 'templates/display-errors-all-keys'
    };
})

Placing logic and data in the root scope is seldom a good practice. Also note that I named the function errorExists, which provides som abstraction upon the actual representation of the errors.

As for your second question, you could add scope.Object = Object; in your link function, but DON'T! Such specific logic doesn't belong in your template. The template should be concerned with wether or not to show the errors, but not why.




回答2:


Here's a nonEmpty filter I added to solve this problem (JavaScript equivalent)

.filter 'nonEmpty', ->
  (object) ->
    !! (object && Object.keys(object).length > 0)

Tests:

%div {{ null | nonEmpty }} should be false
%div {{ { } | nonEmpty }} should be false
%div {{ {a: '1'} | nonEmpty }} should be true

My ng-show condition in the example above can be simplified to just this:

ng-show="errors | nonEmpty"

Now I can easily reuse this check logic, without having to add a new method to each directive or controller where I may want to use it.



来源:https://stackoverflow.com/questions/22852388/how-do-i-access-object-keysobject-length-from-a-directive-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!