diff --git a/CHANGELOG.md b/CHANGELOG.md
index 913d344..21f76fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+1.0.0-rc.22 / 2015-07-17
+==================
+
+ * Added TypeScript Pull-Request
+
+
1.0.0-rc.21 / 2015-07-02
==================
diff --git a/app/index.js b/app/index.js
index bb64625..f4a44b8 100644
--- a/app/index.js
+++ b/app/index.js
@@ -183,11 +183,15 @@ var HirschGenerator = yeoman.generators.Base.extend({
scaffoldFolders: function() {
this.mkdir('src');
this.mkdir('src/app');
- this.mkdir('src/app/common/services');
- this.mkdir('src/app/common/directives');
- this.mkdir('src/app/common/templates');
this.mkdir('src/app/common/decorators');
+ this.mkdir('src/app/common/directives');
this.mkdir('src/app/common/filters');
+ this.mkdir('src/app/common/models');
+ this.mkdir('src/app/common/services');
+ this.mkdir('src/app/common/services/converters');
+ this.mkdir('src/app/common/services/rest');
+ this.mkdir('src/app/common/templates');
+ this.mkdir('src/app/common/util');
this.mkdir('src/app/common/views');
this.mkdir('src/app/core');
this.mkdir('src/assets');
diff --git a/app/templates/_tsd.json b/app/templates/_tsd.json
index ff041fb..8160dcd 100644
--- a/app/templates/_tsd.json
+++ b/app/templates/_tsd.json
@@ -23,9 +23,6 @@
"angularjs/angular-cookies.d.ts": {
"commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da"
},
- "restangular/restangular.d.ts": {
- "commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da"
- },
"jquery/jquery.d.ts": {
"commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da"
},
diff --git a/app/templates/_tslint.json b/app/templates/_tslint.json
index 630484d..008fbaa 100644
--- a/app/templates/_tslint.json
+++ b/app/templates/_tslint.json
@@ -7,7 +7,7 @@
"indent": [true, 2],
"label-position": true,
"label-undefined": true,
- "max-line-length": [true, 140],
+ "max-line-length": [true, 200],
"no-arg": true,
"no-bitwise": true,
"no-console": [true,
diff --git a/app/templates/src/app-ts/common/util/lazy.ts b/app/templates/src/app-ts/common/util/lazy.ts
new file mode 100644
index 0000000..d0c72f1
--- /dev/null
+++ b/app/templates/src/app-ts/common/util/lazy.ts
@@ -0,0 +1,24 @@
+///
+
+module <%= prompts.prefix %>.common.util {
+ 'use strict';
+
+ export class Lazy {
+ private instance: T;
+
+ constructor(private factory: () => T) { }
+
+ get isCreated() {
+ return !!this.instance;
+ }
+
+ get value() {
+ return this.instance || (this.instance = this.factory());
+ }
+
+ reset = () => {
+ this.instance = undefined;
+ return this;
+ }
+ }
+}
diff --git a/app/templates/src/app-ts/common/views/abstractController.ts b/app/templates/src/app-ts/common/views/abstractController.ts
deleted file mode 100644
index 4867c36..0000000
--- a/app/templates/src/app-ts/common/views/abstractController.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-///
-
-module <%= prompts.prefix %>.common.views {
- export class AbstractController {
- constructor($state: ng.ui.IStateService) {
- $state.current.onExit = this.dispose.bind(this);
- }
-
- protected dispose() {
- // nothing to do here
- }
- }
-}
diff --git a/app/templates/src/app-ts/core/router/router.config.ts b/app/templates/src/app-ts/core/router/router.config.ts
index 1660c48..7fccb85 100644
--- a/app/templates/src/app-ts/core/router/router.config.ts
+++ b/app/templates/src/app-ts/core/router/router.config.ts
@@ -1,14 +1,61 @@
///
+/**
+ * Extension for 3rd party type definition file.
+ */
+declare module angular.ui {
+ interface IState {
+ includes?(name: string): void;
+ }
+}
+
module <%= prompts.prefix %>.core.router {
- var routerConfig = ($urlRouterProvider: ng.ui.IUrlRouterProvider) => {
- $urlRouterProvider.otherwise('/home');
+ // this variable is to prevent an issue with the default routes;
+ // if the initial request to the application is for an invalid route
+ // the default route rule triggers multiple times (once during a state
+ // transition). This causes the actual state transition to be superseded.
+ // By capturing a variable inside the module scope, we can make the
+ // default route handler ignore invalid route requests during routing
+ var stateChangeCountingSemaphore = 0;
+ var checkSemaphore = (fn: () => string) => stateChangeCountingSemaphore > 0 ? undefined : fn();
+
+ var routerConfig = ($urlRouterProvider: ng.ui.IUrlRouterProvider, $stateProvider: ng.ui.IStateProvider) => {
+ $urlRouterProvider.otherwise(() => checkSemaphore(() => `/home`));
+
+ // We decorate the 'includes' to extract all states that would match
+ // a $state.includes() test for each state, and extend the state
+ // object with a function that can be used to check whether a given
+ // state is included in the list. This is then used inside the router
+ // pipeline to check whether a given middleware is applicable for a
+ // routing request
+ $stateProvider.decorator('includes', (state, parent): any => {
+ var result = parent(state);
+ var includeNames = Object.keys(result);
+
+ // state is actually a wrapper object, so we unwrap it
+ state = state['self'];
+
+ state.includes = name => includeNames.some(s => s === name);
+
+ return result;
+ });
+ };
+
+ routerConfig.$inject = ['$urlRouterProvider', '$stateProvider'];
+
+ var run = ($rootScope: ng.IRootScopeService, routerService: IRouterService) => {
+ $rootScope.$on('$stateChangeStart', (evt, ...rest: any[]) => {
+ stateChangeCountingSemaphore += 1;
+ rest.unshift(evt.preventDefault.bind(evt));
+ routerService.startPipeline.apply(null, rest).finally(() => stateChangeCountingSemaphore -= 1);
+ });
};
- routerConfig.$inject = ['$urlRouterProvider'];
+ run.$inject = ['$rootScope', ID.RouterService];
angular
.module(`${Namespace}.RouterConfig`, [])
- .config(routerConfig);
+ .config(routerConfig)
+ .run(run);
}
diff --git a/app/templates/src/app-ts/core/router/router.constants.ts b/app/templates/src/app-ts/core/router/router.constants.ts
deleted file mode 100644
index ab4a98d..0000000
--- a/app/templates/src/app-ts/core/router/router.constants.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-///
-
-module <%= prompts.prefix %>.core.router {
-
- var getSecuredRoutes = () => [
- '/private/*'
- ];
-
- angular
- .module(`${Namespace}.RouterConstants`, [])
- .constant(ID.APP_ROUTER_PRIVATE_ROUTES, getSecuredRoutes());
-}
diff --git a/app/templates/src/app-ts/core/router/router.middleware.auth.ts b/app/templates/src/app-ts/core/router/router.middleware.auth.ts
new file mode 100644
index 0000000..5d744da
--- /dev/null
+++ b/app/templates/src/app-ts/core/router/router.middleware.auth.ts
@@ -0,0 +1,31 @@
+///
+
+module <%= prompts.prefix %>.core.router {
+ 'use strict';
+
+ var run = (logger: util.ILoggerFactory, $timeout: ng.ITimeoutService, routerService: IRouterService) => {
+ var log = logger('Auth middleware');
+
+ var authMiddleware: IRouterMiddleware = (actions, context) => {
+ if (context.toState.data && context.toState.data.requiresSession) {
+ // TODO: check if user has session
+ return $timeout(() => { ; }, 1000).then(() => {
+ log.debug('Redirecting to login page...');
+ return actions.redirect('public.login');
+ });
+ } else {
+ return actions.next();
+ }
+ };
+
+ routerService.addMiddleware(authMiddleware, 'auth');
+ };
+
+ run.$inject = [util.ID.LoggerFactory, '$timeout', ID.RouterService];
+
+ angular
+ .module(`${Namespace}.RouterMiddlewareAuth`, [
+ util.ID.LoggerFactory
+ ])
+ .run(run);
+}
diff --git a/app/templates/src/app-ts/core/router/router.middleware.errorHandling.ts b/app/templates/src/app-ts/core/router/router.middleware.errorHandling.ts
new file mode 100644
index 0000000..46ab483
--- /dev/null
+++ b/app/templates/src/app-ts/core/router/router.middleware.errorHandling.ts
@@ -0,0 +1,24 @@
+///
+
+module <%= prompts.prefix %>.core.router {
+ 'use strict';
+
+ var run = ($q: ng.IQService, routerService: IRouterService, logger: util.ILoggerFactory) => {
+ var log = logger('ErrorHandling middleware');
+
+ var errorHandlingMiddleware: IRouterMiddleware = a => a.next().catch(err => {
+ log.error(err);
+ return $q.reject(err);
+ });
+
+ routerService.addMiddleware(errorHandlingMiddleware, 'errorHandling');
+ };
+
+ run.$inject = ['$q', ID.RouterService, util.ID.LoggerFactory];
+
+ angular
+ .module(`${Namespace}.RouterMiddlewareErrorHandling`, [
+ util.ID.LoggerFactory
+ ])
+ .run(run);
+}
diff --git a/app/templates/src/app-ts/core/router/router.module.ts b/app/templates/src/app-ts/core/router/router.module.ts
index 6f94876..e629682 100644
--- a/app/templates/src/app-ts/core/router/router.module.ts
+++ b/app/templates/src/app-ts/core/router/router.module.ts
@@ -6,8 +6,7 @@ module <%= prompts.prefix %>.core.router {
export var Namespace = '<%= prompts.prefix %>.core.router';
export var ID = {
- RouterService: `${Namespace}.RouterService`,
- APP_ROUTER_PRIVATE_ROUTES: `${Namespace}.APP_ROUTER_PRIVATE_ROUTES`
+ RouterService: `${Namespace}.RouterService`
};
angular
@@ -15,10 +14,8 @@ module <%= prompts.prefix %>.core.router {
'ui.router',
'ui.router.router',
'ui.router.state',
-
- `${Namespace}.RouterConstants`,
+
`${Namespace}.RouterConfig`,
- ID.RouterService,
- `${Namespace}.Router`
+ ID.RouterService
]);
}
diff --git a/app/templates/src/app-ts/core/router/router.service.ts b/app/templates/src/app-ts/core/router/router.service.ts
index 2cb410d..a2a8bf4 100644
--- a/app/templates/src/app-ts/core/router/router.service.ts
+++ b/app/templates/src/app-ts/core/router/router.service.ts
@@ -1,49 +1,259 @@
-///
+///
module <%= prompts.prefix %>.core.router {
export interface IRouterService {
- isInitialized: boolean;
- initialize(): ng.IPromise;
+ /**
+ * Flag indicating whether the routing pipeline is currently active.
+ */
+ isNavigating: boolean;
+ startPipeline(preventDef: Function, toState: ng.ui.IState, toParams: any, fromState: ng.ui.IState, fromParams: any): ng.IPromise;
+
+ /**
+ * Add a middleware to the routing pipeline. Middlewares are executed in
+ * the order of registration. If the state parameter is used, the middleware
+ * will only be processed by the pipeline if the target state of the
+ * navigation is either a child state of or equal to the given state (depending
+ * on the given matching mode, defaults to include children). Only absolute
+ * state names are supported, i.e. no wildcards or other patterns are allowed.
+ */
+ addMiddleware(middleware: IRouterMiddleware, name: string, state?: string, stateMatchingMode?: StateMatchingMode): IRouterService;
}
- class RouterService implements IRouterService {
- private initializing = false;
- private initialized = false;
- private deferredInit: ng.IDeferred;
- private log: util.Logger;
-
- static $inject = ['$q', util.ID.LoggerFactory];
- constructor($q: ng.IQService, loggerFactory: util.ILoggerFactory) {
- this.log = loggerFactory('AppRouterService');
- this.deferredInit = $q.defer();
- this.deferredInit.promise.then(() => this.initialized = true);
- }
+ /**
+ * A router middleware is a function that is executed as part of a pipeline.
+ * The middleware can perform exactly one of the following actions when executed
+ * (in addition to any logic the middleware itself contains):
+ * 1) Call the next middleware in the pipeline
+ * 2) Redirect to another state, thereby restarting the pipeline
+ * 3) Cancel the pipeline
+ *
+ * The middleware can also pass information to other middlewares that are executed
+ * after it by modifying the context object. Note that all objects in the context
+ * are mutable, but only the "toParams" object should be modified by the middleware.
+ */
+ export interface IRouterMiddleware {
+ (actions: IRouterMiddlewareActions, context: IRouterMiddlewareContext): ng.IPromise;
+ }
- get isInitialized() {
- return this.initialized;
- }
+ export interface IRouterMiddlewareActions {
+ /**
+ * Execute the next middleware in the pipeline.
+ */
+ next(): ng.IPromise;
+
+ /**
+ * Redirect to the given state, restarting the pipeline.
+ */
+ redirect(to: string, params?: any): ng.IPromise;
/**
- * Here we will load some initial data for the application like the active event, but
- * only at the first run.
+ * Cancel the pipeline.
*/
- initialize = (): ng.IPromise => {
- if (this.initializing || this.initialized) {
- return this.deferredInit.promise;
+ cancel(): ng.IPromise;
+ }
+
+ export interface IRouterMiddlewareContext {
+ /**
+ * The target state of the routing pipeline.
+ */
+ toState: ng.ui.IState;
+
+ /**
+ * The parameters for the target state. This object can be used by middlewares to
+ * pass additional information to other middlewares or to the controller of the
+ * target state.
+ */
+ toParams: any;
+
+ /**
+ * The state that was active when the routing pipeline was started.
+ */
+ fromState: ng.ui.IState;
+ fromParams: any;
+ }
+
+ export enum StateMatchingMode {
+ Exact,
+ IncludeChildren
+ }
+
+ interface IMiddlewareContainer {
+ name: string;
+ middleware: IRouterMiddleware;
+ state: string;
+ stateMatchingMode: StateMatchingMode;
+ }
+
+ class RouterService implements IRouterService {
+ private log: util.ILogger;
+ private middlewares: IMiddlewareContainer[] = [];
+ private sync = false;
+ private current: { state: ng.ui.IState; params: any } = { state: {}, params: {} };
+ private lastPipelineId = 0;
+
+ isNavigating: boolean;
+
+ static $inject = ['$state', '$q', util.ID.LoggerFactory];
+
+ constructor(
+ private $state: ng.ui.IStateService,
+ private $q: ng.IQService,
+ loggerFactory: util.ILoggerFactory
+ ) {
+ this.log = loggerFactory(this);
+ }
+
+ startPipeline = (preventDefault: Function, toState: ng.ui.IState, toParams: any, fromState: ng.ui.IState, fromParams: any) => {
+ // if we are in the process of finishing pipeline processing, don't do anything
+ if (this.sync) {
+ return this.$q.reject('router is syncing');
}
- this.initializing = true;
+ this.lastPipelineId += 1;
+ return this.startPipelineInternal(preventDefault, toState, toParams, fromState, fromParams, this.lastPipelineId);
+ }
+
+ private startPipelineInternal = (
+ preventDefault: Function,
+ toState: ng.ui.IState,
+ toParams: any,
+ fromState: ng.ui.IState,
+ fromParams: any,
+ pipelineId: number
+ ) => {
+ this.log.debug(`Starting routing pipeline [ID: ${pipelineId}; to: ${toState.name}; from: ${fromState.name}]...`);
+ this.isNavigating = true;
+
+ preventDefault();
+
+ // by calling $state.go with notify set to false, we update the URL
+ // in the browser address bar without triggering navigation. This is
+ // necessary, since otherwise the preventDefault() call above would
+ // cause the old URL to be displayed
+ this.$state.go(toState.name, {}, { notify: false });
+ var context = { toState, toParams, fromState, fromParams };
+
+ var redirect = (middlewareName: string) => (to: string, params: any = {}): ng.IPromise => {
+ this.log.debug(`Middleware '${middlewareName}' redirected to '${to}'!`);
+
+ // this check is slightly different from $state.is(), since it
+ // takes into account all params instead of only the ones defined
+ // in the params definition of the state
+ if (to.indexOf(this.current.state.name) === 0 && angular.equals(this.current.params, params)) {
+ // if we got redirected to the last active state, we need to restore
+ // its URL
+ this.$state.go(to, params, { notify: false });
+ return this.endPipeline(pipelineId, this.current.state, params).then(() => this.$q.reject('pipeline ended in source state'));
+ } else {
+ return this.startPipelineInternal(() => { ; }, this.$state.get(to), params, fromState, fromParams, pipelineId);
+ }
+ };
+
+ var cancel = (middlewareName: string) => (): ng.IPromise => {
+ this.log.debug(`Middleware '${middlewareName}' canceled navigation!`);
+
+ // if navigation is canceled, we need to restore the URL of the
+ // original state
+ this.$state.go(fromState.name, fromParams, { notify: false });
+ return this.endPipeline(pipelineId, fromState, fromParams).then(() => this.$q.reject('canceled'));
+ };
+
+ var next = (remaining: IMiddlewareContainer[]) => (): ng.IPromise => {
+ if (remaining.length === 0) {
+ var isInitialRequest = fromState.name === '';
+
+ // before we transition to the final state, we quickly return to the original
+ // state, but without triggering any navigation; however, on initial page load
+ // there is no previous state to return to, so skip the restore
+ var restore = () => this.$state.go(fromState.name, fromParams, { notify: false });
+ var go = () => this.$state.go(context.toState.name, context.toParams);
+
+ this.sync = true;
+ return (isInitialRequest ? go() : restore().then(go))
+ .then(() => this.endPipeline(pipelineId, context.toState, context.toParams))
+ .finally(() => this.sync = false)
+ .then(() => context);
+ }
- // TODO: load initial data
- this.log.info('loadInitialData');
- this.deferredInit.resolve();
+ var container = remaining.splice(0, 1)[0];
+ var executeMiddleware =
+ container.stateMatchingMode === StateMatchingMode.Exact
+ ? toState.name === container.state
+ : toState.includes(container.state);
- return this.deferredInit.promise;
+ if (executeMiddleware) {
+ this.log.debug(`Executing middleware '${container.name}'...`);
+
+ var guardActive = false;
+ var guard = (fn: Function) => (...rest: any[]) => {
+ if (guardActive) {
+ return this.$q.reject(new Error(`Middleware ${container.name} is trying to execute more than one action!`));
+ }
+
+ guardActive = true;
+
+ if (pipelineId !== this.lastPipelineId) {
+ return this.cancelPipeline(pipelineId);
+ }
+
+ return fn.apply(null, rest);
+ };
+
+ var actions = {
+ next: guard(next(remaining)),
+ cancel: guard(cancel(container.name)),
+ redirect: guard(redirect(container.name))
+ };
+
+ return container.middleware(actions, context).then(() => {
+ if (!guardActive) {
+ return this.$q.reject(new Error(`Middleware ${container.name} did not execute any action!`));
+ }
+
+ return this.$q.when();
+ });
+ } else {
+ this.log.debug(`Skipping middleware '${container.name}'...`);
+ return next(remaining)();
+ }
+ };
+
+ var clone = this.middlewares.slice(0);
+ return next(clone)();
+ };
+
+ addMiddleware = (
+ middleware: IRouterMiddleware,
+ name: string,
+ state: string = '',
+ stateMatchingMode = StateMatchingMode.IncludeChildren
+ ) => {
+ this.middlewares.push({ middleware, state, name, stateMatchingMode });
+ return this;
+ };
+
+ private endPipeline = (pipelineId: number, state: ng.ui.IState, params: any) => {
+ this.log.debug(`Finished routing pipeline [ID: ${pipelineId}] in state '${state.name}'!`);
+ this.isNavigating = false;
+ this.current = { state, params };
+ return this.$q.when();
+ };
+
+ private cancelPipeline = (pipelineId: number) => {
+ this.log.debug(`Canceled routing pipeline [ID: ${pipelineId}]!`);
+ this.isNavigating = false;
+ return this.$q.when();
};
}
angular
- .module(ID.RouterService, [])
+ .module(ID.RouterService, [
+ 'ui.router',
+ 'ui.router.router',
+ 'ui.router.state',
+
+ util.ID.LoggerFactory
+ ])
.service(ID.RouterService, RouterService);
}
diff --git a/app/templates/src/app-ts/core/router/router.ts b/app/templates/src/app-ts/core/router/router.ts
deleted file mode 100644
index 79f72d5..0000000
--- a/app/templates/src/app-ts/core/router/router.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-///
-
-/**
- * Fix for 3rd party type definition file.
- */
-declare module angular.ui {
- interface IUrlRouterService {
- listen(): void;
- }
-}
-
-module <%= prompts.prefix %>.core.router {
- 'use strict';
-
- angular
- .module(`${Namespace}.Router`, [])
- .run(AppRouter);
-
- AppRouter.$inject = [
- '$q',
- '$rootScope',
- '$urlRouter',
- util.ID.LoggerFactory,
- '$state',
- ID.RouterService,
- ID.APP_ROUTER_PRIVATE_ROUTES
- ];
-
- function AppRouter(
- $q: ng.IQService,
- $rootScope: ng.IRootScopeService,
- $urlRouter: ng.ui.IUrlRouterService,
- logger: util.ILoggerFactory,
- $state: ng.ui.IStateService,
- routerService: IRouterService,
- privateRoutes: string[]) {
- var log = logger('AppRouter');
- log.info('start');
-
- $rootScope.$on('$locationChangeSuccess', onLocationChange);
-
- $urlRouter.listen();
-
- function onLocationChange(event, toUrl, fromUrl) {
- log.info('onLocationChange', { toUrl, fromUrl });
-
- // Halt state change from even starting
- event.preventDefault();
-
- routerService.initialize()
- .then(() => ensurePrivateRoute(toUrl))
- .then(() => hasValidSession())
- .catch(err => {
- log.warn('catch', err);
- if (err.redirct) {
- log.info('redirect to login');
- $state.go('authLogin');
- }
-
- if (err.sync) {
- log.info('Browser sync');
- $urlRouter.sync();
- }
- });
- }
-
- function ensurePrivateRoute(toUrl: string) {
- var deferred = $q.defer();
- toUrl = parseRoute(toUrl);
-
- var isPrivate = privateRoutes.some(r => doesUrlMatchPattern(r, toUrl));
-
- if (isPrivate) {
- deferred.resolve();
- } else {
- deferred.reject({
- sync: true
- });
- }
-
- return deferred.promise;
- }
-
- function hasValidSession() {
- var deferred = $q.defer();
-
- // TODO: Check if the user has a valid session
- if (true) {
- deferred.resolve();
- } else {
- deferred.reject({
- session: true,
- redirct: true
- });
- }
-
- return deferred.promise;
- }
-
- function parseRoute(route: string) {
- return route.split('#')[1] || route || '';
- }
-
- function doesUrlMatchPattern(pattern: string, route: string) {
- var exp = new RegExp(pattern);
- return exp.test(route);
- }
- }
-}
diff --git a/app/templates/src/app-ts/core/util/backend.ts b/app/templates/src/app-ts/core/util/backend.ts
new file mode 100644
index 0000000..90991ea
--- /dev/null
+++ b/app/templates/src/app-ts/core/util/backend.ts
@@ -0,0 +1,44 @@
+///
+
+module <%= prompts.prefix %>.core.util {
+ 'use strict';
+
+ export interface IBackend {
+ url: string;
+ get(url: string, config?: ng.IRequestShortcutConfig): ng.IPromise;
+ delete(url: string, config?: ng.IRequestShortcutConfig): ng.IPromise;
+ post(url: string, data: any, config?: ng.IRequestShortcutConfig): ng.IPromise;
+ put(url: string, data: any, config?: ng.IRequestShortcutConfig): ng.IPromise;
+ }
+
+ class Backend implements IBackend {
+ static $inject = ['$http', constants.ID.AppConfig];
+ constructor(private $http: ng.IHttpService, private appConfig: constants.IAppConfig) {
+ }
+
+ get = (url: string, config?: ng.IRequestShortcutConfig) => this.unwrap(this.$http.get(this.prefix(url), this.headers(config)));
+ delete = (url: string, config?: ng.IRequestShortcutConfig) => this.unwrap(this.$http.delete(this.prefix(url), this.headers(config)));
+ post = (url: string, data, config?: ng.IRequestShortcutConfig) => this.unwrap(this.$http.post(this.prefix(url), data, this.headers(config)));
+ put = (url: string, data, config?: ng.IRequestShortcutConfig) => this.unwrap(this.$http.put(this.prefix(url), data, this.headers(config)));
+
+ get url() { return `${this.appConfig.BASE_URL}`; }
+
+ private prefix = (url: string) => `${this.appConfig.BASE_URL}${url}`;
+ private headers = (config?: ng.IRequestShortcutConfig) => {
+ config = config || {};
+ config.headers = config.headers || {};
+ config.headers.Accept = 'application/json';
+ return config;
+ };
+
+ private unwrap(p: ng.IHttpPromise) {
+ return p.then(resp => resp.data);
+ }
+ }
+
+ angular
+ .module(ID.Backend, [
+ constants.Namespace
+ ])
+ .service(ID.Backend, Backend);
+}
diff --git a/app/templates/src/app-ts/core/util/logger.ts b/app/templates/src/app-ts/core/util/logger.ts
index a579b9e..d6b35bf 100644
--- a/app/templates/src/app-ts/core/util/logger.ts
+++ b/app/templates/src/app-ts/core/util/logger.ts
@@ -1,31 +1,54 @@
///
+/**
+ * Extend global function interface with ES6 name property.
+ */
+interface Function {
+ name?: string;
+}
+
module <%= prompts.prefix %>.core.util {
'use strict';
- var loggerService = ($log, _, moment, appConfig): ILoggerFactory => {
- return name => new Logger($log, _, moment, appConfig, name);
- };
-
- loggerService.$inject = ['$log', constants.ID.lodash, constants.ID.moment, constants.ID.AppConfig];
-
export interface ILoggerFactory {
/**
* Get the logger for the given name.
*/
- (name: string): Logger;
+ (name: string): ILogger;
+
+ /**
+ * Get a logger for the given object. The name of the logger is inferred
+ * from the name of the constructor function if it is supported by the
+ * browser.
+ */
+ (obj: Object): ILogger;
+
+ /**
+ * Get a logger for the given function. The name of the logger is inferred
+ * from the name of the function if it is supported by the browser.
+ */
+ (obj: Function): ILogger;
+ }
+
+ export interface ILogger {
+ debug(text: string | Object | any[], object?: any);
+ info(text: string | Object | any[], object?: any);
+ warn(text: string | Object | any[], object?: any);
+ error(text: string | Object | any[], object?: any);
}
- // TODO: rewrite as angular decorator on top of $log
export class Logger {
constructor(
private $log: angular.ILogService,
- private _: _.LoDashStatic,
- private moment: moment.MomentStatic,
+ private dateFormatter: (date: Date, format?: string) => string,
private config: constants.IAppConfig,
public name: string) {
}
+ debug(text: string | Object | any[], object?: any) {
+ this.log('debug', text, object);
+ }
+
info(text: string | Object | any[], object?: any) {
this.log('info', text, object);
}
@@ -38,29 +61,46 @@ module <%= prompts.prefix %>.core.util {
this.log('error', text, object);
}
- private log(type: string, text: string | Object | any[], object?: any) {
- if (this.config.environment !== 'production') {
+ private log = (type: string, text: string | Object | any[], object?: any) => {
+ if (this.config.ENVIRONMENT !== 'prod') {
- if (this._.isObject(text) || this._.isArray(text)) {
+ if (angular.isObject(text) || angular.isArray(text)) {
object = text;
text = undefined;
}
text = text || '';
-
- if (this._.isBoolean(object)) {
+
+ if (object === true || object === false) {
object = (object) ? 'YES' : 'NO';
}
object = object || '';
var arrow = (text !== '' || object !== '') ? '=> ' : '';
- this.$log[type]('[' + this.moment().format('HH:mm:ss.ms') + ' - ' + this.name + '] ' + arrow + text, object);
+ this.$log[type](`[${this.dateFormatter(new Date(), 'HH:mm:ss.sss')} - ${this.name}] ${arrow}${text}`, object);
}
- }
+ };
}
+ var loggerService = ($log, $filter, appConfig: constants.IAppConfig): ILoggerFactory => (obj: string | Object | Function) => {
+ var name: string;
+ if (angular.isObject(obj) && obj.constructor && obj.constructor.name) {
+ name = obj.constructor.name;
+ } else if (angular.isFunction(obj) && (obj).name) {
+ name = (obj).name;
+ } else {
+ name = obj || '';
+ }
+
+ return new Logger($log, $filter('date'), appConfig, name);
+ };
+
+ loggerService.$inject = ['$log', '$filter', constants.ID.AppConfig];
+
angular
- .module(ID.LoggerFactory, [])
+ .module(ID.LoggerFactory, [
+ constants.Namespace
+ ])
.factory(ID.LoggerFactory, loggerService);
}
diff --git a/app/templates/src/app-ts/core/util/util.module.ts b/app/templates/src/app-ts/core/util/util.module.ts
index d77415f..ebbcdae 100644
--- a/app/templates/src/app-ts/core/util/util.module.ts
+++ b/app/templates/src/app-ts/core/util/util.module.ts
@@ -7,12 +7,14 @@ module <%= prompts.prefix %>.core.util {
export var ID = {
AppEvents: `${Namespace}.Events`,
- LoggerFactory: `${Namespace}.Logger`
+ LoggerFactory: `${Namespace}.Logger`,
+ Backend: `${Namespace}.Backend`
};
angular
.module(Namespace, [
ID.AppEvents,
- ID.LoggerFactory
+ ID.LoggerFactory,
+ ID.Backend
]);
}
diff --git a/app/templates/src/app-ts/home/views/home.ts b/app/templates/src/app-ts/home/views/home.ts
index 485d693..4841184 100644
--- a/app/templates/src/app-ts/home/views/home.ts
+++ b/app/templates/src/app-ts/home/views/home.ts
@@ -24,17 +24,17 @@ module <%= prompts.prefix %>.home.views {
title: string;
}
- class HomeController extends common.views.AbstractController implements IHomeController {
+ class HomeController implements IHomeController {
private offs: Function[] = [];
title = 'Hirsch says hi!';
- static $inject = ['$state', core.util.ID.AppEvents];
- constructor($state, events: core.util.IAppEvents) {
- super($state);
-
+ static $inject = ['$scope', core.util.ID.AppEvents];
+ constructor($scope, events: core.util.IAppEvents) {
this.offs.push(events.on('someEvent', this.onSomeEvent));
+ $scope.$on('$destroy', () => this.dispose());
+
this.activate();
}
@@ -46,8 +46,7 @@ module <%= prompts.prefix %>.home.views {
// run initialization logic
};
- protected dispose() {
- super.dispose();
+ private dispose() {
this.offs.forEach(off => off());
}
}
diff --git a/constant/index.js b/constant/index.js
index 73fa21d..5fa4b9a 100644
--- a/constant/index.js
+++ b/constant/index.js
@@ -8,6 +8,7 @@ var Generator = module.exports = function Generator() {
ScriptBase.apply(this, arguments);
this.generatorName = 'constant';
this.dirName = 'constants';
+ this.$namespace = this.dirName;
};
util.inherits(Generator, ScriptBase);
@@ -21,15 +22,19 @@ Generator.prototype.prompting = function () {
this.modulePrompt();
};
+Generator.prototype.folderPrompting = function () {
+ this.folderPrompt(this.dirName);
+};
+
Generator.prototype.initComponents = function () {
- this.readComponents(this.module, this.generatorName);
+ this.readComponents(this.module, this.dirName);
};
Generator.prototype.createFiles = function createFiles() {
this.appTemplate(this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
this.testTemplate('unit', this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
if (!this.env.options.typescript) {
- this.appTemplate('sub.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate('sub.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}
};
diff --git a/directive/index.js b/directive/index.js
index 2be329a..2fd0adb 100644
--- a/directive/index.js
+++ b/directive/index.js
@@ -8,6 +8,7 @@ var Generator = module.exports = function Generator() {
ScriptBase.apply(this, arguments);
this.generatorName = 'directive';
this.dirName = 'directives';
+ this.$namespace = this.dirName;
};
util.inherits(Generator, ScriptBase);
@@ -20,6 +21,10 @@ Generator.prototype.prompting = function () {
this.modulePrompt();
};
+Generator.prototype.folderPrompting = function () {
+ this.folderPrompt(this.dirName);
+};
+
Generator.prototype.options = function () {
var done = this.async();
var prompts = [
@@ -54,7 +59,7 @@ Generator.prototype.options = function () {
this.hasTemplate = props.hasTemplate;
this.hasController = props.hasController;
this.hasLinkFnc = props.hasLinkFnc;
- var relModulePath = path.join(this.module, this.dirName, this.name + '.' + this.generatorName + '.html').toLowerCase();
+ var relModulePath = path.join(this.module, this.dirName, this.name + '.' + this.generatorName + '.html');
var relAppPath = path.join(this.env.options.appPath, relModulePath).replace(/^src/, '').replace(/\\/g, '/').replace(/^\//, '');
this.templateUrl = relAppPath;
done();
@@ -62,7 +67,7 @@ Generator.prototype.options = function () {
};
Generator.prototype.initComponents = function () {
- this.readComponents(this.module, this.generatorName, function () {
+ this.readComponents(this.module, this.dirName, function () {
this.components = this.components.map(function (c) {
return c.replace(/Directive$/, '');
});
@@ -72,9 +77,9 @@ Generator.prototype.initComponents = function () {
Generator.prototype.createFiles = function createFiles() {
this.appTemplate(this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
if (this.env.options.typescript) {
- this.appTemplate(this.dirName + '.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate(this.generatorName + 's.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}else{
- this.appTemplate('sub.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate('sub.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}
if (this.hasTemplate) {
this.htmlTemplate(this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
diff --git a/filter/index.js b/filter/index.js
index 8e6e282..155f4f3 100644
--- a/filter/index.js
+++ b/filter/index.js
@@ -8,6 +8,7 @@ var Generator = module.exports = function Generator() {
ScriptBase.apply(this, arguments);
this.generatorName = 'filter';
this.dirName = 'filters';
+ this.$namespace = this.dirName;
};
util.inherits(Generator, ScriptBase);
@@ -21,16 +22,20 @@ Generator.prototype.prompting = function () {
this.modulePrompt();
};
+Generator.prototype.folderPrompting = function () {
+ this.folderPrompt(this.dirName);
+};
+
Generator.prototype.initComponents = function () {
- this.readComponents(this.module, this.generatorName);
+ this.readComponents(this.module, this.dirName);
};
Generator.prototype.createFiles = function createFiles() {
this.appTemplate(this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
if (this.env.options.typescript) {
- this.appTemplate(this.dirName + '.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate(this.generatorName + 's.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}else{
- this.appTemplate('sub.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate('sub.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}
this.testTemplate('unit', this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
};
diff --git a/package.json b/package.json
index 916f9b6..447a4e9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "generator-hirsch",
- "version": "1.0.0-rc.21",
+ "version": "1.0.0-rc.22",
"description": "Yeoman generator for large module-based AngularJS applications with the option to use typescript",
"license": "MIT",
"main": "app/index.js",
diff --git a/script-base.js b/script-base.js
index 271b255..8f1f070 100644
--- a/script-base.js
+++ b/script-base.js
@@ -38,6 +38,8 @@ var Generator = module.exports = function Generator() {
this.prefix = packageJson.prefix;
this.components = [];
+ this.typingNesting = '';
+
this.env.options.srcPath = projectConfig.path.srcDir;
this.env.options.appPath = path.join(projectConfig.path.srcDir, projectConfig.path.appDir);
@@ -74,12 +76,14 @@ Generator.prototype.readModules = function (cb) {
}.bind(this));
};
-Generator.prototype.readComponents = function (module, type, cb) {
+Generator.prototype.readComponents = function (module, dirName, cb) {
var done = this.async();
- hirschUtils.getComponentsFromFileStructure(this, module || 'common', type, function (components) {
+ hirschUtils.getComponentsFromFileStructure(this, module || 'common', dirName, function (components) {
var self = this;
_.forEach(components, function (item) {
- self.components.push(item);
+ if (item !== self.classedName) {
+ self.components.push(item);
+ }
});
if (cb) {
cb();
@@ -110,6 +114,30 @@ Generator.prototype.modulePrompt = function () {
}.bind(this));
};
+Generator.prototype.folderPrompt = function($default, cb) {
+ var done = this.async();
+ var prompts = [
+ {
+ name: 'folder',
+ message: 'In which folder should the file be placed?',
+ default: $default
+ }
+ ];
+
+ this.prompt(prompts, function(props) {
+ this.dirName = props.folder.replace(/(\/|\\)/g, '/').replace(/(^\/|\/$)/g, '');
+ this.$namespace = this.dirName.replace(/\//g, '.');
+ var levels = (this.$namespace.match(/\./g) || []).length;
+ while (levels--) {
+ this.typingNesting += '../';
+ }
+ if (cb) {
+ cb(this.folder);
+ }
+ done();
+ }.bind(this));
+}
+
Generator.prototype.appTemplate = function (src, dest) {
yeoman.generators.Base.prototype.template.apply(this, [
src + this.scriptSuffix,
@@ -121,9 +149,12 @@ Generator.prototype.appTemplate = function (src, dest) {
Generator.prototype.testTemplate = function (type, src, dest) {
type = type || 'unit';
+ dest = path.join(this.env.options.testPath[type], dest);
+ dest += dest.indexOf('.spec') >= 0 ? '' : '.spec';
+ dest += this.scriptSuffix;
yeoman.generators.Base.prototype.template.apply(this, [
src + '.' + type + '.spec' + this.scriptSuffix,
- path.join(this.env.options.testPath[type], dest) + this.scriptSuffix,
+ dest,
this,
{ interpolate: /<%=([\s\S]+?)%>/g }
]);
diff --git a/service/index.js b/service/index.js
index f6de6bb..800df92 100644
--- a/service/index.js
+++ b/service/index.js
@@ -8,6 +8,7 @@ var Generator = module.exports = function Generator() {
ScriptBase.apply(this, arguments);
this.generatorName = 'service';
this.dirName = 'services';
+ this.$namespace = this.dirName;
};
util.inherits(Generator, ScriptBase);
@@ -21,8 +22,12 @@ Generator.prototype.prompting = function () {
this.modulePrompt();
};
+Generator.prototype.folderPrompting = function () {
+ this.folderPrompt(this.dirName);
+};
+
Generator.prototype.initComponents = function () {
- this.readComponents(this.module, this.generatorName);
+ this.readComponents(this.module, this.dirName);
};
Generator.prototype.promptForServiceType = function () {
@@ -44,15 +49,15 @@ Generator.prototype.promptForServiceType = function () {
};
Generator.prototype.createFiles = function createFiles() {
- var templateName = (this.useFactory) ?'factory':'service';
+ var templateName = (this.useFactory && !this.env.options.typescript) ?'factory':'service';
this.appTemplate(templateName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
this.testTemplate('unit', templateName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));
if (this.env.options.typescript) {
- this.appTemplate(this.dirName + '.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate(this.generatorName + 's.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
} else {
- this.appTemplate('sub.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate('sub.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}
};
diff --git a/templates/typescript/directive.ts b/templates/typescript/directive.ts
index 58eb5c5..abc48e3 100644
--- a/templates/typescript/directive.ts
+++ b/templates/typescript/directive.ts
@@ -1,6 +1,6 @@
-///
+///
-module <%= prefix %>.<%= module %>.directives {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
class <%= classedName %>Directive implements angular.IDirective {
diff --git a/templates/typescript/directive.unit.spec.ts b/templates/typescript/directive.unit.spec.ts
index d2c0e09..7c3cfb9 100644
--- a/templates/typescript/directive.unit.spec.ts
+++ b/templates/typescript/directive.unit.spec.ts
@@ -1,6 +1,6 @@
-///
+///
-module <%= prefix %>.<%= module %>.directives.test {
+module <%= prefix %>.<%= module %>.<%= $namespace %>.test {
'use strict';
describe(`Unit: ${Namespace}.<%= classedName %>Directive`, () => {
diff --git a/templates/typescript/directives.module.ts b/templates/typescript/directives.module.ts
index 420568a..8469478 100644
--- a/templates/typescript/directives.module.ts
+++ b/templates/typescript/directives.module.ts
@@ -1,9 +1,9 @@
-///
+///
-module <%= prefix %>.<%= module %>.directives {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
- export var Namespace = '<%= prefix %>.<%= module %>.directives';
+ export var Namespace = '<%= prefix %>.<%= module %>.<%= $namespace %>';
angular
.module(Namespace, [<% for (var i = 0, l = components.length; i < l; i++) { %>
diff --git a/templates/typescript/filter.ts b/templates/typescript/filter.ts
index 7ffaa2f..e568aea 100644
--- a/templates/typescript/filter.ts
+++ b/templates/typescript/filter.ts
@@ -1,6 +1,6 @@
-///
+///
-module <%= prefix %>.<%= module %>.filters {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
export interface I<%= classedName %>Filter {
diff --git a/templates/typescript/filter.unit.spec.ts b/templates/typescript/filter.unit.spec.ts
index fe31fa9..108ce8b 100644
--- a/templates/typescript/filter.unit.spec.ts
+++ b/templates/typescript/filter.unit.spec.ts
@@ -1,6 +1,6 @@
-///
+///
-module <%= prefix %>.<%= module %>.filters.test {
+module <%= prefix %>.<%= module %>.<%= $namespace %>.test {
'use strict';
describe(`Unit: ${Namespace}.<%= classedName %>Filter`, () => {
diff --git a/templates/typescript/filters.module.ts b/templates/typescript/filters.module.ts
index 855c82a..17c3d2d 100644
--- a/templates/typescript/filters.module.ts
+++ b/templates/typescript/filters.module.ts
@@ -1,9 +1,9 @@
-///
+///
-module <%= prefix %>.<%= module %>.filters {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
- export var Namespace = '<%= prefix %>.<%= module %>.filters';
+ export var Namespace = '<%= prefix %>.<%= module %>.<%= $namespace %>';
angular
.module(Namespace, [<% for (var i = 0, l = components.length; i < l; i++) { %>
diff --git a/templates/typescript/module.midway.spec.ts b/templates/typescript/module.midway.spec.ts
index 2af8dae..a8ac8bc 100644
--- a/templates/typescript/module.midway.spec.ts
+++ b/templates/typescript/module.midway.spec.ts
@@ -1,4 +1,4 @@
-///
+///
module <%= prefix %>.<%= cameledName %>.test {
'use strict';
diff --git a/templates/typescript/module.ts b/templates/typescript/module.ts
index 1bb059e..1c65c00 100644
--- a/templates/typescript/module.ts
+++ b/templates/typescript/module.ts
@@ -1,4 +1,4 @@
-///
+///
module <%= prefix %>.<%= cameledName %> {
'use strict';
diff --git a/templates/typescript/service.ts b/templates/typescript/service.ts
index d983b04..9695a2a 100644
--- a/templates/typescript/service.ts
+++ b/templates/typescript/service.ts
@@ -1,15 +1,16 @@
-///
+///
-module <%= prefix %>.<%= module %>.services {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
- export interface I<%= classedName %>Service {
+ export interface I<%= classedName %> {
method(param: string): string;
}
- class <%= classedName %>Service implements I<%= classedName %>Service {
+ class <%= classedName %> implements I<%= classedName %> {
private field;
+ static $inject = [];
constructor() {
this.field = 'value';
}
@@ -19,16 +20,16 @@ module <%= prefix %>.<%= module %>.services {
}
}<% if(useFactory) { %>
- var factory = (): I<%= classedName %>Service => {
+ var factory = (): I<%= classedName %> => {
// TODO: private initialization code
// TODO: pass initialization parameters to class
- return new <%= classedName %>Service();
+ return new <%= classedName %>();
};
factory.$inject = [];<% } %>
angular
- .module(ID.<%= classedName %>Service, [])<% if(!useFactory) { %>
- .service(ID.<%= classedName %>Service, <%= classedName %>Service)<% } %><% if(useFactory) { %>
- .factory(ID.<%= classedName %>Service, factory)<% } %>;
+ .module(ID.<%= classedName %>, [])<% if(!useFactory) { %>
+ .service(ID.<%= classedName %>, <%= classedName %>)<% } %><% if(useFactory) { %>
+ .factory(ID.<%= classedName %>, factory)<% } %>;
}
diff --git a/templates/typescript/service.unit.spec.ts b/templates/typescript/service.unit.spec.ts
index 24de629..ba1d805 100644
--- a/templates/typescript/service.unit.spec.ts
+++ b/templates/typescript/service.unit.spec.ts
@@ -1,14 +1,14 @@
-///
+///
-module <%= prefix %>.<%= module %>.services.test {
+module <%= prefix %>.<%= module %>.<%= $namespace %>.test {
'use strict';
- describe(`Unit: ${Namespace}.<%= classedName %>Service`, () => {
+ describe(`Unit: ${Namespace}.<%= classedName %>`, () => {
beforeEach(module(Namespace));
- var service: I<%= classedName %>Service;
- beforeEach(angular.mock.inject([ID.<%= classedName %>Service, s => service = s]));
+ var service: I<%= classedName %>;
+ beforeEach(angular.mock.inject([ID.<%= classedName %>, s => service = s]));
it('should contain a <%= classedName %> service', () => should.exist(service));
diff --git a/templates/typescript/services.module.ts b/templates/typescript/services.module.ts
index 7a00352..e26281b 100644
--- a/templates/typescript/services.module.ts
+++ b/templates/typescript/services.module.ts
@@ -1,18 +1,18 @@
-///
+///
-module <%= prefix %>.<%= module %>.services {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
- export var Namespace = '<%= prefix %>.<%= module %>.services';
+ export var Namespace = '<%= prefix %>.<%= module %>.<%= $namespace %>';
export var ID = {<% for (var i = 0, l = components.length; i < l; i++) { %>
<%= components[i] %>: `${Namespace}.<%= components[i] %>`, <% } %>
- <%= classedName %>Service: `${Namespace}.<%= classedName %>Service`
+ <%= classedName %>: `${Namespace}.<%= classedName %>`
};
angular
.module(Namespace, [<% for (var i = 0, l = components.length; i < l; i++) { %>
ID.<%= components[i] %>, <% } %>
- ID.<%= classedName %>Service
+ ID.<%= classedName %>
]);
}
diff --git a/templates/typescript/view.ts b/templates/typescript/view.ts
index fb979aa..828dffe 100644
--- a/templates/typescript/view.ts
+++ b/templates/typescript/view.ts
@@ -1,6 +1,6 @@
-///
+///
-module <%= prefix %>.<%= module %>.views {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
var stateConfig = ($stateProvider: ng.ui.IStateProvider) => {
@@ -22,25 +22,18 @@ module <%= prefix %>.<%= module %>.views {
export interface I<%= classedName %>Controller {
prop: string;
- asyncProp: string[];
+ asyncProp: ng.IPromise;
method(param: string): string;
action(): void;
}
- class <%= classedName %>Controller extends common.views.AbstractController implements I<%= classedName %>Controller {
- private offs: Function[] = [];
-
+ class <%= classedName %>Controller implements I<%= classedName %>Controller {
prop: string;
- asyncProp: string[];
+ asyncProp: ng.IPromise;
- static $inject = ['$state', core.util.ID.AppEvents];
- constructor($state, events: core.util.IAppEvents) {
- super($state);
-
+ static $inject = [];
+ constructor() {
this.prop = '';
- this.asyncProp = [];
-
- this.offs.push(events.on('someEvent', this.onSomeEvent));
this.activate();
}
@@ -53,25 +46,14 @@ module <%= prefix %>.<%= module %>.views {
// TODO: perform some action
};
- private onSomeEvent = (eventObj: any) => {
- // TODO: handle event
- };
-
private activate = () => {
// TODO: call some service to asynchronously return data
- // this.someService.getData().then(data => this.asyncProp = data);
+ // this.asyncProp = this.someService.getData();
};
-
- protected dispose() {
- super.dispose();
- this.offs.forEach(off => off());
- }
}
angular
- .module(`${Namespace}.<%= classedName %>`, [
- core.util.Namespace
- ])
+ .module(`${Namespace}.<%= classedName %>`, [])
.config(stateConfig)
.controller(ID.<%= classedName %>Controller, <%= classedName %>Controller);
}
diff --git a/templates/typescript/view.unit.spec.ts b/templates/typescript/view.unit.spec.ts
index d0bb121..c607612 100644
--- a/templates/typescript/view.unit.spec.ts
+++ b/templates/typescript/view.unit.spec.ts
@@ -1,6 +1,6 @@
-///
+///
-module <%= prefix %>.<%= module %>.views.test {
+module <%= prefix %>.<%= module %>.<%= $namespace %>.test {
'use strict';
describe(`Unit: ${Namespace}.<%= classedName %>Controller`, () => {
diff --git a/templates/typescript/views.module.ts b/templates/typescript/views.module.ts
index 5ff1402..88301ad 100644
--- a/templates/typescript/views.module.ts
+++ b/templates/typescript/views.module.ts
@@ -1,9 +1,9 @@
-///
+///
-module <%= prefix %>.<%= module %>.views {
+module <%= prefix %>.<%= module %>.<%= $namespace %> {
'use strict';
- export var Namespace = '<%= prefix %>.<%= module %>.views';
+ export var Namespace = '<%= prefix %>.<%= module %>.<%= $namespace %>';
angular
.module(Namespace, [
diff --git a/util.js b/util.js
index 610e6ae..6e0c92e 100644
--- a/util.js
+++ b/util.js
@@ -56,11 +56,14 @@ module.exports = {
});
},
- getComponentsFromFileStructure: function (scope, module, type, cb) {
- fs.readdir(scope.destinationPath(path.join(scope.env.options.appPath, module, type + 's')), function (err, files) {
+ getComponentsFromFileStructure: function (scope, module, dirName, cb) {
+ var fullDirName = scope.destinationPath(path.join(scope.env.options.appPath, module, dirName));
+ fs.readdir(fullDirName, function (err, files) {
if (files) {
var components = _.uniq(files.filter(function(f) {
return f.indexOf('.module') === -1;
+ }).filter(function(f) {
+ return !fs.statSync(path.join(fullDirName, f)).isDirectory();
}).map(function(f) {
return f.replace(/\.(js|ts|html|js\.map)$/, '');
}).map(_s.classify));
diff --git a/view/index.js b/view/index.js
index 5536fc1..73affc5 100644
--- a/view/index.js
+++ b/view/index.js
@@ -8,6 +8,7 @@ var Generator = module.exports = function Generator() {
ScriptBase.apply(this, arguments);
this.generatorName = 'view';
this.dirName = 'views';
+ this.$namespace = this.dirName;
};
util.inherits(Generator, ScriptBase);
@@ -20,6 +21,10 @@ Generator.prototype.prompting = function () {
this.modulePrompt();
};
+Generator.prototype.folderPrompting = function () {
+ this.folderPrompt(this.dirName);
+};
+
Generator.prototype.options = function () {
var done = this.async();
var prompts = [
@@ -32,7 +37,7 @@ Generator.prototype.options = function () {
this.prompt(prompts, function (props) {
this.url = props.url;
- var relModulePath = path.join(this.module, this.dirName, this.name + '.html').toLowerCase();
+ var relModulePath = path.join(this.module, this.dirName, this.name + '.html');
var relAppPath = path.join(this.env.options.appPath, relModulePath).replace(/^src/, '').replace(/\\/g, '/').replace(/^\//, '');
this.templateUrl = relAppPath;
done();
@@ -40,15 +45,15 @@ Generator.prototype.options = function () {
};
Generator.prototype.initComponents = function () {
- this.readComponents(this.module, this.generatorName);
+ this.readComponents(this.module, this.dirName);
};
Generator.prototype.createFiles = function createFiles() {
this.appTemplate(this.generatorName, path.join(this.module, this.dirName, this.name));
if (this.env.options.typescript) {
- this.appTemplate(this.dirName + '.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate(this.generatorName + 's.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}else{
- this.appTemplate('sub.module', path.join(this.module, this.dirName, this.dirName + '.module'));
+ this.appTemplate('sub.module', path.join(this.module, this.dirName, path.basename(this.dirName) + '.module'));
}
this.htmlTemplate(this.generatorName, path.join(this.module, this.dirName, this.name));
this.testTemplate('unit', this.generatorName, path.join(this.module, this.dirName, this.name + '.' + this.generatorName));