Búsqueda de sitios web

javascript - ¿Cómo puedo usar una clase para mi viewmodel de shell en Durandal?


Estoy revisando la plantilla Hot Towel, y tratando de hacer que funcione en TypeScript, me he encontrado con un problema al convertir el viewmodel del shell. Estoy tratando de traducir esto a TS, y me parece más lógico que debería ser una clase, en lugar de simplemente exportar funciones como se muestra aquí. Revisé este enfoque pero, prestando atención a los comentarios aquí, decidí no seguirlo.

Después de investigar un poco, encontré este hilo, que sugería que debería ser tan simple como sobrescribir router.getActivatableInstance, pero no parece que pueda avanzar lo suficiente para que esa función sea llamada nunca.

Aquí está mi main.ts (también envuelto en una clase):

/// <reference path="dts/toastr.d.ts" />
/// <reference path="dts/durandal.d.ts" />
/// <reference path="dts/require.d.ts" />

import _app = module('durandal/app');
import _system = module('durandal/system');
import _viewLocator = module('durandal/viewLocator');
import _router = module('durandal/plugins/router');
import _logger = module('services/logger');

export class HOPS {

    public init(): void {
        _logger.log('init', this, 'HOPS', false);
        _system.debug(true);
        this._startApp();
    }

    private _startApp(): void {
        _logger.log('_startApp', this, 'HOPS', false);

        _app.start().then(() => {
            toastr.options.positionClass = 'toast-bottom-right';
            toastr.options.backgroundpositionClass = 'toast-bottom-right';

            var defImpl = _router.getActivatableInstance; //default Implementation

            _router.getActivatableInstance = function (routeInfo: _router.routeInfo, paramaters: any, module: any) {
                var functionName = routeInfo.name.substring(0, 1).toUpperCase() + routeInfo.name.substring(1);
                if (typeof module [functionName] == 'function') {
                    var instance = new module [functionName]();
                    instance.__moduleId__ = module.__moduleId__;
                    return instance;
                }
                else return defImpl(routeInfo, paramaters, module );
            }

            _router.handleInvalidRoute = (route: _router.routeInfo, paramaters: any) => {
                _logger.logError('No Route Found', route, 'main', true);
            }

            _router.useConvention();
            _viewLocator.useConvention();

            _app.adaptToDevice();

            _app.setRoot('viewmodels/shell', 'entrance');
        });
    }
}

var hops: HOPS = new HOPS();
hops.init();

(Área de juegos mostrando JS aquí: http://bit.ly/WyNbhn)

... y aquí está mi shell.ts:

import _system = module('durandal/system');
import _router = module('durandal/plugins/router');
import _logger = module('services/logger');

export class Shell{

    public router = _router;

    public activate(): JQueryPromise {
        _logger.log("activate", null, 'shell', false);
        return this.boot();
    }

    public boot(): JQueryPromise {
        _logger.log("boot", null, 'shell', false);
        this.router.mapNav('home')
        this.router.mapNav('details');
        _logger.log('SciHops SPA Loaded!', null, _system.getModuleId(this), true);
        return this.router.activate('home');
    }
}

... que se compila a:

define(["require", "exports", 'durandal/system', 'durandal/plugins/router', 'services/logger'], function(require, exports, ___system__, ___router__, ___logger__) {
    /// <reference path="../dts/_references.ts" />
    var _system = ___system__;

    var _router = ___router__;

    var _logger = ___logger__;

    var shell = (function () {
        function shell() {

            this.router = _router;
        }
        shell.prototype.activate = function () {
            _logger.log("activate", null, 'shell', false);
            return this.boot();
        };
        shell.prototype.boot = function () {
            _logger.log("boot", null, 'shell', false);
            this.router.mapNav('home');
            this.router.mapNav('details');
            _logger.log('SciHops SPA Loaded!', null, _system.getModuleId(this), true);
            return this.router.activate('home');
        };
        return shell;
    })();
    exports.shell = shell;    
})

Con la clase anterior, obtengo un error de enlace porque router está indefinido. Si agrego export var router=_router, entonces este error desaparece, pero el método activate en mi clase shell nunca se llama.

Todo lo anterior funciona bien para los viewmodels posteriores, pero simplemente falla en el shell.

¿Qué estoy haciendo mal y cómo puedo hacer que una clase TS funcione como mi viewmodel de shell en Durandal?

Respuesta #1

El problema es que tu router no está resolviendo tu módulo shell.ts.

Todos tus otros modelos de vista están pasando a través del método router.getActivatableInstance y se están instanciando y se les está añadiendo __moduleId__. Excepto tu módulo shell.ts.

Esta es una manera rápida y sucia de hacer que tu módulo shell.ts funcione.

Dentro de tu main.ts puedes requerir tu módulo shell.ts:

import _shell = module('shell');

Luego, aún dentro de tu main.ts, puedes instanciar tu clase shell y asignarle el moduleId.

var shell = new _shell.Shell();
shell.__moduleId__ = _shell.__moduleId__;
_app.setRoot(shell, 'entrance');

No es lo más bonito que hayas visto. Pero hace el trabajo. Eso es básicamente hacer lo mismo que la sobrecarga de getActivatableInstance que estás haciendo. Dado que tu shell no está pasando por el módulo router.js, tienes que hacerlo manualmente.

Respuesta #2

Otra opción es exportar la clase como el módulo para Duranal. nota: este método también funciona para los viewmodels.

Un ejemplo funcional basado en el "Durandal Starter Kit" de nuget:

    import router = require('plugins/router')
    import app = require('durandal/app')

    class Shell {
        router =  router;
        public search() {
            //It's really easy to show a message box.
            //You can add custom options too. Also, it returns a promise for the user's response.
            app.showMessage('Search not yet implemented...');
        }
        public activate() {
            router.map([
                { route: '', title: 'Welcome', moduleId: 'viewmodels/welcome', nav: true },
                { route: 'flickr', moduleId: 'viewmodels/flickr', nav: true },
            ]).buildNavigationModel();


            return router.activate();
        }
    }

    // Magic happens here:
    export = Shell;

El código original 'js':

    define(['plugins/router', 'durandal/app'], function (router, app) {
        return {
            router: router,
            search: function() {
                //It's really easy to show a message box.
                //You can add custom options too. Also, it returns a promise for the user's response.
                app.showMessage('Search not yet implemented...');
            },
            activate: function () {
                router.map([
                    { route: '', title:'Welcome', moduleId: 'viewmodels/welcome', nav: true },
                    { route: 'flickr', moduleId: 'viewmodels/flickr', nav: true },
                ]).buildNavigationModel();


                return router.activate();
            }
        };
    });

Artículos relacionados