Вопрос

Angular.js has a handy built-in filter, json, which displays JavaScript objects as nicely formatted JSON.

However, it seems to filter out object properties that begin with $ by default:

Template:

<pre>{{ {'name':'value', 'special':'yes', '$reallyspecial':'Er...'} | json }}</pre>

Displayed:

{
  "name": "value",
  "special": "yes"
}

http://plnkr.co/edit/oem4HJ9utZMYGVbPkT6N?p=preview

Can I make properties beginning with $ be displayed like other properties?

Это было полезно?

Решение

Basically you can't. It is "hard-coded" into the filter's behaviour.
Nonetheless, it is quite easy to build a custom JSON filter that behaves identically with the Angular's one but not filtering out properties starting with '$'.

(Scroll further down for sample code and a short demo.)


If you take a look at the 1.2.15 version source code, you will find out that the json filter is defined like this:

function jsonFilter() {
  return function(object) {
    return toJson(object, true);
  };
}

So, it uses the toJson() function (the second parameter (true) means: format my JSON nicely).


So, our next stop is the toJson() function, that looks like this:

function toJson(obj, pretty) {
  if (typeof obj === 'undefined') return undefined;
  return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
}

This function makes use of the "native" JSON.stringify() function, passing a custom replacer function (toJsonReplacer).


The toJsonReplacer() function handles some special cases: It checks if the key starts with $ and ignores it if it does (this is what we want to change) and it checks if the value is either a Window, a Document or a Scope object (in which case it converts it to a descriptive string in order to avoid "Converting circular structure to JSON" errors).

function toJsonReplacer(key, value) {
  var val = value;

  if (typeof key === 'string' && key.charAt(0) === '$') {
    val = undefined;
  } else if (isWindow(value)) {
    val = '$WINDOW';
  } else if (value &&  document === value) {
    val = '$DOCUMENT';
  } else if (isScope(value)) {
    val = '$SCOPE';
  }

  return val;
}

For the sake of completeness, the two functions that check for Window and Scope look like this:

function isWindow(obj) {
  return obj && obj.document && obj.location && obj.alert && obj.setInterval;
}

function isScope(obj) {
  return obj && obj.$evalAsync && obj.$watch;
}

Finally, all we need to do is to create a custom filter that uses the exact same code, with the sole difference that our toJsonReplacer() won't filter out properties starting with $.

app.filter('customJson', function () {
    function isWindow(obj) {
        return obj && 
               obj.document && 
               obj.location && 
               obj.alert && 
               obj.setInterval;
    }

    function isScope(obj) {
        return obj && 
               obj.$evalAsync && 
               obj.$watch;
    }

    function toJsonReplacer(key, value) {
        var val = value;

        if (isWindow(value)) {
            val = '$WINDOW';
        } else if (value && (document === value)) {
            val = '$DOCUMENT';
        } else if (isScope(value)) {
            val = '$SCOPE';
        }

        return val;
    }

    function toJson(obj, pretty) {
        if (typeof obj === 'undefined') { return undefined; }
        return JSON.stringify(obj, toJsonReplacer, pretty ? '  ' : null);
    }

    return function(object) {
        return toJson(object, true);
    };
});

See, also, this short demo.


* The downside is that your custom JSON filter will not benefit from further improvement/enhancement of Angular's json filter, so you'll have to re-define your's to incorporate changes. Of course, for such a basic and simple filter like this, one should'nt expect frequent or extensive changes, but that doesn't mean there aren't going to be any.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top