diff --git a/templates/app/client/app/account(auth)/signup/signup.component.js b/templates/app/client/app/account(auth)/signup/signup.component.js
new file mode 100644
index 000000000..c8138cab2
--- /dev/null
+++ b/templates/app/client/app/account(auth)/signup/signup.component.js
@@ -0,0 +1,80 @@
+// @flow
+import { Component } from '@angular/core';
+<%_ if(filters.uirouter) { -%>
+import { StateService } from 'ui-router-ng2';<% } %>
+<%_ if(filters.ngroute) { -%><% } %>
+import { AuthService } from '../../../components/auth/auth.service';
+import {ANGULARCLASS_MATCH_CONTROL_DIRECTIVES} from '@angularclass/match-control';
+
+<%_ if(filters.flow) { -%>
+type User = {
+ name: string;
+ email: string;
+ password: string;
+};<% } %>
+<%_ if(filters.ts) { -%>
+interface User {
+ name: string;
+ email: string;
+ password: string;
+}<% } %>
+
+export let SignupComponent = @Component({
+ selector: 'signup',
+ template: require('./signup.<%=templateExt%>'),
+ directives: [...ANGULARCLASS_MATCH_CONTROL_DIRECTIVES]
+})
+class SignupComponent {
+ user: User = {
+ name: '',
+ email: '',
+ password: ''
+ };
+ errors = {};
+ submitted = false;
+ AuthService;
+ <%_ if(filters.ngroute) { -%><% } %>
+ <%_ if(filters.uirouter) { -%>
+ StateService;<% } %>
+
+ static parameters = [AuthService, <% if(filters.ngroute) { %><% } else { %>StateService<% } %>];
+ constructor(_AuthService_: AuthService, <% if(filters.ngroute) { %><% } else { %>_StateService_: StateService<% } %>) {
+ this.AuthService = _AuthService_;
+ <%_ if(filters.ngroute) { -%><% } -%>
+ <%_ if(filters.uirouter) { -%>
+ this.StateService = _StateService_;<% } -%>
+ }
+
+ register(form) {
+ this.submitted = true;
+
+ return this.AuthService.createUser({
+ name: this.user.name,
+ email: this.user.email,
+ password: this.user.password
+ })
+ .then(() => {
+ // Account created, redirect to home
+ <% if(filters.ngroute) { %>this.$location.path('/');<% } -%>
+ <% if(filters.uirouter) { %>this.StateService.go('main');<% } -%>
+ })
+ .catch(err => {
+ err = err.data;
+ this.errors = {};
+ <%_ if(filters.mongooseModels) { -%>
+ // Update validity of form fields that match the mongoose errors
+ err.errors.forEach((error, field) => {
+ // form[field].$setValidity('mongoose', false);
+ this.errors[field] = error.message;
+ });<% } %>
+ <%_ if(filters.sequelizeModels) { -%>
+ // Update validity of form fields that match the sequelize errors
+ if(err.name) {
+ err.fields.forEach(field => {
+ // form[field].$setValidity('mongoose', false);
+ this.errors[field] = err.message;
+ });
+ }<% } %>
+ });
+ }
+}
diff --git a/templates/app/client/app/account(auth)/signup/signup.controller.js b/templates/app/client/app/account(auth)/signup/signup.controller.js
deleted file mode 100644
index 85e71d28f..000000000
--- a/templates/app/client/app/account(auth)/signup/signup.controller.js
+++ /dev/null
@@ -1,77 +0,0 @@
-'use strict';
-// @flow
-import angular from 'angular';
-
-<%_ if(filters.flow) { -%>
-type User = {
- name: string;
- email: string;
- password: string;
-};
-<%_ } -%>
-<%_ if(filters.ts) { -%>
-interface User {
- name: string;
- email: string;
- password: string;
-}
-<%_ } -%>
-
-export default class SignupController {
- user: User = {
- name: '',
- email: '',
- password: ''
- };
- errors = {};
- submitted = false;
- Auth;
- <%_ if(filters.ngroute) { -%>
- $location;
- <%_ } if(filters.uirouter) { -%>
- $state;<% } %>
-
- /*@ngInject*/
- constructor(Auth<% if (filters.ngroute) { %>, $location<% } %><% if (filters.uirouter) { %>, $state<% } %>) {
- this.Auth = Auth;
- <%_ if(filters.ngroute) { -%>
- this.$location = $location;
- <%_ } if(filters.uirouter) { -%>
- this.$state = $state;<% } %>
- }
-
- register(form) {
- this.submitted = true;
-
- if(form.$valid) {
- return this.Auth.createUser({
- name: this.user.name,
- email: this.user.email,
- password: this.user.password
- })
- .then(() => {
- // Account created, redirect to home
- <% if(filters.ngroute) { %>this.$location.path('/');<% } -%>
- <% if(filters.uirouter) { %>this.$state.go('main');<% } -%>
- })
- .catch(err => {
- err = err.data;
- this.errors = {};
- <%_ if(filters.mongooseModels) { -%>
- // Update validity of form fields that match the mongoose errors
- angular.forEach(err.errors, (error, field) => {
- form[field].$setValidity('mongoose', false);
- this.errors[field] = error.message;
- });<% } %>
- <%_ if(filters.sequelizeModels) { -%>
- // Update validity of form fields that match the sequelize errors
- if(err.name) {
- angular.forEach(err.fields, field => {
- form[field].$setValidity('mongoose', false);
- this.errors[field] = err.message;
- });
- }<% } %>
- });
- }
- }
-}
diff --git a/templates/app/client/app/app(css).css b/templates/app/client/app/app(css).css
index d1b63a10f..742a27394 100644
--- a/templates/app/client/app/app(css).css
+++ b/templates/app/client/app/app(css).css
@@ -52,6 +52,10 @@
}
}<% } %>
+[hidden] {
+ display: none !important;
+}
+
/* Component styles are injected through gulp */
/* inject:css */
@import 'admin/admin.css';
diff --git a/templates/app/client/app/app(less).less b/templates/app/client/app/app(less).less
index 191118fbb..3876bd607 100644
--- a/templates/app/client/app/app(less).less
+++ b/templates/app/client/app/app(less).less
@@ -23,6 +23,11 @@
}
}
<% } %>
+
+[hidden] {
+ display: none !important;
+}
+
/* inject:less */
@import 'admin/admin.less';
@import 'main/main.less';
diff --git a/templates/app/client/app/app(sass).scss b/templates/app/client/app/app(sass).scss
index 1f774272e..21a563b1f 100644
--- a/templates/app/client/app/app(sass).scss
+++ b/templates/app/client/app/app(sass).scss
@@ -25,10 +25,18 @@ $fa-font-path: '/assets/fonts/font-awesome/';
.container {
max-width: 730px;
}
+}<% } %>
+
+[hidden] {
+ display: none !important;
+}
+
+.ng-valid[required], .ng-valid.required {
+ border-left: 5px solid #42A948; /* green */
}
-<% } %>
-// Component styles are injected through gulp
-/* inject:scss */
+
+.ng-invalid:not(form) {
+ border-left: 5px solid #a94442; /* red */
+}
+
@import 'admin/admin.scss';
-@import 'main/main.scss';
-/* endinject */
diff --git a/templates/app/client/app/app(stylus).styl b/templates/app/client/app/app(stylus).styl
index a38c5b193..6597f3eda 100644
--- a/templates/app/client/app/app(stylus).styl
+++ b/templates/app/client/app/app(stylus).styl
@@ -22,6 +22,10 @@ $icon-font-path = '../assets/fonts/bootstrap/'
.container
max-width 730px
<% } %>
+
+[hidden]
+ display: none !important
+
// Component styles are injected through gulp
/* inject:styl */
@import "admin/admin"
diff --git a/templates/app/client/app/app.component.js b/templates/app/client/app/app.component.js
new file mode 100644
index 000000000..7f0859ba3
--- /dev/null
+++ b/templates/app/client/app/app.component.js
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+export let AppComponent = @Component({
+ selector: 'app',
+ template: `
+
+
`
+})
+class AppComponent {}
diff --git a/templates/app/client/app/app.constants.js b/templates/app/client/app/app.constants.js
index 885cf21a1..730aed801 100644
--- a/templates/app/client/app/app.constants.js
+++ b/templates/app/client/app/app.constants.js
@@ -1,6 +1 @@
-'use strict';
-import angular from 'angular';
-
-export default angular.module('<%= scriptAppName %>.constants', [])
- .constant('appConfig', require('../../server/config/environment/shared'))
- .name;
+export default from '../../server/config/environment/shared';
diff --git a/templates/app/client/app/app.js b/templates/app/client/app/app.js
index 2937399dd..1c1e40634 100644
--- a/templates/app/client/app/app.js
+++ b/templates/app/client/app/app.js
@@ -1,81 +1,23 @@
-'use strict';
-import angular from 'angular';
-// import ngAnimate from 'angular-animate';
-import ngCookies from 'angular-cookies';
-import ngResource from 'angular-resource';
-import ngSanitize from 'angular-sanitize';
-<%_ if(filters.socketio) { _%>
-import 'angular-socket-io';<% } %>
-<%_ if(filters.ngroute) { _%>
-const ngRoute = require('angular-route');<% } %>
-<%_ if(filters.uirouter) { _%>
-import uiRouter from 'angular-ui-router';<% } %>
-<%_ if(filters.uibootstrap) { _%>
-import uiBootstrap from 'angular-ui-bootstrap';<% } %>
-// import ngMessages from 'angular-messages';
-<%_ if(filters.auth) { _%>
-// import ngValidationMatch from 'angular-validation-match';<% } %>
+import '!!style!css!sass!./app.<%= styleExt %>';
+import './polyfills';
-import {routeConfig} from './app.config';
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-<%_ if(filters.auth) { _%>
-import _Auth from '../components/auth/auth.module';
-import account from './account';
-import admin from './admin';<% } %>
-import navbar from '../components/navbar/navbar.component';
-import footer from '../components/footer/footer.component';
-import main from './main/main.component';
-import constants from './app.constants';
-import util from '../components/util/util.module';
-<%_ if(filters.socketio) { _%>
-import socket from '../components/socket/socket.service';<% } %>
+// depending on the env mode, enable prod mode or add debugging modules
+if(process.env.NODE_ENV === 'production') {
+ enableProdMode();
+}
+import { AppModule } from './app.module';
-import './app.<%= styleExt %>';
+export function main() {
+ return platformBrowserDynamic().bootstrapModule(AppModule);
+}
-angular.module('<%= scriptAppName %>', [
- ngCookies,
- ngResource,
- ngSanitize,
- <%_ if(filters.socketio) { %>
- 'btford.socket-io',<% } %>
- <%_ if(filters.ngroute) { %>
- ngRoute,<% } _%>
- <%_ if(filters.uirouter) { %>
- uiRouter,<% } _%>
- <%_ if(filters.uibootstrap) { %>
- uiBootstrap,<% } %>
- <%_ if(filters.auth) { %>
- _Auth,
- account,
- admin,<% } _%>
- navbar,
- footer,
- main,
- constants,
- <%_ if(filters.socketio) { _%>
- socket,<% } %>
- util
-])
- .config(routeConfig)
- <%_ if(filters.auth) { _%>
- .run(function($rootScope, $location, Auth) {
- 'ngInject';
- // Redirect to login if route requires auth and you're not logged in
- $rootScope.$on('$stateChangeStart', function(event, next) {
- Auth.isLoggedIn(function(loggedIn) {
- if(next.authenticate && !loggedIn) {
- $location.path('/login');
- }
- });
- });
- })<% } %>;
-
-angular
- .element(document)
- .ready(() => {
- angular.bootstrap(document, ['<%= scriptAppName %>'], {
- strictDi: true
- });
- });
+if(document.readyState === 'complete') {
+ main();
+} else {
+ document.addEventListener('DOMContentLoaded', main);
+}
diff --git a/templates/app/client/app/app.module.js b/templates/app/client/app/app.module.js
new file mode 100644
index 000000000..cb9aed6f4
--- /dev/null
+++ b/templates/app/client/app/app.module.js
@@ -0,0 +1,102 @@
+// import angular from 'angular';
+// // import ngAnimate from 'angular-animate';
+// import ngCookies from 'angular-cookies';
+// import ngResource from 'angular-resource';
+// import ngSanitize from 'angular-sanitize';
+// <%_ if(filters.socketio) { _%>
+// import 'angular-socket-io';<% } %>
+// <%_ if(filters.ngroute) { _%>
+// const ngRoute = require('angular-route');<% } %>
+// <%_ if(filters.uirouter) { _%>
+// import uiRouter from 'angular-ui-router';<% } %>
+// <%_ if(filters.uibootstrap) { _%>
+// import uiBootstrap from 'angular-ui-bootstrap';<% } %>
+// // import ngMessages from 'angular-messages';
+// <%_ if(filters.auth) { _%>
+// // import ngValidationMatch from 'angular-validation-match';<% } %>
+
+// import {routeConfig} from './app.config';
+
+// <%_ if(filters.auth) { _%>
+// import _Auth from '../components/auth/auth.module';
+// import account from './account';
+// import admin from './admin';<% } %>
+// import navbar from '../components/navbar/navbar.component';
+// import footer from '../components/footer/footer.component';
+// import main from './main/main.component';
+// import constants from './app.constants';
+// import util from '../components/util/util.module';
+// <%_ if(filters.socketio) { _%>
+// import socket from '../components/socket/socket.service';<% } %>
+
+// .config(routeConfig)
+// <%_ if(filters.auth) { _%>
+// .run(function($rootScope, $location, Auth) {
+// 'ngInject';
+// // Redirect to login if route requires auth and you're not logged in
+// $rootScope.$on('$stateChangeStart', function(event, next) {
+// Auth.isLoggedIn(function(loggedIn) {
+// if(next.authenticate && !loggedIn) {
+// $location.path('/login');
+// }
+// });
+// });
+// })<% } %>;
+
+
+import { NgModule, ErrorHandler, Injectable } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import {
+ HttpModule,
+ BaseRequestOptions,
+ RequestOptions,
+ RequestOptionsArgs,
+} from '@angular/http';
+<%_ if (filters.uirouter) { -%>
+import { UIRouterModule } from 'ui-router-ng2';<% } %>
+import { provideAuth } from 'angular2-jwt';
+
+import { AppComponent } from './app.component';
+import { MainModule } from './main/main.module';
+import { DirectivesModule } from '../components/directives.module';
+import { AccountModule } from './account/account.module';
+// import { AdminModule } from './admin/admin.module';
+
+import constants from './app.constants';
+
+let providers = [
+ provideAuth({
+ // Allow using AuthHttp while not logged in
+ noJwtError: true,
+ })
+];
+
+if(constants.env === 'development') {
+ @Injectable()
+ class HttpOptions extends BaseRequestOptions {
+ merge(options/*:RequestOptionsArgs*/)/*:RequestOptions*/ {
+ options.url = `http://localhost:9000${options.url}`;
+ return super.merge(options);
+ }
+ }
+
+ providers.push({ provide: RequestOptions, useClass: HttpOptions });
+}
+
+export let AppModule = @NgModule({
+ providers,
+ imports: [
+ BrowserModule,
+ HttpModule,
+ UIRouterModule.forRoot(),
+ MainModule,
+ DirectivesModule,
+ AccountModule,
+ // AdminModule,
+ ],
+ declarations: [
+ AppComponent,
+ ],
+ bootstrap: [AppComponent]
+})
+class AppModule {}
diff --git a/templates/app/client/app/main/main(html).html b/templates/app/client/app/main/main(html).html
index 13b811715..df7fe9fee 100644
--- a/templates/app/client/app/main/main(html).html
+++ b/templates/app/client/app/main/main(html).html
@@ -10,8 +10,11 @@
'Allo, 'Allo!
<% if (filters.socketio) { %>
@@ -19,9 +22,9 @@
<% } %>
diff --git a/templates/app/client/app/main/main(pug).pug b/templates/app/client/app/main/main(pug).pug
index 5084e5f22..9ad5d0ac9 100644
--- a/templates/app/client/app/main/main(pug).pug
+++ b/templates/app/client/app/main/main(pug).pug
@@ -8,15 +8,15 @@ header#banner.hero-unit
.row
.col-lg-12
h1.page-header Features:
- ul.nav.nav-tabs.nav-stacked.col-md-4.col-lg-4.col-sm-6(ng-repeat='thing in $ctrl.awesomeThings')
+ ul.nav.nav-tabs.nav-stacked.col-md-4.col-lg-4.col-sm-6(ng-repeat='thing in awesomeThings')
li
- a(href='#', uib-tooltip='{{thing.info}}')
- | {{thing.name}}<% if (filters.socketio) { %>
- button.close(type='button', ng-click='$ctrl.deleteThing(thing)') ×<% } %><% if (filters.socketio) { %>
+ a(href='#', [tooltip]='{{thing.info}}')
+ | {{thing.name}}<% if (filters.models) { %>
+ button.close(type='button', (click)='deleteThing(thing)') ×<% } %><% if (filters.socketio) { %>
form.thing-form
label Syncs in realtime across clients
p.input-group
- input.form-control(type='text', placeholder='Add a new thing here.', ng-model='$ctrl.newThing')
+ input.form-control(type='text', placeholder='Add a new thing here.', ng-model='newThing')
span.input-group-btn
- button.btn.btn-primary(type='submit', ng-click='$ctrl.addThing()') Add New<% } %>
+ button.btn.btn-primary(type='submit', (click)='addThing()') Add New<% } %>
diff --git a/templates/app/client/app/main/main.component.js b/templates/app/client/app/main/main.component.js
index a1dc2479a..ed2927b76 100644
--- a/templates/app/client/app/main/main.component.js
+++ b/templates/app/client/app/main/main.component.js
@@ -1,40 +1,48 @@
-import angular from 'angular';
-<%_ if(filters.ngroute) { _%>
-const ngRoute = require('angular-route');<% } _%>
-<%_ if(filters.uirouter) { _%>
-import uiRouter from 'angular-ui-router';<% } _%>
-
-import routing from './main.routes';
-
-export class MainController {
- $http;
+import { Component, OnInit<% if(filters.socketio) { %>, OnDestroy<% } %> } from '@angular/core';
+import { Http } from '@angular/http';
+import { SocketService } from '../../components/socket/socket.service';
+
+export let MainComponent = @Component({
+ selector: 'main',
+ template: require('./main.<%=templateExt%>'),
+ styles: [require('./main.<%=styleExt%>')],
+})
+class MainComponent implements OnInit<% if(filters.socketio) { %>, OnDestroy<% } %> {
+ Http;
<%_ if(filters.socketio) { -%>
- socket;<% } %>
+ SocketService;<% } %>
awesomeThings = [];
<%_ if(filters.models) { -%>
newThing = '';<% } %>
- /*@ngInject*/
- constructor($http<% if(filters.socketio) { %>, $scope, socket<% } %>) {
- this.$http = $http;
+ static parameters = [Http, SocketService];
+ constructor(_Http_: Http<% if(filters.socketio) { %>, _SocketService_: SocketService<% } %>) {
+ this.Http = _Http_;
<%_ if(filters.socketio) { -%>
- this.socket = socket;
-
- $scope.$on('$destroy', function() {
- socket.unsyncUpdates('thing');
- });<% } %>
+ this.SocketService = _SocketService_;<% } %>
}
- $onInit() {
- this.$http.get('/api/things').then(response => {
- this.awesomeThings = response.data;<% if (filters.socketio) { %>
- this.socket.syncUpdates('thing', this.awesomeThings);<% } %>
- });
+ ngOnInit() {
+ this.Http.get('/api/things')
+ .map(res => {
+ return res.json();
+ <%_ if(filters.socketio) { -%>
+ // this.SocketService.syncUpdates('thing', this.awesomeThings);<% } %>
+ })
+ .catch(err => Observable.throw(err.json().error || 'Server error'))
+ .subscribe(things => {
+ this.awesomeThings = things;
+ });
}<% if (filters.models) { %>
+ <%_ if(filters.socketio) { %>
+
+ ngOnDestroy() {
+ this.SocketService.unsyncUpdates('thing');
+ }<% } %>
addThing() {
- if (this.newThing) {
- this.$http.post('/api/things', { name: this.newThing });
+ if(this.newThing) {
+ this.Http.post('/api/things', { name: this.newThing });
this.newThing = '';
}
}
@@ -43,16 +51,3 @@ export class MainController {
this.$http.delete('/api/things/' + thing._id);
}<% } %>
}
-
-export default angular.module('<%= scriptAppName %>.main', [
- <%_ if(filters.ngroute) { _%>
- ngRoute<% } _%>
- <%_ if(filters.uirouter) { _%>
- uiRouter<% } _%>
-])
- .config(routing)
- .component('main', {
- template: require('./main.<%= templateExt %>'),
- controller: MainController
- })
- .name;
diff --git a/templates/app/client/app/main/main.module.js b/templates/app/client/app/main/main.module.js
new file mode 100644
index 000000000..cad630bfa
--- /dev/null
+++ b/templates/app/client/app/main/main.module.js
@@ -0,0 +1,48 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+<%_ if(filters.uirouter) { %>
+import { UIRouterModule } from 'ui-router-ng2';<% } %>
+<%_ if(filters.ngroute) { %>
+import { RouterModule, Routes } from '@angular/router';<% } %>
+<%_ if(filters.uibootstrap) { %>
+import { TooltipModule } from 'ng2-bootstrap/ng2-bootstrap';<% } %>
+
+import { MainComponent } from './main.component';
+<%_ if(filters.socketio) { -%>
+import { SocketService } from '../../components/socket/socket.service';<% } %>
+
+<%_ if(filters.ngroute) { _%>
+export const ROUTES: Routes = [
+ { path: '', component: MainComponent },
+];<% } %>
+<%_ if(filters.uirouter) { _%>
+export const STATES = [
+ { name: 'main', url: '/', component: MainComponent },
+];<% } %>
+
+export let MainModule = @NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ <%_ if(filters.ngroute) { _%>
+ RouterModule.forChild(ROUTES),<% } %>
+ <%_ if(filters.uirouter) { _%>
+ UIRouterModule.forChild({
+ states: STATES,
+ }),<% } %>
+ <%_ if(filters.uibootstrap) { %>
+ TooltipModule,<% } %>
+ ],
+ declarations: [
+ MainComponent,
+ ],
+ <%_ if(filters.socketio) { -%>
+ providers: [
+ SocketService,
+ ],<% } %>
+ exports: [
+ MainComponent,
+ ],
+})
+class MainModule {}
diff --git a/templates/app/client/app/main/main.routes.js b/templates/app/client/app/main/main.routes.js
deleted file mode 100644
index 95c7968a9..000000000
--- a/templates/app/client/app/main/main.routes.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict';
-
-<%_ if(filters.ngroute) { _%>
-export default function routes($routeProvider) {
- 'ngInject';
- $routeProvider
- .when('/', {
- template: '
'
- });
-};<% } %>
-<%_ if(filters.uirouter) { _%>
-export default function routes($stateProvider) {
- 'ngInject';
- $stateProvider
- .state('main', {
- url: '/',
- template: '
'
- });
-};<% } %>
diff --git a/templates/app/client/app/polyfills.js b/templates/app/client/app/polyfills.js
new file mode 100644
index 000000000..be86870ec
--- /dev/null
+++ b/templates/app/client/app/polyfills.js
@@ -0,0 +1,17 @@
+import 'core-js/es6';
+import 'core-js/es7/reflect';
+import 'zone.js/dist/zone';
+
+if(!ENV) {
+ var ENV = 'development';
+}
+
+if(ENV === 'production') {
+ // Production
+} else {
+ // Development
+
+ Error.stackTraceLimit = Infinity;
+
+ require('zone.js/dist/long-stack-trace-zone');
+}
diff --git a/templates/app/client/components/auth(auth)/auth.module.js b/templates/app/client/components/auth(auth)/auth.module.js
index 1859bcd00..82f92e6df 100644
--- a/templates/app/client/components/auth(auth)/auth.module.js
+++ b/templates/app/client/components/auth(auth)/auth.module.js
@@ -1,32 +1,19 @@
'use strict';
-import angular from 'angular';
-import constants from '../../app/app.constants';
-import util from '../util/util.module';
-import ngCookies from 'angular-cookies';
-import {authInterceptor} from './interceptor.service';
-import {routerDecorator} from './router.decorator';
-import {AuthService} from './auth.service';
-import {UserResource} from './user.service';
-<%_ if (filters.ngroute) { _%>
-const ngRoute = require('angular-route');<% } %>
-<%_ if (filters.uirouter) { _%>
-import uiRouter from 'angular-ui-router';<% } %>
+// import {authInterceptor} from './interceptor.service';
+// import {routerDecorator} from './router.decorator';
-function addInterceptor($httpProvider) {
- 'ngInject';
- $httpProvider.interceptors.push('authInterceptor');
-}
+// function addInterceptor($httpProvider) {
+// $httpProvider.interceptors.push('authInterceptor');
+// }
-export default angular.module('<%= scriptAppName %>.auth', [
- constants,
- util,
- ngCookies<% if(filters.ngroute) { %>,
- ngRoute<% } if(filters.uirouter) { %>,
- uiRouter<% } %>
-])
- .factory('authInterceptor', authInterceptor)
- .run(routerDecorator)
- .factory('Auth', AuthService)
- .factory('User', UserResource)
- .config(['$httpProvider', addInterceptor])
- .name;
+import { NgModule } from '@angular/core';
+import { AuthService } from './auth.service';
+import { UserService } from './user.service';
+
+export let AuthModule = @NgModule({
+ providers: [
+ AuthService,
+ UserService
+ ]
+})
+class AuthModule {}
diff --git a/templates/app/client/components/auth(auth)/auth.service.js b/templates/app/client/components/auth(auth)/auth.service.js
index e38a12632..d2753b0de 100644
--- a/templates/app/client/components/auth(auth)/auth.service.js
+++ b/templates/app/client/components/auth(auth)/auth.service.js
@@ -1,6 +1,15 @@
-'use strict';
+import { Injectable, EventEmitter, Output } from '@angular/core';
+import { Response } from '@angular/http';
+import { AuthHttp } from 'angular2-jwt';
+import { UserService } from './user.service';
+import { Http } from '@angular/http';
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/toPromise';
+import { safeCb, extractData } from '../util';
+import { userRoles } from '../../app/app.constants';
+
// @flow
-class _User {
+class User {
_id: string = '';
name: string = '';
email: string = '';
@@ -8,206 +17,190 @@ class _User {
$promise = undefined;
}
-export function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
- 'ngInject';
- var safeCb = Util.safeCb;
- var currentUser: _User = new _User();
- var userRoles = appConfig.userRoles || [];
+export let AuthService = @Injectable()
+class AuthService {
+ _currentUser: User = {};
+ @Output() currentUserChanged = new EventEmitter(true);
+ userRoles = userRoles || [];
+
+ static parameters = [Http, AuthHttp, UserService];
+ constructor(_Http_: Http, _AuthHttp_: AuthHttp, _UserService_: UserService) {
+ this.Http = _Http_;
+ this.AuthHttp = _AuthHttp_;
+ this.UserService = _UserService_;
+
+ if(localStorage.getItem('id_token')) {
+ this.UserService.get().toPromise()
+ .then(user => {
+ this.currentUser = user;
+ })
+ .catch(err => {
+ console.log(err);
+
+ localStorage.removeItem('id_token');
+ });
+ }
+ }
+
/**
* Check if userRole is >= role
* @param {String} userRole - role of current user
* @param {String} role - role to check against
*/
- var hasRole = function(userRole, role) {
+ static hasRole(userRole, role) {
return userRoles.indexOf(userRole) >= userRoles.indexOf(role);
- };
+ }
- if($cookies.get('token') && $location.path() !== '/logout') {
- currentUser = User.get();
+ get currentUser() {
+ return this._currentUser;
}
- var Auth = {
- /**
- * Authenticate user and save token
- *
- * @param {Object} user - login info
- * @param {Function} callback - function(error, user)
- * @return {Promise}
- */
- login({email, password}, callback?: Function) {
- return $http.post('/auth/local', { email, password })
- .then(res => {
- $cookies.put('token', res.data.token);
- currentUser = User.get();
- return currentUser.$promise;
- })
- .then(user => {
- safeCb(callback)(null, user);
- return user;
- })
- .catch(err => {
- Auth.logout();
- safeCb(callback)(err.data);
- return $q.reject(err.data);
- });
- },
-
- /**
- * Delete access token and user info
- */
- logout() {
- $cookies.remove('token');
- currentUser = new _User();
- },
-
- /**
- * Create a new user
- *
- * @param {Object} user - user info
- * @param {Function} callback - function(error, user)
- * @return {Promise}
- */
- createUser(user, callback?: Function) {
- return User.save(user,
- function(data) {
- $cookies.put('token', data.token);
- currentUser = User.get();
- return safeCb(callback)(null, user);
- },
- function(err) {
- Auth.logout();
- return safeCb(callback)(err);
- }).$promise;
- },
-
- /**
- * Change password
- *
- * @param {String} oldPassword
- * @param {String} newPassword
- * @param {Function} callback - function(error, user)
- * @return {Promise}
- */
- changePassword(oldPassword, newPassword, callback?: Function) {
- return User.changePassword({ id: currentUser._id }, { oldPassword, newPassword }, function() {
- return safeCb(callback)(null);
- }, function(err) {
+ set currentUser(user) {
+ this._currentUser = user;
+ this.currentUserChanged.emit(user);
+ }
+
+ /**
+ * Authenticate user and save token
+ *
+ * @param {Object} user - login info
+ * @param {Function} [callback] - function(error, user)
+ * @return {Promise}
+ */
+ login({email, password}, callback) {
+ return this.Http.post('/auth/local', {
+ email,
+ password
+ })
+ .toPromise()
+ .then(extractData)
+ .then(res => {
+ localStorage.setItem('id_token', res.token);
+ return this.UserService.get().toPromise();
+ })
+ .then(user => {
+ this.currentUser = user;
+ localStorage.setItem('user', JSON.stringify(user));
+ safeCb(callback)(null, user);
+ return user;
+ })
+ .catch(err => {
+ this.logout();
+ safeCb(callback)(err);
+ return Promise.reject(err);
+ });
+ }
+
+ /**
+ * Delete access token and user info
+ * @return {Promise}
+ */
+ logout() {
+ localStorage.removeItem('user');
+ localStorage.removeItem('id_token');
+ this.currentUser = {};
+ return Promise.resolve();
+ }
+
+ /**
+ * Create a new user
+ *
+ * @param {Object} user - user info
+ * @param {Function} callback - optional, function(error, user)
+ * @return {Promise}
+ */
+ createUser(user, callback) {
+ return this.UserService.create(user).toPromise()
+ .then(data => {
+ localStorage.setItem('id_token', data.token);
+ return this.UserService.get().toPromise();
+ })
+ .then(_user => {
+ this.currentUser = _user;
+ return safeCb(callback)(null, _user);
+ })
+ .catch(err => {
+ this.logout();
return safeCb(callback)(err);
- }).$promise;
- },
-
- /**
- * Gets all available info on a user
- *
- * @param {Function} [callback] - function(user)
- * @return {Promise}
- */
- getCurrentUser(callback?: Function) {
- var value = _.get(currentUser, '$promise')
- ? currentUser.$promise
- : currentUser;
-
- return $q.when(value)
- .then(user => {
- safeCb(callback)(user);
- return user;
- }, () => {
- safeCb(callback)({});
- return {};
- });
- },
-
- /**
- * Gets all available info on a user
- *
- * @return {Object}
- */
- getCurrentUserSync() {
- return currentUser;
- },
-
- /**
- * Check if a user is logged in
- *
- * @param {Function} [callback] - function(is)
- * @return {Promise}
- */
- isLoggedIn(callback?: Function) {
- return Auth.getCurrentUser(undefined)
- .then(user => {
- let is = _.get(user, 'role');
+ });
+ }
- safeCb(callback)(is);
- return is;
- });
- },
-
- /**
- * Check if a user is logged in
- *
- * @return {Bool}
- */
- isLoggedInSync() {
- return !!_.get(currentUser, 'role');
- },
-
- /**
- * Check if a user has a specified role or higher
- *
- * @param {String} role - the role to check against
- * @param {Function} [callback] - function(has)
- * @return {Promise}
- */
- hasRole(role, callback?: Function) {
- return Auth.getCurrentUser(undefined)
- .then(user => {
- let has = hasRole(_.get(user, 'role'), role);
+ /**
+ * Change password
+ *
+ * @param {String} oldPassword
+ * @param {String} newPassword
+ * @param {Function} [callback] - function(error, user)
+ * @return {Promise}
+ */
+ changePassword(oldPassword, newPassword, callback) {
+ return this.UserService.changePassword({id: this.currentUser._id}, oldPassword, newPassword)
+ .then(() => safeCb(callback)(null))
+ .catch(err => safeCb(callback)(err));
+ }
- safeCb(callback)(has);
- return has;
- });
- },
-
- /**
- * Check if a user has a specified role or higher
- *
- * @param {String} role - the role to check against
- * @return {Bool}
- */
- hasRoleSync(role) {
- return hasRole(_.get(currentUser, 'role'), role);
- },
-
- /**
- * Check if a user is an admin
- * (synchronous|asynchronous)
- *
- * @param {Function|*} callback - optional, function(is)
- * @return {Bool|Promise}
- */
- isAdmin() {
- return Auth.hasRole
- .apply(Auth, [].concat.apply(['admin'], arguments));
- },
-
- /**
- * Check if a user is an admin
- *
- * @return {Bool}
- */
- isAdminSync() {
- return Auth.hasRoleSync('admin');
- },
-
- /**
- * Get auth token
- *
- * @return {String} - a token string used for authenticating
- */
- getToken() {
- return $cookies.get('token');
- }
- };
+ /**
+ * Gets all available info on a user
+ *
+ * @param {Function} [callback] - function(user)
+ * @return {Promise}
+ */
+ getCurrentUser(callback) {
+ safeCb(callback)(this.currentUser);
+ return Promise.resolve(this.currentUser);
+ }
+
+ /**
+ * Gets all available info on a user
+ *
+ * @return {Object}
+ */
+ getCurrentUserSync() {
+ return this.currentUser;
+ }
+
+ /**
+ * Checks if user is logged in
+ * @returns {Promise}
+ */
+ isLoggedIn(callback) {
+ let is = this.currentUser.hasOwnProperty('role');
+ safeCb(callback)(is);
+ return Promise.resolve(is);
+ }
+
+ /**
+ * Checks if user is logged in
+ * @returns {Boolean}
+ */
+ isLoggedInSync() {
+ return this.currentUser.hasOwnProperty('role');
+ }
+
+ /**
+ * Check if a user is an admin
+ *
+ * @param {Function|*} callback - optional, function(is)
+ * @return {Promise}
+ */
+ isAdmin(callback) {
+ return this.getCurrentUser().then(user => {
+ var is = user.role === 'admin';
+ safeCb(callback)(is);
+ return is;
+ });
+ }
+
+ isAdminSync() {
+ return this.currentUser.role === 'admin';
+ }
- return Auth;
+ /**
+ * Get auth token
+ *
+ * @return {String} - a token string used for authenticating
+ */
+ getToken() {
+ return localStorage.getItem('id_token');
+ }
}
diff --git a/templates/app/client/components/auth(auth)/user.service.js b/templates/app/client/components/auth(auth)/user.service.js
index a458a89a2..561284b53 100644
--- a/templates/app/client/components/auth(auth)/user.service.js
+++ b/templates/app/client/components/auth(auth)/user.service.js
@@ -1,21 +1,53 @@
'use strict';
+import { Injectable } from '@angular/core';
+import { Response } from '@angular/http';
+import { AuthHttp } from 'angular2-jwt';
+import { Observable } from 'rxjs/Rx';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/toPromise';
-export function UserResource($resource) {
- 'ngInject';
- return $resource('/api/users/:id/:controller', {
- id: '@_id'
- }, {
- changePassword: {
- method: 'PUT',
- params: {
- controller: 'password'
- }
- },
- get: {
- method: 'GET',
- params: {
- id: 'me'
- }
- }
- });
+// @flow
+type UserType = {
+ // TODO: use Mongoose model
+ name: string;
+ email: string;
+}
+
+export let UserService = @Injectable()
+class UserService {
+ static parameters = [AuthHttp];
+ constructor(authHttp: AuthHttp) {
+ this.AuthHttp = authHttp;
+ }
+
+ handleError(err) {
+ Observable.throw(err.json().error || 'Server error');
+ }
+
+ query(): Observable
{
+ return this.AuthHttp.get('/api/users/')
+ .map((res:Response) => res.json())
+ .catch(this.handleError);
+ }
+ get(user = {id: 'me'}): Observable {
+ return this.AuthHttp.get(`/api/users/${user.id}`)
+ .map((res:Response) => res.json())
+ .catch(this.handleError);
+ }
+ create(user: UserType) {
+ return this.AuthHttp.post('/api/users/', user)
+ .map((res:Response) => res.json())
+ .catch(this.handleError);
+ }
+ changePassword(user, oldPassword, newPassword) {
+ return this.AuthHttp.put(`/api/users/${user.id}/password`, {oldPassword, newPassword})
+ .map((res:Response) => res.json())
+ .catch(this.handleError);
+ }
+ remove(user) {
+ return this.AuthHttp.delete(`/api/users/${user.id}`)
+ .map((res:Response) => res.json())
+ .catch(this.handleError);
+ }
}
diff --git a/templates/app/client/components/directives.module.js b/templates/app/client/components/directives.module.js
new file mode 100644
index 000000000..def4d52bc
--- /dev/null
+++ b/templates/app/client/components/directives.module.js
@@ -0,0 +1,33 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { UIRouterModule } from 'ui-router-ng2';
+import { CollapseModule } from 'ng2-bootstrap/ng2-bootstrap';
+
+import { AuthModule } from './auth/auth.module';
+
+import { NavbarComponent } from './navbar/navbar.component';
+import { FooterComponent } from './footer/footer.component';
+<%_ if(filters.oauth) { -%>
+import { OauthButtonsComponent } from './oauth-buttons/oauth-buttons.component';<% } %>
+
+export let DirectivesModule = @NgModule({
+ imports: [
+ CommonModule,
+ UIRouterModule.forChild(),
+ CollapseModule,
+ AuthModule,
+ ],
+ declarations: [
+ NavbarComponent,
+ FooterComponent,
+ <%_ if(filters.oauth) { -%>
+ OauthButtonsComponent,<% } %>
+ ],
+ exports: [
+ NavbarComponent,
+ FooterComponent,
+ <%_ if(filters.oauth) { -%>
+ OauthButtonsComponent,<% } %>
+ ]
+})
+class DirectivesModule {}
diff --git a/templates/app/client/components/footer/footer.component.js b/templates/app/client/components/footer/footer.component.js
index 88b9312a7..87914ad78 100644
--- a/templates/app/client/components/footer/footer.component.js
+++ b/templates/app/client/components/footer/footer.component.js
@@ -1,10 +1,8 @@
-import angular from 'angular';
+import { Component } from '@angular/core';
-export class FooterComponent {}
-
-export default angular.module('directives.footer', [])
- .component('footer', {
- template: require('./footer.<%= templateExt %>'),
- controller: FooterComponent
- })
- .name;
+export let FooterComponent = @Component({
+ selector: 'footer',
+ template: require('./footer.html'),
+ styles: [require('./footer.scss')]
+})
+class FooterComponent {}
diff --git a/templates/app/client/components/modal(uibootstrap)/modal(css).css b/templates/app/client/components/modal(uibootstrap)/modal(css).css
deleted file mode 100644
index ae0406856..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal(css).css
+++ /dev/null
@@ -1,23 +0,0 @@
-.modal-primary .modal-header,
-.modal-info .modal-header,
-.modal-success .modal-header,
-.modal-warning .modal-header,
-.modal-danger .modal-header {
- color: #fff;
- border-radius: 5px 5px 0 0;
-}
-.modal-primary .modal-header {
- background: #428bca;
-}
-.modal-info .modal-header {
- background: #5bc0de;
-}
-.modal-success .modal-header {
- background: #5cb85c;
-}
-.modal-warning .modal-header {
- background: #f0ad4e;
-}
-.modal-danger .modal-header {
- background: #d9534f;
-}
diff --git a/templates/app/client/components/modal(uibootstrap)/modal(html).html b/templates/app/client/components/modal(uibootstrap)/modal(html).html
deleted file mode 100644
index f04d0db03..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal(html).html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
diff --git a/templates/app/client/components/modal(uibootstrap)/modal(less).less b/templates/app/client/components/modal(uibootstrap)/modal(less).less
deleted file mode 100644
index dd1357d2c..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal(less).less
+++ /dev/null
@@ -1,25 +0,0 @@
-.modal-primary,
-.modal-info,
-.modal-success,
-.modal-warning,
-.modal-danger {
- .modal-header {
- color: #fff;
- border-radius: 5px 5px 0 0;
- }
-}
-.modal-primary .modal-header {
- background: @brand-primary;
-}
-.modal-info .modal-header {
- background: @brand-info;
-}
-.modal-success .modal-header {
- background: @brand-success;
-}
-.modal-warning .modal-header {
- background: @brand-warning;
-}
-.modal-danger .modal-header {
- background: @brand-danger;
-}
diff --git a/templates/app/client/components/modal(uibootstrap)/modal(pug).pug b/templates/app/client/components/modal(uibootstrap)/modal(pug).pug
deleted file mode 100644
index 71b4321b3..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal(pug).pug
+++ /dev/null
@@ -1,8 +0,0 @@
-.modal-header
- button.close(ng-if='modal.dismissable', type='button', ng-click='$dismiss()') ×
- h4.modal-title(ng-if='modal.title', ng-bind='modal.title')
-.modal-body
- p(ng-if='modal.text', ng-bind='modal.text')
- div(ng-if='modal.html', ng-bind-html='modal.html')
-.modal-footer
- button.btn(ng-repeat='button in modal.buttons', ng-class='button.classes', ng-click='button.click($event)', ng-bind='button.text')
diff --git a/templates/app/client/components/modal(uibootstrap)/modal(sass).scss b/templates/app/client/components/modal(uibootstrap)/modal(sass).scss
deleted file mode 100644
index 3b0b9d96a..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal(sass).scss
+++ /dev/null
@@ -1,25 +0,0 @@
-.modal-primary,
-.modal-info,
-.modal-success,
-.modal-warning,
-.modal-danger {
- .modal-header {
- color: #fff;
- border-radius: 5px 5px 0 0;
- }
-}
-.modal-primary .modal-header {
- background: $brand-primary;
-}
-.modal-info .modal-header {
- background: $brand-info;
-}
-.modal-success .modal-header {
- background: $brand-success;
-}
-.modal-warning .modal-header {
- background: $brand-warning;
-}
-.modal-danger .modal-header {
- background: $brand-danger;
-}
diff --git a/templates/app/client/components/modal(uibootstrap)/modal(stylus).styl b/templates/app/client/components/modal(uibootstrap)/modal(stylus).styl
deleted file mode 100644
index d394ee047..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal(stylus).styl
+++ /dev/null
@@ -1,23 +0,0 @@
-.modal-primary
-.modal-info
-.modal-success
-.modal-warning
-.modal-danger
- .modal-header
- color #fff
- border-radius 5px 5px 0 0
-
-.modal-primary .modal-header
- background #428bca
-
-.modal-info .modal-header
- background #5bc0de
-
-.modal-success .modal-header
- background #5cb85c
-
-.modal-warning .modal-header
- background #f0ad4e
-
-.modal-danger .modal-header
- background #d9534f
diff --git a/templates/app/client/components/modal(uibootstrap)/modal.service.js b/templates/app/client/components/modal(uibootstrap)/modal.service.js
deleted file mode 100644
index 31de851db..000000000
--- a/templates/app/client/components/modal(uibootstrap)/modal.service.js
+++ /dev/null
@@ -1,77 +0,0 @@
-'use strict';
-import angular from 'angular';
-
-export function Modal($rootScope, $uibModal) {
- /**
- * Opens a modal
- * @param {Object} scope - an object to be merged with modal's scope
- * @param {String} modalClass - (optional) class(es) to be applied to the modal
- * @return {Object} - the instance $uibModal.open() returns
- */
- function openModal(scope = {}, modalClass = 'modal-default') {
- var modalScope = $rootScope.$new();
-
- angular.extend(modalScope, scope);
-
- return $uibModal.open({
- template: require('./modal.<%= templateExt %>'),
- windowClass: modalClass,
- scope: modalScope
- });
- }
-
- // Public API here
- return {
-
- /* Confirmation modals */
- confirm: {
-
- /**
- * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
- * @param {Function} del - callback, ran when delete is confirmed
- * @return {Function} - the function to open the modal (ex. myModalFn)
- */
- delete(del = angular.noop) {
- /**
- * Open a delete confirmation modal
- * @param {String} name - name or info to show on modal
- * @param {All} - any additional args are passed straight to del callback
- */
- return function() {
- var args = Array.prototype.slice.call(arguments);
- var name = args.shift();
- var deleteModal;
-
- deleteModal = openModal({
- modal: {
- dismissable: true,
- title: 'Confirm Delete',
- html: 'Are you sure you want to delete ' + name + ' ?
',
- buttons: [{
- classes: 'btn-danger',
- text: 'Delete',
- click: function(e) {
- deleteModal.close(e);
- }
- }, {
- classes: 'btn-default',
- text: 'Cancel',
- click: function(e) {
- deleteModal.dismiss(e);
- }
- }]
- }
- }, 'modal-danger');
-
- deleteModal.result.then(function(event) {
- del.apply(event, args);
- });
- };
- }
- }
- };
-}
-
-export default angular.module('<%= scriptAppName %>.Modal', [])
- .factory('Modal', Modal)
- .name;
diff --git a/templates/app/client/components/navbar/navbar(html).html b/templates/app/client/components/navbar/navbar(html).html
index 79d7b5a78..2b4284e55 100644
--- a/templates/app/client/components/navbar/navbar(html).html
+++ b/templates/app/client/components/navbar/navbar(html).html
@@ -1,7 +1,7 @@
-
+
<% if (filters.auth) { %>
+
uiSrefActive="active"<% } else { %>ng-class="{active: isActive(item.link)}"<% } %>>
+ uiSref="{{item.state}}"<% } else { %>ng-href="{{item.link}}"<% } %>>{{item.title}}
+
+ <%_ if(filters.auth) { -%>
+
uiSrefActive="active"<% } else { %>ng-class="{active: isActive('/admin')}"<% } %>>
+ uiSref="admin"<% } else { %>href="/admin"<% } %>>Admin
+ <% } %>
+
+ <%_ if(filters.auth) { -%>
<% } %>
diff --git a/templates/app/client/components/navbar/navbar.component.js b/templates/app/client/components/navbar/navbar.component.js
index 8cf2ab0b1..129f1cb72 100644
--- a/templates/app/client/components/navbar/navbar.component.js
+++ b/templates/app/client/components/navbar/navbar.component.js
@@ -1,44 +1,54 @@
-'use strict';
-/* eslint no-sync: 0 */
-import angular from 'angular';
+import { Component } from '@angular/core';
+<%_ if (filters.auth) { -%>
+ <%_ if (filters.uirouter) { -%>
+import { StateService } from 'ui-router-ng2';<% } %>
+import { AuthService } from '../auth/auth.service';<% } %>
-export class NavbarComponent {
+export let NavbarComponent = @Component({
+ selector: 'navbar',
+ template: require('./navbar.html')
+})
+class NavbarComponent {
+ isCollapsed = true;
+ isLoggedIn;
+ isAdmin;
+ currentUser = {};
menu = [{
- 'title': 'Home',
- <% if (filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %>
+ title: 'Home',
+ <% if(filters.uirouter) { %>'state': 'main'<% } else { %>'link': '/'<% } %>,
}];
- <%_ if(!filters.uirouter) { -%>
- $location;
- <%_ } -%>
- <%_ if (filters.auth) { -%>
- isLoggedIn: Function;
- isAdmin: Function;
- getCurrentUser: Function;
- <%_ } -%>
- isCollapsed = true;
- <%_ if(filters.ngroute || filters.auth) { _%>
+ <%_ if(filters.auth) { -%>
- constructor(<% if(!filters.uirouter) { %>$location<% } if(!filters.uirouter && filters.auth) { %>, <% } if (filters.auth) { %>Auth<% } %>) {
- 'ngInject';
- <%_ if(!filters.uirouter) { _%>
- this.$location = $location;
- <%_ } _%>
- <%_ if (filters.auth) { _%>
- this.isLoggedIn = Auth.isLoggedInSync;
- this.isAdmin = Auth.isAdminSync;
- this.getCurrentUser = Auth.getCurrentUserSync;
- <%_ } _%>
- }<% } %>
- <%_ if(!filters.uirouter) { _%>
+ static parameters = [AuthService<% if(filters.uirouter) { %>, StateService<% } %>];
+ constructor(authService: AuthService<% if(filters.uirouter) { %>, stateService: StateService<% } %>) {
+ this.AuthService = authService;
+ this.StateService = stateService;
- isActive(route) {
- return route === this.$location.path();
- }<% } %>
-}
+ this.reset();
-export default angular.module('directives.navbar', [])
- .component('navbar', {
- template: require('./navbar.<%= templateExt %>'),
- controller: NavbarComponent
- })
- .name;
+ this.AuthService.currentUserChanged.subscribe(user => {
+ this.currentuser = user;
+ this.reset();
+ })
+ }
+
+ reset() {
+ this.AuthService.isLoggedIn().then(is => {
+ this.isLoggedIn = is;
+ });
+ this.AuthService.isAdmin().then(is => {
+ this.isAdmin = is;
+ });
+ this.AuthService.getCurrentUser().then(user => {
+ this.currentUser = user;
+ });
+ }
+
+ logout() {
+ let promise = this.AuthService.logout();
+ <%_ if (filters.uirouter) { -%>
+ this.StateService.go('login');<% } -%>
+ <%_ if (filters.ngroute) { -%><% } -%>
+ return promise;
+ }<% } -%>
+}
diff --git a/templates/app/client/components/oauth-buttons(oauth)/index.js b/templates/app/client/components/oauth-buttons(oauth)/index.js
deleted file mode 100644
index 5b328d684..000000000
--- a/templates/app/client/components/oauth-buttons(oauth)/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-'use strict';
-import angular from 'angular';
-
-export function OauthButtonsController($window) {
- 'ngInject';
- this.loginOauth = function(provider) {
- $window.location.href = '/auth/' + provider;
- };
-}
-
-export default angular.module('<%= scriptAppName %>.oauthButtons', [])
- .directive('oauthButtons', function() {
- return {
- template: require('./oauth-buttons.<%= templateExt %>'),
- restrict: 'EA',
- controller: OauthButtonsController,
- controllerAs: 'OauthButtons',
- scope: {
- classes: '@'
- }
- };
- })
- .name;
diff --git a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons(html).html b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons(html).html
index 5ea19425f..7b9b0623a 100644
--- a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons(html).html
+++ b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons(html).html
@@ -1,12 +1,12 @@
-<% if(filters.facebookAuth) { %>
+<% if(filters.facebookAuth) { %>
Connect with Facebook
-<% } if (filters.googleAuth) { %>
+<% } if (filters.googleAuth) { %>
Connect with Google+
-<% } if (filters.twitterAuth) { %>
Connect with Twitter
<% } %>
diff --git a/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.component.js b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.component.js
new file mode 100644
index 000000000..c29bbff2c
--- /dev/null
+++ b/templates/app/client/components/oauth-buttons(oauth)/oauth-buttons.component.js
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+export let OauthButtonsComponent = @Component({
+ selector: 'oauth-buttons',
+ template: require('./oauth-buttons.<%=templateExt%>'),
+ styles: [require('./oauth-buttons.<%=styleExt%>')],
+})
+class OauthButtonsComponent {
+ loginOauth(provider) {
+ window.location.href = `/auth/${provider}`;
+ };
+}
diff --git a/templates/app/client/components/socket(socketio)/socket.mock.js b/templates/app/client/components/socket(socketio)/socket.mock.js
index fdf9e5a57..eb7ff0125 100644
--- a/templates/app/client/components/socket(socketio)/socket.mock.js
+++ b/templates/app/client/components/socket(socketio)/socket.mock.js
@@ -1,17 +1,13 @@
'use strict';
-const angular = require('angular');
+import { noop } from 'lodash-es';
-angular.module('socketMock', [])
- .factory('socket', function() {
- return {
- socket: {
- connect: function() {},
- on: function() {},
- emit: function() {},
- receive: function() {}
- },
-
- syncUpdates: function() {},
- unsyncUpdates: function() {}
+export class SocketServiceMock {
+ socket = {
+ connect: noop,
+ on: noop,
+ emit: noop,
+ receive: noop
};
- });
+ syncUpdates = noop;
+ unsyncUpdates = noop;
+}
diff --git a/templates/app/client/components/socket(socketio)/socket.service.js b/templates/app/client/components/socket(socketio)/socket.service.js
index a41af682a..efc3473de 100644
--- a/templates/app/client/components/socket(socketio)/socket.service.js
+++ b/templates/app/client/components/socket(socketio)/socket.service.js
@@ -1,77 +1,68 @@
'use strict';
-import * as _ from 'lodash';
-import angular from 'angular';
+import { Injectable } from '@angular/core';
+import { noop, find, remove } from 'lodash';
import io from 'socket.io-client';
+import constants from '../../app/app.constants';
-function Socket(socketFactory) {
- 'ngInject';
- // socket.io now auto-configures its connection when we ommit a connection url
- var ioSocket = io('', {
+export let SocketService = @Injectable()
+class SocketService {
+ socket;
+
+ constructor() {
+ this.socket = io(constants.env === 'development' ? `localhost:${constants.port}` : '', {
// Send auth token on connection, you will need to DI the Auth service above
// 'query': 'token=' + Auth.getToken()
- path: '/socket.io-client'
});
+ }
- var socket = socketFactory({ ioSocket });
-
- return {
- socket,
-
- /**
- * Register listeners to sync an array with updates on a model
- *
- * Takes the array we want to sync, the model name that socket updates are sent from,
- * and an optional callback function after new items are updated.
- *
- * @param {String} modelName
- * @param {Array} array
- * @param {Function} cb
- */
- syncUpdates(modelName, array, cb) {
- cb = cb || angular.noop;
-
- /**
- * Syncs item creation/updates on 'model:save'
- */
- socket.on(modelName + ':save', function (item) {
- var oldItem = _.find(array, {_id: item._id});
- var index = array.indexOf(oldItem);
- var event = 'created';
-
- // replace oldItem if it exists
- // otherwise just add item to the collection
- if (oldItem) {
- array.splice(index, 1, item);
- event = 'updated';
- } else {
- array.push(item);
- }
+ /**
+ * Register listeners to sync an array with updates on a model
+ *
+ * Takes the array we want to sync, the model name that socket updates are sent from,
+ * and an optional callback function after new items are updated.
+ *
+ * @param {String} modelName
+ * @param {Array} array
+ * @param {Function} cb
+ */
+ syncUpdates(modelName, array, cb = noop) {
+ /**
+ * Syncs item creation/updates on 'model:save'
+ */
+ this.socket.on(`${modelName}:save`, function(item) {
+ var oldItem = find(array, {_id: item._id});
+ var index = array.indexOf(oldItem);
+ var event = 'created';
- cb(event, item, array);
- });
+ // replace oldItem if it exists
+ // otherwise just add item to the collection
+ if(oldItem) {
+ array.splice(index, 1, item);
+ event = 'updated';
+ } else {
+ array.push(item);
+ }
- /**
- * Syncs removed items on 'model:remove'
- */
- socket.on(modelName + ':remove', function (item) {
- var event = 'deleted';
- _.remove(array, {_id: item._id});
- cb(event, item, array);
- });
- },
+ cb(event, item, array);
+ });
- /**
- * Removes listeners for a models updates on the socket
- *
- * @param modelName
- */
- unsyncUpdates(modelName) {
- socket.removeAllListeners(modelName + ':save');
- socket.removeAllListeners(modelName + ':remove');
- }
- };
+ /**
+ * Syncs removed items on 'model:remove'
+ */
+ this.socket.on(`${modelName}:remove`, function(item) {
+ var event = 'deleted';
+ remove(array, {_id: item._id});
+ cb(event, item, array);
+ });
}
-export default angular.module('<%= scriptAppName %>.socket', [])
- .factory('socket', Socket)
- .name;
+ /**
+ * Removes listeners for a models updates on the socket
+ *
+ * @param modelName
+ */
+ unsyncUpdates(modelName) {
+ this.socket.removeAllListeners(`${modelName}:save`);
+ this.socket.removeAllListeners(`${modelName}:remove`);
+ }
+}
diff --git a/templates/app/client/components/util.js b/templates/app/client/components/util.js
new file mode 100644
index 000000000..11255b391
--- /dev/null
+++ b/templates/app/client/components/util.js
@@ -0,0 +1,67 @@
+/**
+ * The Util service is for thin, globally reusable, utility functions
+ */
+
+import {
+ isFunction,
+ noop,
+} from 'lodash';
+import { Response } from '@angular/http';
+
+/**
+ * Return a callback or noop function
+ *
+ * @param {Function|*} cb - a 'potential' function
+ * @return {Function}
+ */
+export function safeCb(cb) {
+ return isFunction(cb) ? cb : noop;
+}
+
+/**
+ * Parse a given url with the use of an anchor element
+ *
+ * @param {String} url - the url to parse
+ * @return {Object} - the parsed url, anchor element
+ */
+export function urlParse(url) {
+ var a = document.createElement('a');
+ a.href = url;
+
+ // Special treatment for IE, see http://stackoverflow.com/a/13405933 for details
+ if (a.host === '') {
+ a.href = a.href;
+ }
+
+ return a;
+}
+
+/**
+ * Test whether or not a given url is same origin
+ *
+ * @param {String} url - url to test
+ * @param {String|String[]} [origins] - additional origins to test against
+ * @return {Boolean} - true if url is same origin
+ */
+export function isSameOrigin(url, origins) {
+ url = Util.urlParse(url);
+ origins = (origins && [].concat(origins)) || [];
+ origins = origins.map(Util.urlParse);
+ origins.push(window.location);
+ origins = origins.filter(function(o) {
+ let hostnameCheck = url.hostname === o.hostname;
+ let protocolCheck = url.protocol === o.protocol;
+ // 2nd part of the special treatment for IE fix (see above):
+ // This part is when using well-known ports 80 or 443 with IE,
+ // when window.location.port==='' instead of the real port number.
+ // Probably the same cause as this IE bug: https://goo.gl/J9hRta
+ let portCheck = url.port === o.port || (o.port === '' && (url.port === '80' || url.port === '443'));
+ return hostnameCheck && protocolCheck && portCheck;
+ });
+ return origins.length >= 1;
+}
+
+export function extractData(res: Response) {
+ if(!res.text()) return {};
+ return res.json() || { };
+}
diff --git a/templates/app/client/components/util/util.module.js b/templates/app/client/components/util/util.module.js
deleted file mode 100644
index b627e4486..000000000
--- a/templates/app/client/components/util/util.module.js
+++ /dev/null
@@ -1,7 +0,0 @@
-'use strict';
-import angular from 'angular';
-import {UtilService} from './util.service';
-
-export default angular.module('<%= scriptAppName %>.util', [])
- .factory('Util', UtilService)
- .name;
diff --git a/templates/app/client/components/util/util.service.js b/templates/app/client/components/util/util.service.js
deleted file mode 100644
index 7101c92f8..000000000
--- a/templates/app/client/components/util/util.service.js
+++ /dev/null
@@ -1,65 +0,0 @@
-'use strict';
-import angular from 'angular';
-
-/**
- * The Util service is for thin, globally reusable, utility functions
- */
-export function UtilService($window) {
- 'ngInject';
- var Util = {
- /**
- * Return a callback or noop function
- *
- * @param {Function|*} cb - a 'potential' function
- * @return {Function}
- */
- safeCb(cb) {
- return (angular.isFunction(cb)) ? cb : angular.noop;
- },
-
- /**
- * Parse a given url with the use of an anchor element
- *
- * @param {String} url - the url to parse
- * @return {Object} - the parsed url, anchor element
- */
- urlParse(url) {
- var a = document.createElement('a');
- a.href = url;
-
- // Special treatment for IE, see http://stackoverflow.com/a/13405933 for details
- if (a.host === '') {
- a.href = a.href;
- }
-
- return a;
- },
-
- /**
- * Test whether or not a given url is same origin
- *
- * @param {String} url - url to test
- * @param {String|String[]} [origins] - additional origins to test against
- * @return {Boolean} - true if url is same origin
- */
- isSameOrigin(url, origins) {
- url = Util.urlParse(url);
- origins = (origins && [].concat(origins)) || [];
- origins = origins.map(Util.urlParse);
- origins.push($window.location);
- origins = origins.filter(function(o) {
- let hostnameCheck = url.hostname === o.hostname;
- let protocolCheck = url.protocol === o.protocol;
- // 2nd part of the special treatment for IE fix (see above):
- // This part is when using well-known ports 80 or 443 with IE,
- // when $window.location.port==='' instead of the real port number.
- // Probably the same cause as this IE bug: https://goo.gl/J9hRta
- let portCheck = url.port === o.port || (o.port === '' && (url.port === '80' || url.port === '443'));
- return hostnameCheck && protocolCheck && portCheck;
- });
- return origins.length >= 1;
- }
- };
-
- return Util;
-}
diff --git a/templates/app/client/polyfills.js b/templates/app/client/polyfills.js
index 92650a052..768b85092 100644
--- a/templates/app/client/polyfills.js
+++ b/templates/app/client/polyfills.js
@@ -9,7 +9,7 @@
// Prefer CoreJS over the polyfills above
import 'core-js/es6';
import 'core-js/es7/reflect';
-// require('zone.js/dist/zone');
+import 'zone.js/dist/zone';
<%_ if(filters.ts) { _%>
// Typescript emit helpers polyfill
@@ -33,4 +33,4 @@ if(ENV === 'production') {
Error.stackTraceLimit = Infinity;
<% } %>
// require('zone.js/dist/long-stack-trace-zone');
-}
\ No newline at end of file
+}
diff --git a/templates/app/server/app.js b/templates/app/server/app.js
index 7dbbcba9f..6f3a9dcf6 100644
--- a/templates/app/server/app.js
+++ b/templates/app/server/app.js
@@ -27,8 +27,7 @@ if(config.seedDB) {
var app = express();
var server = http.createServer(app);<% if (filters.socketio) { %>
var socketio = require('socket.io')(server, {
- serveClient: config.env !== 'production',
- path: '/socket.io-client'
+ serveClient: false
});
require('./config/socketio').default(socketio);<% } %>
require('./config/express').default(app);
diff --git a/templates/app/server/config/environment/development.js b/templates/app/server/config/environment/development.js
index fde4920c9..64c7bcd65 100644
--- a/templates/app/server/config/environment/development.js
+++ b/templates/app/server/config/environment/development.js
@@ -4,7 +4,6 @@
// Development specific configuration
// ==================================
module.exports = {<% if (filters.mongoose) { %>
-
// MongoDB connection options
mongo: {
uri: 'mongodb://localhost/<%= lodash.slugify(appname) %>-dev'
@@ -24,5 +23,4 @@ module.exports = {<% if (filters.mongoose) { %>
// Seed database on startup
seedDB: true
-
};
diff --git a/templates/app/server/config/environment/shared.js b/templates/app/server/config/environment/shared.js
index 64a5eab99..c1e3e1032 100644
--- a/templates/app/server/config/environment/shared.js
+++ b/templates/app/server/config/environment/shared.js
@@ -1,6 +1,8 @@
'use strict';
exports = module.exports = {
+ env: process.env.NODE_ENV,
+ port: process.env.PORT || <%= devPort %>,
// List of user roles
userRoles: ['guest', 'user', 'admin']
};
diff --git a/templates/app/server/config/express.js b/templates/app/server/config/express.js
index 172d4f06d..94d1709b1 100644
--- a/templates/app/server/config/express.js
+++ b/templates/app/server/config/express.js
@@ -11,6 +11,7 @@ import shrinkRay from 'shrink-ray';
import bodyParser from 'body-parser';
import methodOverride from 'method-override';
import cookieParser from 'cookie-parser';
+import cors from 'cors';
import errorHandler from 'errorhandler';
import path from 'path';
<%_ if(!filters.noModels) { -%>
@@ -72,11 +73,9 @@ export default function(app) {
* Lusca - express server security
* https://github.com/krakenjs/lusca
*/
- if(env !== 'test' && !process.env.SAUCE_USERNAME) {
+ if(env !== 'test' && env !== 'development' && !process.env.SAUCE_USERNAME) {
app.use(lusca({
- csrf: {
- angular: true
- },
+ csrf: true,
xframe: 'SAMEORIGIN',
hsts: {
maxAge: 31536000, //1 year, in seconds
@@ -94,7 +93,17 @@ export default function(app) {
const makeWebpackConfig = require('../../webpack.make');
const webpackConfig = makeWebpackConfig({ DEV: true });
const compiler = webpack(webpackConfig);
- const browserSync = require('browser-sync').create();
+ const browserSync = require('browser-sync').create();
+
+ app.use(cors({
+ origin: true,
+ credentials: true,
+ }));
+
+ app.use(function(req, res, next) {
+ res.setHeader('Access-Control-Allow-Origin', `http://localhost:${config.browserSyncPort}`);
+ next();
+ });
/**
* Run Browsersync and use middleware for Hot Module Replacement
@@ -122,16 +131,16 @@ export default function(app) {
* Reload all devices when bundle is complete
* or send a fullscreen error message to the browser instead
*/
- compiler.plugin('done', function (stats) {
+ compiler.plugin('done', function(stats) {
console.log('webpack done hook');
- if(stats.hasErrors() || stats.hasWarnings()) {
- return browserSync.sockets.emit('fullscreen:message', {
- title: "Webpack Error:",
- body: stripAnsi(stats.toString()),
- timeout: 100000
- });
- }
- browserSync.reload();
+ if(stats.hasErrors() || stats.hasWarnings()) {
+ return browserSync.sockets.emit('fullscreen:message', {
+ title: "Webpack Error:",
+ body: stripAnsi(stats.toString()),
+ timeout: 100000
+ });
+ }
+ browserSync.reload();
});
}
diff --git a/templates/app/webpack.make.js b/templates/app/webpack.make.js
index 6c04a7449..14ac2caed 100644
--- a/templates/app/webpack.make.js
+++ b/templates/app/webpack.make.js
@@ -41,20 +41,6 @@ module.exports = function makeWebpackConfig(options) {
app: './client/app/app.<%= scriptExt %>',
polyfills: './client/polyfills.<%= scriptExt %>',
vendor: [
- 'angular',
- 'angular-animate',
- 'angular-aria',
- 'angular-cookies',
- 'angular-resource',
- <%_ if(filters.ngroute) { _%>
- 'angular-route',<% } %>
- 'angular-sanitize',
- <%_ if(filters.socketio) { _%>
- 'angular-socket-io',<% } %>
- <%_ if(filters.uibootstrap) { -%>
- 'angular-ui-bootstrap',<% } %>
- <%_ if(filters.uirouter) { _%>
- 'angular-ui-router',<% } %>
'lodash'
]
};
@@ -220,10 +206,10 @@ module.exports = function makeWebpackConfig(options) {
// SASS LOADER
// Reference: https://github.com/jtangelder/sass-loader
test: /\.(scss|sass)$/,
- loaders: ['style', 'css', 'sass'],
+ loaders: ['raw', 'sass'],
include: [
path.resolve(__dirname, 'node_modules/bootstrap-sass/assets/stylesheets/*.scss'),
- path.resolve(__dirname, 'client/app/app.scss')
+ path.resolve(__dirname, 'client')
]<% } %>
<%_ if(filters.less) { _%>
// LESS LOADER
@@ -246,11 +232,6 @@ module.exports = function makeWebpackConfig(options) {
}<% } %>]
};
- config.module.postLoaders = [{
- test: /\.<%= scriptExt %>$/,
- loader: 'ng-annotate?single_quotes'
- }];
-
<%_ if(filters.babel) { _%>
// ISPARTA INSTRUMENTER LOADER
// Reference: https://github.com/ColCh/isparta-instrumenter-loader
@@ -372,7 +353,9 @@ module.exports = function makeWebpackConfig(options) {
'process.env': {
NODE_ENV: '"development"'
}
- })
+ }),
+
+ new webpack.HotModuleReplacementPlugin()
);
}