Search code examples
typescriptjsrenderjsviews

JsRender/JsViews: How to get {for} working for subclassed arrays


I've subclassed Array (using TypeScript), in order to be able to add extra functions to the Array. See code below.

Since then, I've noticed that the {for} tag of JsRender/JsViews does not work any more. I've also noticed JsRender/JsViews uses $.isArray(...) in order to iterate arrays, which will return false on subclassed arrays.

When I change JsRender/JsViews code to use (data instanceof Array) it does work.

Is there another way (without changing JsRender/JsViews code) to get subclassed arrays to work with JsRender/JsViews {for} tag?

Typescript:

module MyModule {
    export class Collection<T> implements Array<T> {
        constructor() {
            Array.apply(this, arguments);
            return new Array();
        }

        length: number;
        toString(): string { return ""; }
        //... All other Array properties/methods
    }

    // replace dummy declarations with the real thing
    Collection['prototype'] = new Array();
}

Generated Javascript:

var MyModule;
(function (MyModule) {
    var Collection = (function () {
        function Collection() {
            Array.apply(this, arguments);
            return new Array();
        }

        Collection.prototype.toString = function () { return ""; };
        //... All other Array properties/methods

        return Collection;
    })();
    MyModule.Collection = Collection;

    // replace dummy declarations with the real thing
    Collection['prototype'] = new Array();
})(MyModule|| (MyModule= {}));

Solution

  • Your SubclassedCollection is not a JavaScript array (just as a jQuery object is not a JavaScript array). Unlike your Collection, it returns "[object Object]" from Object.prototype.toString.call(ob) - rather than "[object Array]".

    One possible approach might be to use the function:

    function toArray(ob) {
        return Array.prototype.slice.call(ob);
    }
    

    and either write

    MyModule.ViewModel = { Coll: toArray(collection2) };
    

    or else register toArray as a helper (~toArray) or a converter ("toArray") and then write your template as:

    {{for ~toArray(Coll)}}
    

    (or alternatively {{for Coll convert=~toArray}} or {{for Coll convert="toArray"}}).

    Here is an updated fork of your jsfiddle: http://jsfiddle.net/16L670re/