├── .gitignore
├── 00 Boilerplate
├── README.md
├── package.json
├── src
│ ├── css
│ │ └── site.css
│ ├── index.html
│ └── index.ts
├── tsconfig.json
└── webpack.config.js
├── 01 HelloAngular
├── README.md
├── package.json
├── src
│ ├── components
│ │ └── common
│ │ │ └── header.ts
│ ├── css
│ │ └── site.css
│ ├── index.html
│ └── index.ts
├── tsconfig.json
└── webpack.config.js
├── 02 Navigation
├── README.md
├── package.json
├── src
│ ├── app-routes.ts
│ ├── components
│ │ ├── common
│ │ │ └── header.ts
│ │ ├── login
│ │ │ └── login.ts
│ │ ├── patient
│ │ │ └── patient.ts
│ │ └── patients
│ │ │ └── patients.ts
│ ├── css
│ │ └── site.css
│ ├── index.html
│ └── index.ts
├── tsconfig.json
└── webpack.config.js
├── 03 List Page
├── README.md
├── package.json
├── src
│ ├── api
│ │ └── patientAPI.ts
│ ├── app-routes.ts
│ ├── components
│ │ ├── common
│ │ │ └── header.ts
│ │ ├── login
│ │ │ └── login.ts
│ │ ├── patient
│ │ │ └── patient.ts
│ │ └── patients
│ │ │ ├── patients.ts
│ │ │ ├── patientsList.ts
│ │ │ └── searchPatient.ts
│ ├── css
│ │ └── site.css
│ ├── index.html
│ ├── index.ts
│ ├── mockData
│ │ └── patients.json
│ └── model
│ │ └── patient.ts
├── tsconfig.json
└── webpack.config.js
├── 04 Form Page
├── README.md
├── package.json
├── src
│ ├── api
│ │ └── patientAPI.ts
│ ├── app-routes.ts
│ ├── components
│ │ ├── common
│ │ │ └── header.ts
│ │ ├── login
│ │ │ └── login.ts
│ │ ├── patient
│ │ │ └── patient.ts
│ │ └── patients
│ │ │ ├── patients.ts
│ │ │ ├── patientsList.ts
│ │ │ └── searchPatient.ts
│ ├── css
│ │ └── site.css
│ ├── index.html
│ ├── index.ts
│ ├── mockData
│ │ └── patients.json
│ └── model
│ │ └── patient.ts
├── tsconfig.json
└── webpack.config.js
├── 05 Form Validation
├── README.md
├── package.json
├── src
│ ├── api
│ │ └── patientAPI.ts
│ ├── app-routes.ts
│ ├── components
│ │ ├── common
│ │ │ └── header.ts
│ │ ├── login
│ │ │ └── login.ts
│ │ ├── patient
│ │ │ └── patient.ts
│ │ └── patients
│ │ │ ├── patients.ts
│ │ │ ├── patientsList.ts
│ │ │ └── searchPatient.ts
│ ├── css
│ │ └── site.css
│ ├── index.html
│ ├── index.ts
│ ├── mockData
│ │ └── patients.json
│ ├── model
│ │ └── patient.ts
│ └── validations
│ │ └── validateDni.ts
├── tsconfig.json
└── webpack.config.js
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist/
4 | typings/
5 | *.orig
6 | .idea/
7 | */src/**/*.js
8 | */src/**/*.js.map
--------------------------------------------------------------------------------
/00 Boilerplate/README.md:
--------------------------------------------------------------------------------
1 | # 00 Boilerplate
2 |
3 | In this sample we are going to setup the basic plumbing to "build" our project and launch it in a dev server.
4 |
5 | ## We are going to use:
6 |
7 | - [Webpack](https://webpack.github.io/)
8 | - [Typescript](http://www.typescriptlang.org/)
9 |
10 | ## The most interesting parts worth to take a look
11 |
12 | - package.json: check packages installed.
13 |
14 | - webpack.config.js: check the build process and ts-loader to handle typescript.
15 |
16 | - src: javascript using imports.
17 |
--------------------------------------------------------------------------------
/00 Boilerplate/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular1_5-sample-app",
3 | "version": "1.0.0",
4 | "description": "Angular 1.5 Sample App",
5 | "main": "index.js",
6 | "scripts": {
7 | "postinstall": "typings install",
8 | "start": "webpack-dev-server"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/Lemoncode/angular1_5-sample-app.git"
13 | },
14 | "homepage": "https://github.com/Lemoncode/angular1_5-sample-app",
15 | "keywords": [
16 | "angular",
17 | "sample",
18 | "app"
19 | ],
20 | "author": "Lemoncode",
21 | "license": "MIT",
22 | "devDependencies": {
23 | "css-loader": "^0.25.0",
24 | "extract-text-webpack-plugin": "^1.0.1",
25 | "html-webpack-plugin": "^2.22.0",
26 | "style-loader": "^0.13.1",
27 | "ts-loader": "^0.8.2",
28 | "typescript": "^1.8.10",
29 | "typings": "^1.3.3",
30 | "webpack": "^1.13.2",
31 | "webpack-dev-server": "^1.15.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/00 Boilerplate/src/css/site.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: #ACDF2C
3 | }
4 |
--------------------------------------------------------------------------------
/00 Boilerplate/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 1.5 Sample App
6 |
7 |
8 |
9 |
00 Boilerplate
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/00 Boilerplate/src/index.ts:
--------------------------------------------------------------------------------
1 | var App = console.log('Hello from ts');
2 |
3 | export default App;
4 |
--------------------------------------------------------------------------------
/00 Boilerplate/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "sourceMap": true,
9 | "jsx": "react",
10 | "experimentalDecorators": true,
11 | "emitDecoratorMetadata": true,
12 | "noLib": false,
13 | "preserveConstEnums": true,
14 | "suppressImplicitAnyIndexErrors": true
15 | },
16 | "compileOnSave": false,
17 | "exclude": [
18 | "node_modules"
19 | ],
20 | "atom": {
21 | "rewriteTsconfig": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/00 Boilerplate/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | var basePath = __dirname;
6 |
7 | module.exports = {
8 | context: path.join(basePath, "src"),
9 | resolve: {
10 | extensions: ['', '.js', '.ts']
11 | },
12 |
13 | entry: {
14 | app: './index.ts',
15 | styles: [
16 | './css/site.css'
17 | ],
18 | vendor: [
19 |
20 | ]
21 | },
22 |
23 | output: {
24 | path: path.join(basePath, "dist"),
25 | filename: '[name].js'
26 | },
27 |
28 | devServer: {
29 | contentBase: './dist', //Content base
30 | inline: true, //Enable watch and live reload
31 | host: 'localhost',
32 | port: 8080
33 | },
34 |
35 | devtool: 'source-map',
36 |
37 | module: {
38 | loaders: [
39 | {
40 | test: /\.ts$/,
41 | exclude: /node_modules/,
42 | loader: 'ts'
43 | },
44 | {
45 | test: /\.css$/,
46 | exclude: /node_modules/,
47 | loader: ExtractTextPlugin.extract('style','css')
48 | }
49 | ]
50 | },
51 |
52 | plugins: [
53 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
54 | new ExtractTextPlugin('[name].css'),
55 | new HtmlWebpackPlugin({
56 | filename: 'index.html',
57 | template: 'index.html'
58 | })
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/01 HelloAngular/README.md:
--------------------------------------------------------------------------------
1 | # 01 Hello Angular
2 |
3 | In this sample we are going to create an instantiante a minimum angular 1.5 application.
4 |
5 | We are going to take as startup point _00 Boilerplate_
6 |
7 | # Summary steps:
8 |
9 | - Install Angular libraries.
10 | - Creating the app.
11 | - Instantiating the app from the HTML.
12 | - Creating a component.
13 | - Displaying a component.
14 |
15 | # Steps to build it
16 |
17 | ## Prerequisites
18 |
19 | Prerequisites, you will need to have nodejs installed in your computer. If you want to follow this step guides you will need to take as starting point sample "00 Boilerplate"
20 |
21 | ## Steps
22 |
23 |
24 | Let's start by installing Angular 1.x library
25 |
26 | ```
27 | npm install angular@1.5.8 --save
28 | ```
29 |
30 |
31 | Let's install the angularjs types:
32 |
33 | ```
34 | npm install @Types/angular --save-dev
35 | ```
36 |
37 | We will need to install JQuery types as well
38 |
39 | ```
40 | npm install @Types/jquery --save-dev
41 | ```
42 |
43 | Under _src_ folder let's replace the content of the _index.ts_ file:
44 |
45 | ```javascript
46 | import * as angular from 'angular'
47 |
48 | var app = angular.module('myAppointmentsApp', []);
49 |
50 | // Just to test if the app is instantiated
51 | // check the browser console (developer window)
52 | console.log(app);
53 | ```
54 |
55 | Now if we open the console we can check that the app has been created successfuly
56 | (in the console window we can expand the dumped app object).
57 |
58 | Let's create our first component.
59 |
60 | First we will indicate in the HTML that we are going to use this application (index.html):
61 |
62 | ```javascript
63 |
64 | ```
65 |
66 | Under _src_ let's create the following subfolders _components/common_ and
67 | under that subfolder let's create a file called _header.ts_ this file will
68 | contain a simple "header" component:
69 |
70 | ```javascript
71 | import * as angular from 'angular';
72 |
73 | class HeaderController {
74 | sampleBinding : string;
75 |
76 | constructor() {
77 | this.sampleBinding = "Testing binding header component";
78 | }
79 | }
80 |
81 | export const header = {
82 | template: 'Header testing bindings: {{$ctrl.sampleBinding}}',
83 | controller: HeaderController
84 | }
85 | ```
86 |
87 | Let's register this component in the _index.ts_ file
88 |
89 | ```javascript
90 | var app = angular.module('myAppointmentsApp', []);
91 |
92 | app.component('header', header);
93 | ```
94 |
95 | Let's use this component in our _index.html_ file
96 |
97 | ```html
98 |
99 |
100 |
101 |
102 |
103 | ```
104 |
105 | Now we can run the sample
106 |
107 | ```
108 | npm start
109 | ```
110 |
111 | And we can see how the _header_ component gets instantiated and bindings are
112 | working as expected.
113 |
--------------------------------------------------------------------------------
/01 HelloAngular/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular1_5-sample-app",
3 | "version": "1.0.0",
4 | "description": "Angular 1.5 Sample App",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/Lemoncode/angular1_5-sample-app.git"
12 | },
13 | "homepage": "https://github.com/Lemoncode/angular1_5-sample-app",
14 | "keywords": [
15 | "angular",
16 | "sample",
17 | "app"
18 | ],
19 | "author": "Lemoncode",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "css-loader": "^0.25.0",
23 | "extract-text-webpack-plugin": "^1.0.1",
24 | "html-webpack-plugin": "^2.28.0",
25 | "style-loader": "^0.13.1",
26 | "ts-loader": "^2.0.1",
27 | "typescript": "^2.2.1",
28 | "webpack": "^1.14.0",
29 | "webpack-dev-server": "^1.16.3"
30 | },
31 | "dependencies": {
32 | "angular": "^1.5.8"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/Lemoncode/angular1_5-sample-app/issues"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/01 HelloAngular/src/components/common/header.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class HeaderController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Testing binding header component";
8 | }
9 | }
10 |
11 | export const header = {
12 | template: 'Header testing bindings: {{$ctrl.sampleBinding}}',
13 | controller: HeaderController
14 | }
15 |
--------------------------------------------------------------------------------
/01 HelloAngular/src/css/site.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: #ACDF2C
3 | }
4 |
--------------------------------------------------------------------------------
/01 HelloAngular/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 1.5 Sample App
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/01 HelloAngular/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular'
2 | import {header} from './components/common/header';
3 |
4 | var app = angular.module('myAppointmentsApp', []);
5 |
6 | app.component('header', header);
7 |
--------------------------------------------------------------------------------
/01 HelloAngular/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "sourceMap": true,
9 | "jsx": "react",
10 | "experimentalDecorators": true,
11 | "emitDecoratorMetadata": true,
12 | "noLib": false,
13 | "preserveConstEnums": true,
14 | "suppressImplicitAnyIndexErrors": true
15 | },
16 | "compileOnSave": false,
17 | "exclude": [
18 | "node_modules"
19 | ],
20 | "atom": {
21 | "rewriteTsconfig": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/01 HelloAngular/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | var basePath = __dirname;
6 |
7 | module.exports = {
8 | context: path.join(basePath, "src"),
9 | resolve: {
10 | extensions: ['', '.js', '.ts']
11 | },
12 |
13 | entry: {
14 | app: './index.ts',
15 | styles: [
16 | './css/site.css'
17 | ],
18 | vendor: [
19 |
20 | ]
21 | },
22 |
23 | output: {
24 | path: path.join(basePath, "dist"),
25 | filename: '[name].js'
26 | },
27 |
28 | devServer: {
29 | contentBase: './dist', //Content base
30 | inline: true, //Enable watch and live reload
31 | host: 'localhost',
32 | port: 8080
33 | },
34 |
35 | devtool: 'source-map',
36 |
37 | module: {
38 | loaders: [
39 | {
40 | test: /\.ts$/,
41 | exclude: /node_modules/,
42 | loader: 'ts'
43 | },
44 | {
45 | test: /\.css$/,
46 | exclude: /node_modules/,
47 | loader: ExtractTextPlugin.extract('style','css')
48 | }
49 | ]
50 | },
51 |
52 | plugins: [
53 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
54 | new ExtractTextPlugin('[name].css'),
55 | new HtmlWebpackPlugin({
56 | filename: 'index.html',
57 | template: 'index.html'
58 | })
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/02 Navigation/README.md:
--------------------------------------------------------------------------------
1 | # 02 Navigation
2 |
3 | In this sample we are going to add route navigation support to our sample app.
4 |
5 | We are going to take as startup point _01 HelloAngular_
6 |
7 | # Summary steps:
8 |
9 | - Install routing libraries.
10 | - Define routes.
11 | - Create empty pages components, including a basic navgation.
12 |
13 | # Steps to build it
14 |
15 | ## Prerequisites
16 |
17 | Prerequisites, you will need to have nodejs installed in your computer. If you want to follow this step guides you will need to take as starting point sample "01 HelloAngular"
18 |
19 | ## Steps
20 |
21 |
22 | Let's start by installing angular-ui-router (angular 1.5 component router is an
23 | abandoned project, [more info](http://stackoverflow.com/questions/33652668/angular-1-5-and-new-component-router)).
24 |
25 | ```
26 | npm install angular-ui-router --save
27 | ```
28 |
29 | We need to install types definition for angular-ui-router
30 |
31 | ```
32 | npm install @Types/angular-ui-router --save-dev
33 | ```
34 |
35 | We need to install as well es6-promise types
36 |
37 | ```
38 | npm install @Types/es6-promise --save-dev
39 | ```
40 |
41 | Now we need to indicate in _index.ts_ that we are going to use this module in our
42 | app:
43 |
44 | ```javascript
45 | import 'angular-ui-router';
46 |
47 | // (...)
48 |
49 | var app = angular.module('myAppointmentsApp', ['ui.router']);
50 |
51 | app.component('login', login);
52 | ```
53 |
54 | The next step is to define our routing config, let's create a file called
55 | _app-routes.ts_ there we will setup the ui-router and setup a route to a login pages
56 |
57 | ```javascript
58 | function routing($locationProvider: ng.ILocationProvider,
59 | $stateProvider: angular.ui.IStateProvider,
60 | $urlRouterProvider: angular.ui.IUrlRouterProvider) {
61 |
62 | // html5 removes the need for # in URL
63 | $locationProvider.html5Mode({
64 | enabled: false
65 | });
66 |
67 | $stateProvider.state('home', {
68 | url: '/home',
69 | views: {
70 | 'content@': { template: ' ' }
71 | }
72 | }
73 | );
74 |
75 | $urlRouterProvider.otherwise('/home');
76 | }
77 |
78 | export default routing;
79 | ```
80 |
81 | Now we have to comeback to our index.ts file and register our routing function
82 |
83 | ```javascript
84 | import routing from './app-routes';
85 | // (...)
86 |
87 | var app = angular.module('myAppointmentsApp', ['ui.router'])
88 | .config(routing);;
89 | ```
90 |
91 | Under _components_ subfolder let's create a new subfolder called _login_ and
92 | there we are going to create a dummy login component:
93 |
94 | ```javascript
95 | import * as angular from 'angular';
96 |
97 | class LoginController {
98 | sampleBinding : string;
99 |
100 | constructor() {
101 | this.sampleBinding = "Hello form Login";
102 | }
103 | }
104 |
105 | export const login = {
106 | template: 'bindings test: {{$ctrl.sampleBinding}} ',
107 | controller: LoginController
108 | }
109 | ```
110 |
111 | Now we need to setup the placeholder for the views, we will do that in
112 | _index.html_ file:
113 |
114 |
115 | ```html
116 |
120 | ```
121 |
122 | Let's create a dummy _patients_ component (later on it will display a list of
123 | patients appointments). We will create a subofolder components/patients and under
124 | that subfolder let's create a file called _patients.ts_
125 |
126 | ```javascript
127 | import * as angular from 'angular';
128 |
129 | class PatientsController {
130 | sampleBinding : string;
131 |
132 | constructor() {
133 | this.sampleBinding = "Hello from Patients";
134 | }
135 | }
136 |
137 | export const patients = {
138 | template: 'Bindings test: {{$ctrl.sampleBinding}} ',
139 | controller: PatientsController
140 | }
141 | ```
142 |
143 | We have to register this component in our index.ts file
144 |
145 | ```javascript
146 | import {patients} from './components/patients/patients';
147 | // (...)
148 |
149 | app.component('patients', patients);
150 | ```
151 |
152 | Now we need to register a route to our new patients component:
153 |
154 |
155 | We can run the app and manually navigate to the _patients_ route, but rather
156 | we will just add a link from _login_ route to _patients_ route, let's open
157 | _components/login/login.ts_ and replace the html template by using backticks
158 | and adding a new link:
159 |
160 | ```javascript
161 | export const login = {
162 | template: `
163 | bindings test: {{$ctrl.sampleBinding}}
164 | Navigate to patients
165 | `,
166 | controller: LoginController
167 | }
168 | ```
169 |
170 | Now we can repeat the same steps to create the _patient_ component.
171 |
--------------------------------------------------------------------------------
/02 Navigation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular1_5-sample-app",
3 | "version": "1.0.0",
4 | "description": "Angular 1.5 Sample App",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/Lemoncode/angular1_5-sample-app.git"
12 | },
13 | "homepage": "https://github.com/Lemoncode/angular1_5-sample-app",
14 | "keywords": [
15 | "angular",
16 | "sample",
17 | "app"
18 | ],
19 | "author": "Lemoncode",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "@types/angular": "^1.6.7",
23 | "@types/jquery": "^2.0.40",
24 | "css-loader": "^0.25.0",
25 | "extract-text-webpack-plugin": "^1.0.1",
26 | "html-webpack-plugin": "^2.22.0",
27 | "style-loader": "^0.13.1",
28 | "ts-loader": "^2.0.1",
29 | "typescript": "^2.2.1",
30 | "webpack": "^1.13.2",
31 | "webpack-dev-server": "^1.15.1"
32 | },
33 | "dependencies": {
34 | "angular": "^1.5.8",
35 | "angular-ui-router": "^0.4.2"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/02 Navigation/src/app-routes.ts:
--------------------------------------------------------------------------------
1 | function routing($locationProvider: ng.ILocationProvider,
2 | $stateProvider: angular.ui.IStateProvider,
3 | $urlRouterProvider: angular.ui.IUrlRouterProvider) {
4 |
5 | // html5 removes the need for # in URL
6 | $locationProvider.html5Mode({
7 | enabled: false
8 | });
9 |
10 | $stateProvider.state('home', {
11 | url: '/home',
12 | views: {
13 | 'content@': { template: ' ' }
14 | }
15 | }
16 | );
17 |
18 | $stateProvider.state('patients', {
19 | url: '/patients',
20 | views: {
21 | 'content@': { template: ' ' }
22 | }
23 | }
24 | );
25 |
26 | $stateProvider.state('patient', {
27 | url: '/patient',
28 | views: {
29 | 'content@': { template: ' ' }
30 | }
31 | }
32 | );
33 |
34 | $urlRouterProvider.otherwise('/home');
35 | }
36 |
37 | export default routing;
38 |
--------------------------------------------------------------------------------
/02 Navigation/src/components/common/header.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class HeaderController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Testing binding header component";
8 | }
9 | }
10 |
11 | export const header = {
12 | template: 'Header testing bindings: {{$ctrl.sampleBinding}} ',
13 | controller: HeaderController
14 | }
15 |
--------------------------------------------------------------------------------
/02 Navigation/src/components/login/login.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class LoginController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Hello from Login";
8 | }
9 | }
10 |
11 | export const login = {
12 | template: `
13 | bindings test: {{$ctrl.sampleBinding}}
14 | Navigate to patients
15 | `,
16 | controller: LoginController
17 | }
18 |
--------------------------------------------------------------------------------
/02 Navigation/src/components/patient/patient.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class PatientController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Hello from Patient";
8 | }
9 | }
10 |
11 | export const patient = {
12 | template: `
13 | Bindings test: {{$ctrl.sampleBinding}}
14 | `,
15 | controller: PatientController
16 | }
17 |
--------------------------------------------------------------------------------
/02 Navigation/src/components/patients/patients.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class PatientsController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Hello from Patients";
8 | }
9 | }
10 |
11 | export const patients = {
12 | template: `
13 | Bindings test: {{$ctrl.sampleBinding}}
14 | Navigate to patient
15 | `,
16 | controller: PatientsController
17 | }
18 |
--------------------------------------------------------------------------------
/02 Navigation/src/css/site.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: #ACDF2C
3 | }
4 |
--------------------------------------------------------------------------------
/02 Navigation/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 1.5 Sample App
6 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/02 Navigation/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular'
2 | import 'angular-ui-router';
3 | import routing from './app-routes';
4 | import {header} from './components/common/header';
5 | import {login} from './components/login/login';
6 | import {patients} from './components/patients/patients';
7 | import {patient} from './components/patient/patient';
8 |
9 |
10 | var app = angular.module('myAppointmentsApp', ['ui.router'])
11 | .config(routing);
12 |
13 | app.component('header', header);
14 | app.component('login', login);
15 | app.component('patients', patients);
16 | app.component('patient', patient);
17 |
--------------------------------------------------------------------------------
/02 Navigation/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "sourceMap": true,
9 | "jsx": "react",
10 | "experimentalDecorators": true,
11 | "emitDecoratorMetadata": true,
12 | "noLib": false,
13 | "preserveConstEnums": true,
14 | "suppressImplicitAnyIndexErrors": true
15 | },
16 | "compileOnSave": false,
17 | "exclude": [
18 | "node_modules"
19 | ],
20 | "atom": {
21 | "rewriteTsconfig": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/02 Navigation/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | var basePath = __dirname;
6 |
7 | module.exports = {
8 | context: path.join(basePath, "src"),
9 | resolve: {
10 | extensions: ['', '.js', '.ts']
11 | },
12 |
13 | entry: {
14 | app: './index.ts',
15 | styles: [
16 | './css/site.css'
17 | ],
18 | vendor: [
19 |
20 | ]
21 | },
22 |
23 | output: {
24 | path: path.join(basePath, "dist"),
25 | filename: '[name].js'
26 | },
27 |
28 | devServer: {
29 | contentBase: './dist', //Content base
30 | inline: true, //Enable watch and live reload
31 | host: 'localhost',
32 | port: 8080
33 | },
34 |
35 | devtool: 'source-map',
36 |
37 | module: {
38 | loaders: [
39 | {
40 | test: /\.ts$/,
41 | exclude: /node_modules/,
42 | loader: 'ts'
43 | },
44 | {
45 | test: /\.css$/,
46 | exclude: /node_modules/,
47 | loader: ExtractTextPlugin.extract('style','css')
48 | }
49 | ]
50 | },
51 |
52 | plugins: [
53 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
54 | new ExtractTextPlugin('[name].css'),
55 | new HtmlWebpackPlugin({
56 | filename: 'index.html',
57 | template: 'index.html'
58 | })
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/03 List Page/README.md:
--------------------------------------------------------------------------------
1 | # 03 List Page
2 |
3 | In this sample we are going to build up the appointments page. This will include
4 | creating the entities, creating a fake api (to simulate we are hitting a remote
5 | server), creating the layout, plus the ui interaction.
6 |
7 | We are going to take as startup point _02 Navigation_
8 |
9 | # Summary steps:
10 |
11 | - Import bootstrap libraries.
12 | - Create the searchPatient component (dummy).
13 | - Create the listComponent.
14 |
15 | - Add mock data plus copy to dev.
16 | - Create entities.
17 | - Create api.
18 |
19 |
20 | # Steps to build it
21 |
22 | ## Prerequisites
23 |
24 | Prerequisites, you will need to have nodejs installed in your computer. If you want to follow this step guides you will need to take as starting point sample "02 Navigation"
25 |
26 | ## Steps
27 |
28 | ### Style
29 |
30 | Before getting started building the app, let's install bootstrap and jquery, we will
31 | use bootstrap as a base to generate the layout.
32 |
33 | ```
34 | npm install jquery --save
35 | npm install bootstrap --save
36 | ```
37 | In webpack.config.js:
38 |
39 |
40 |
41 |
42 | We will just use a plugin to expose "$" (jquery) and "JQuery"
43 | as global names.
44 |
45 | ```javascript
46 | plugins: [
47 | // ...
48 | //Expose jquery used by bootstrap
49 | new webpack.ProvidePlugin({
50 | $: "jquery",
51 | jQuery: "jquery"
52 | })
53 | ]
54 | }
55 | ```
56 | And let's add bootstrap.css to the styles array to be processed by webpack (webpack.config.js)
57 |
58 | ```javascript
59 | entry: {
60 | // (...)
61 | styles: [
62 | '../node_modules/bootstrap/dist/css/bootstrap.css',
63 | './css/site.css'
64 | ],
65 | // (...)
66 | },
67 | ```
68 |
69 | Bootstrap will expose glyphicons and other features, let's expose the right loaders
70 | for this.
71 |
72 | First we will install file-loader package
73 |
74 | ```
75 | npm install file-loader --save-dev
76 | npm install url-loader --save-dev
77 | ```
78 | We need to indicate that we will use bootstrap javascript:
79 |
80 | ```javascript
81 | entry: {
82 | // ...
83 | vendor: [
84 | 'bootstrap'
85 | ]
86 | },
87 | ```
88 |
89 | On the CSS loader section, we have to remove the exclude "node_modules" folder
90 |
91 | ```javascript
92 | {
93 | test: /\.css$/,
94 | loader: ExtractTextPlugin.extract('style','css')
95 | },
96 | ```
97 |
98 | Then we will configure the loader for fonts / images.
99 |
100 | ```javacript
101 | loaders: [
102 | // (...)
103 | //Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack
104 | {test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
105 | {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
106 | {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
107 | {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" },
108 | {
109 | test: /\.png$/,
110 | loader: 'file?limit=0&name=[path][name].[hash].',
111 | exclude: /node_modules/
112 | }
113 | ]
114 | },
115 | ```
116 |
117 | ### Layout
118 |
119 | Let's start by creating two separate child components one for
120 | the search panel, and another one for the list panel.
121 |
122 | ```javascript
123 | import * as angular from 'angular';
124 |
125 | class SearchPatientController {
126 | constructor() {
127 | }
128 | }
129 |
130 | export const searchPatient = {
131 | template:
132 | `
133 |
134 |
135 |
136 |
137 |
Buscar paciente
138 |
140 |
141 |
142 |
165 |
166 |
167 | `,
168 | controller: SearchPatientController
169 | }
170 | ```
171 |
172 | We have to register this component at app level _src/index.ts_
173 |
174 | ```javascript
175 | import {searchPatient} from './components/patients/searchPatient';
176 |
177 | // (...)
178 |
179 | app.component('searchPatient', searchPatient);
180 | ```
181 |
182 | Let's use this component in the _patients_ page.
183 |
184 | ```javascript
185 | import * as angular from 'angular';
186 |
187 | class PatientsController {
188 | constructor() {
189 |
190 | }
191 | }
192 |
193 | export const patients = {
194 | template: `
195 |
200 | `,
201 | controller: PatientsController
202 | }
203 | ```
204 |
205 | Let's do a quick test and check that the component is properly displayed:
206 |
207 | ```
208 | npm start
209 | ```
210 |
211 | Great ! we get the panel displayed, let's draw the second component
212 | (list result).
213 |
214 | Let's create a new component called _patientsList_ in the following path
215 | src/components/patients/patientsList.ts
216 |
217 | ```javascript
218 | import * as angular from 'angular';
219 |
220 | class PatientsListController {
221 | constructor() {
222 | }
223 | }
224 |
225 | export const patientsList = {
226 | template:
227 | `
228 |
229 |
236 |
237 |
238 |
239 |
240 | DNI
241 | Paciente
242 | Especialidad
243 | Doctor
244 | Cita
245 | Hora
246 |
247 |
248 |
249 |
250 | {{p.dni}}
251 | Sample Name
252 |
253 | Sample Specialty
254 |
256 |
257 |
258 | {{p.doctor}}
259 | {{p.date}}
260 |
261 | Sample Time
262 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 | `,
272 | controller: PatientsListController
273 | }
274 | ```
275 |
276 | Let's register this component in the index.ts
277 |
278 | ```javascript
279 | import {patientsList} from './components/patients/patientsList';
280 |
281 | app.component('patientsList', patientsList);
282 | ```
283 |
284 |
285 | Let's use it in our patients component:
286 |
287 | ```javascript
288 | export const patients = {
289 | template: `
290 |
296 | `,
297 | controller: PatientsController
298 | }
299 | ```
300 |
301 | ### Data
302 |
303 | Let's create a mock json file under the "MockDataFolder" let's call it _patients.json
304 |
305 | ```json
306 | [
307 | {
308 | "id": 1,
309 | "dni": "1234567A",
310 | "name": "John Doe",
311 | "specialty": "Traumatología",
312 | "doctor": "Karl J. Linville",
313 | "date": "2017-02-15T00:00:00Z",
314 | "time": "2017-02-15T00:08:30Z"
315 | },
316 | {
317 | "id": 2,
318 | "dni": "5067254B",
319 | "name": "Anna S. Batiste",
320 | "specialty": "Cirugía",
321 | "doctor": "Gladys C. Horton",
322 | "date": "2017-02-15T00:00:00Z",
323 | "time": "2017-02-15T09:10:00Z"
324 | },
325 | {
326 | "id": 3,
327 | "dni": "1902045C",
328 | "name": "Octavia L. Hilton",
329 | "specialty": "Traumatología",
330 | "doctor": "Karl J. Linville",
331 | "date": "2017-03-17T00:00:00Z",
332 | "time": "2017-03-17T10:10:00Z"
333 | },
334 | {
335 | "id": 4,
336 | "dni": "1880514D",
337 | "name": "Tony M. Herrera",
338 | "specialty": "Oftalmología",
339 | "doctor": "Ruthie A. Nemeth",
340 | "date": "2017-03-17T00:00:00Z",
341 | "time": "2017-03-17T11:00:00Z"
342 | },
343 | {
344 | "id": 5,
345 | "dni": "6810774E",
346 | "name": "Robert J. Macias",
347 | "specialty": "Cirugía",
348 | "doctor": "Gladys C. Horton",
349 | "date": "2017-02-15T00:00:00Z",
350 | "time": "2017-02-15T11:30:00Z"
351 | }
352 | ]
353 | ```
354 |
355 | We need to copy this mock data to the folder where the dev server is going to run,
356 | in order to do this we are going to use _copy_webpack_plugin, let's install it
357 |
358 | ```
359 | npm install copy-webpack-plugin --save-dev
360 | ```
361 |
362 | Then in the _webpack.config.js_ file let's properly configure the plugin:
363 |
364 | ```javascript
365 |
366 | var CopyWebpackPlugin = require('copy-webpack-plugin');
367 |
368 | ...
369 |
370 | plugins: [
371 | /// (...)
372 | new CopyWebpackPlugin([
373 | { from: 'mockData/*'},
374 | ])
375 | ]
376 | ```
377 |
378 | Let's start by creating an entity called _Patient_ that will hold info about the patient and a medical appointment,
379 | we will place this file under _/src/model/patient.ts_
380 |
381 | ```javascript
382 | export class Patient {
383 | id: number;
384 | dni: string;
385 | name: string;
386 | specialty: string;
387 | doctor: string;
388 | date: Date;
389 | time: Date;
390 | }
391 | ```
392 |
393 | And to end up with the client data layer, we will create a fake api (promise based) that will expose methods load the list of
394 | appointments plus specialties.
395 |
396 | ```javascript
397 | import { Patient } from '../model/patient';
398 |
399 | export class PatientAPI {
400 | public static $inject: Array = ["$http"];
401 |
402 | private baseUrl: string = './mockData/patients.json';
403 |
404 | constructor(private $http : angular.IHttpService) {
405 |
406 | }
407 |
408 | getAllPatientsAsync(): Promise> {
409 | return this.$http.get(this.baseUrl).then(response => response.data);
410 | };
411 | }
412 | ```
413 |
414 | We need to register this service in the main app.
415 |
416 | ```javascript
417 | import {PatientAPI} from './api/patientAPI';
418 |
419 | app.service('PatientAPI', PatientAPI);
420 | ```
421 |
422 | ### Interaction
423 |
424 | Now it's time to load the information about the patient's appointments in the
425 | appointments table.
426 |
427 | First of all let's import some needed name space (patients entitiy, plus
428 | patients data api) in our _patientsList.ts
429 |
430 | ```javascript
431 | import {PatientAPI} from "../../api/patientAPI";
432 | import {Patient} from '../../model/patient';
433 | ```
434 |
435 | We are going to define a member variable that will hold a list of Patients,
436 | and request the PatientsAPI service, then we will make the AJAX called
437 | to dynamically load the list of patients (_patientsList.ts_).
438 |
439 | ```javascript
440 | class PatientsListController {
441 | public static $inject: Array = ["PatientAPI"];
442 | public patients : Array = []
443 |
444 | constructor(patientAPI : PatientAPI) {
445 | patientAPI.getAllPatientsAsync().then((data) => {
446 | this.patients = data;
447 | }
448 | );
449 | }
450 | }
451 | ```
452 |
453 | Finally we are going to bind the list into the layout using an ng-repeat
454 | and binding the fields to the given span.
455 |
456 | ```html
457 |
458 | {{patient.dni}}
459 | {{patient.name}}
460 |
461 | {{patient.specialty}}
462 |
464 |
465 |
466 | {{patient.doctor}}
467 | {{patient.date | date:'dd/MM/yyyy'}}
468 |
469 | {{patient.time | date:'hh:mm'}}
470 |
472 |
473 |
474 |
475 | ```
476 |
--------------------------------------------------------------------------------
/03 List Page/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular1_5-sample-app",
3 | "version": "1.0.0",
4 | "description": "Angular 1.5 Sample App",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/Lemoncode/angular1_5-sample-app.git"
12 | },
13 | "homepage": "https://github.com/Lemoncode/angular1_5-sample-app",
14 | "keywords": [
15 | "angular",
16 | "sample",
17 | "app"
18 | ],
19 | "author": "Lemoncode",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "@types/angular": "^1.6.7",
23 | "@types/angular-ui-router": "^1.1.36",
24 | "@types/es6-promise": "0.0.32",
25 | "@types/jquery": "^2.0.40",
26 | "copy-webpack-plugin": "^3.0.1",
27 | "css-loader": "^0.25.0",
28 | "extract-text-webpack-plugin": "^1.0.1",
29 | "file-loader": "^0.9.0",
30 | "html-webpack-plugin": "^2.22.0",
31 | "style-loader": "^0.13.1",
32 | "ts-loader": "^2.0.1",
33 | "typescript": "^2.2.1",
34 | "url-loader": "^0.5.7",
35 | "webpack": "^1.13.2",
36 | "webpack-dev-server": "^1.15.1"
37 | },
38 | "dependencies": {
39 | "angular": "^1.5.8",
40 | "angular-ui-router": "^0.4.2",
41 | "bootstrap": "^3.3.7",
42 | "jquery": "^3.1.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/03 List Page/src/api/patientAPI.ts:
--------------------------------------------------------------------------------
1 | import { Patient } from '../model/patient';
2 |
3 | export class PatientAPI {
4 | public static $inject: Array = ["$http"];
5 |
6 | private baseUrl: string = './mockData/patients.json';
7 |
8 | constructor(private $http : angular.IHttpService) {
9 |
10 | }
11 |
12 | getAllPatientsAsync(): Promise> {
13 | return this.$http.get(this.baseUrl).then(response => response.data);
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/03 List Page/src/app-routes.ts:
--------------------------------------------------------------------------------
1 | function routing($locationProvider: ng.ILocationProvider,
2 | $stateProvider: angular.ui.IStateProvider,
3 | $urlRouterProvider: angular.ui.IUrlRouterProvider) {
4 |
5 | // html5 removes the need for # in URL
6 | $locationProvider.html5Mode({
7 | enabled: false
8 | });
9 |
10 | $stateProvider.state('home', {
11 | url: '/home',
12 | views: {
13 | 'content@': { template: ' ' }
14 | }
15 | }
16 | );
17 |
18 | $stateProvider.state('patients', {
19 | url: '/patients',
20 | views: {
21 | 'content@': { template: ' ' }
22 | }
23 | }
24 | );
25 |
26 | $stateProvider.state('patient', {
27 | url: '/patient',
28 | views: {
29 | 'content@': { template: ' ' }
30 | }
31 | }
32 | );
33 |
34 | $urlRouterProvider.otherwise('/home');
35 | }
36 |
37 | export default routing;
38 |
--------------------------------------------------------------------------------
/03 List Page/src/components/common/header.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class HeaderController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Testing binding header component";
8 | }
9 | }
10 |
11 | export const header = {
12 | template: 'Header testing bindings: {{$ctrl.sampleBinding}} ',
13 | controller: HeaderController
14 | }
15 |
--------------------------------------------------------------------------------
/03 List Page/src/components/login/login.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class LoginController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Hello from Login";
8 | }
9 | }
10 |
11 | export const login = {
12 | template: `
13 | bindings test: {{$ctrl.sampleBinding}}
14 | Navigate to patients
15 | `,
16 | controller: LoginController
17 | }
18 |
--------------------------------------------------------------------------------
/03 List Page/src/components/patient/patient.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class PatientController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Hello from Patient";
8 | }
9 | }
10 |
11 | export const patient = {
12 | template: `
13 | Bindings test: {{$ctrl.sampleBinding}}
14 | `,
15 | controller: PatientController
16 | }
17 |
--------------------------------------------------------------------------------
/03 List Page/src/components/patients/patients.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class PatientsController {
4 | constructor() {
5 |
6 | }
7 | }
8 |
9 | export const patients = {
10 | template: `
11 |
17 | `,
18 | controller: PatientsController
19 | }
20 |
--------------------------------------------------------------------------------
/03 List Page/src/components/patients/patientsList.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 | import {PatientAPI} from "../../api/patientAPI";
3 | import {Patient} from '../../model/patient';
4 |
5 | class PatientsListController {
6 | public static $inject: Array = ["PatientAPI"];
7 | public patients : Array = []
8 |
9 | constructor(patientAPI : PatientAPI) {
10 | patientAPI.getAllPatientsAsync().then((data) => {
11 | this.patients = data;
12 | }
13 | );
14 | }
15 | }
16 |
17 | export const patientsList = {
18 | template:
19 | `
20 |
21 |
28 |
29 |
30 |
31 |
32 | DNI
33 | Paciente
34 | Especialidad
35 | Doctor
36 | Cita
37 | Hora
38 |
39 |
40 |
41 |
42 | {{patient.dni}}
43 | {{patient.name}}
44 |
45 | {{patient.specialty}}
46 |
48 |
49 |
50 | {{patient.doctor}}
51 | {{patient.date | date:'dd/MM/yyyy'}}
52 |
53 | {{patient.time | date:'hh:mm'}}
54 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | `,
64 | controller: PatientsListController
65 | }
66 |
--------------------------------------------------------------------------------
/03 List Page/src/components/patients/searchPatient.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class SearchPatientController {
4 | constructor() {
5 | }
6 | }
7 |
8 | export const searchPatient = {
9 | template:
10 | `
11 |
12 |
13 |
14 |
15 |
Buscar paciente
16 |
18 |
19 |
20 |
43 |
44 |
45 | `,
46 | controller: SearchPatientController
47 | }
48 |
--------------------------------------------------------------------------------
/03 List Page/src/css/site.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: #ACDF2C
3 | }
4 |
--------------------------------------------------------------------------------
/03 List Page/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 1.5 Sample App
6 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/03 List Page/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular'
2 | import 'angular-ui-router';
3 | import routing from './app-routes';
4 | import {header} from './components/common/header';
5 | import {login} from './components/login/login';
6 | import {patients} from './components/patients/patients';
7 | import {patient} from './components/patient/patient';
8 | import {searchPatient} from './components/patients/searchPatient';
9 | import {patientsList} from './components/patients/patientsList';
10 | import {PatientAPI} from './api/patientAPI';
11 |
12 | var app = angular.module('myAppointmentsApp', ['ui.router'])
13 | .config(routing);
14 |
15 | app.service('PatientAPI', PatientAPI);
16 |
17 | app.component('header', header);
18 | app.component('login', login);
19 | app.component('patients', patients);
20 | app.component('patient', patient);
21 | app.component('searchPatient', searchPatient);
22 | app.component('patientsList', patientsList);
23 |
--------------------------------------------------------------------------------
/03 List Page/src/mockData/patients.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "dni": "1234567A",
5 | "name": "John Doe",
6 | "specialty": "Traumatology",
7 | "doctor": "Karl J. Linville",
8 | "date": "2017-02-15T00:00:00Z",
9 | "time": "2017-02-15T00:08:30Z"
10 | },
11 | {
12 | "id": 2,
13 | "dni": "5067254B",
14 | "name": "Anna S. Batiste",
15 | "specialty": "Surgery",
16 | "doctor": "Gladys C. Horton",
17 | "date": "2017-02-15T00:00:00Z",
18 | "time": "2017-02-15T09:10:00Z"
19 | },
20 | {
21 | "id": 3,
22 | "dni": "1902045C",
23 | "name": "Octavia L. Hilton",
24 | "specialty": "Traumatology",
25 | "doctor": "Karl J. Linville",
26 | "date": "2017-03-17T00:00:00Z",
27 | "time": "2017-03-17T10:10:00Z"
28 | },
29 | {
30 | "id": 4,
31 | "dni": "1880514D",
32 | "name": "Tony M. Herrera",
33 | "specialty": "Ophthalmology",
34 | "doctor": "Ruthie A. Nemeth",
35 | "date": "2017-03-17T00:00:00Z",
36 | "time": "2017-03-17T11:00:00Z"
37 | },
38 | {
39 | "id": 5,
40 | "dni": "6810774E",
41 | "name": "Robert J. Macias",
42 | "specialty": "Surgery",
43 | "doctor": "Gladys C. Horton",
44 | "date": "2017-02-15T00:00:00Z",
45 | "time": "2017-02-15T11:30:00Z"
46 | }
47 | ]
48 |
--------------------------------------------------------------------------------
/03 List Page/src/model/patient.ts:
--------------------------------------------------------------------------------
1 |
2 | export class Patient {
3 | id: number;
4 | dni: string;
5 | name: string;
6 | specialty: string;
7 | doctor: string;
8 | date: string;
9 | time: string;
10 | }
11 |
--------------------------------------------------------------------------------
/03 List Page/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "sourceMap": true,
9 | "jsx": "react",
10 | "experimentalDecorators": true,
11 | "emitDecoratorMetadata": true,
12 | "noLib": false,
13 | "preserveConstEnums": true,
14 | "suppressImplicitAnyIndexErrors": true
15 | },
16 | "compileOnSave": false,
17 | "exclude": [
18 | "node_modules"
19 | ],
20 | "atom": {
21 | "rewriteTsconfig": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/03 List Page/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | var CopyWebpackPlugin = require('copy-webpack-plugin');
6 | var basePath = __dirname;
7 |
8 | module.exports = {
9 | context: path.join(basePath, "src"),
10 | resolve: {
11 | extensions: ['', '.js', '.ts']
12 | },
13 |
14 | entry: {
15 | app: './index.ts',
16 | styles: [
17 | '../node_modules/bootstrap/dist/css/bootstrap.css',
18 | './css/site.css'
19 | ],
20 | vendor: [
21 | 'bootstrap'
22 | ]
23 | },
24 |
25 | output: {
26 | path: path.join(basePath, "dist"),
27 | filename: '[name].js'
28 | },
29 |
30 | devServer: {
31 | contentBase: './dist', //Content base
32 | inline: true, //Enable watch and live reload
33 | host: 'localhost',
34 | port: 8080
35 | },
36 |
37 | devtool: 'source-map',
38 |
39 | module: {
40 | loaders: [
41 | {
42 | test: /\.ts$/,
43 | exclude: /node_modules/,
44 | loader: 'ts'
45 | },
46 | {
47 | test: /\.css$/,
48 | loader: ExtractTextPlugin.extract('style','css')
49 | },
50 | //Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack
51 | {test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
52 | {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
53 | {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
54 | {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" },
55 | {
56 | test: /\.png$/,
57 | loader: 'file?limit=0&name=[path][name].[hash].',
58 | exclude: /node_modules/
59 | }
60 |
61 | ]
62 | },
63 |
64 | plugins: [
65 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
66 | new ExtractTextPlugin('[name].css'),
67 | new HtmlWebpackPlugin({
68 | filename: 'index.html',
69 | template: 'index.html'
70 | }),
71 | new webpack.ProvidePlugin({
72 | $: "jquery",
73 | jQuery: "jquery"
74 | }),
75 | new CopyWebpackPlugin([
76 | { from: 'mockData/*'},
77 | ])
78 | ]
79 | }
80 |
--------------------------------------------------------------------------------
/04 Form Page/README.md:
--------------------------------------------------------------------------------
1 | # 04 Form Page
2 |
3 | In this sample we are going to build up an appoinment edit form.
4 |
5 | We are going to take as startup point _03 List Page
6 |
7 | # Summary steps:
8 |
9 | - Add navigation link.
10 | - Build the component layout.
11 | - Create API plumbing to retrieve data from a given appointment.
12 | - Load the data into the component controller and bind them to the HTML.
13 | - Trigger save on console log.
14 |
15 | # Steps to build it
16 |
17 | ## Prerequisites
18 |
19 | Prerequisites, you will need to have nodejs installed in your computer. If you want to follow this step guides you will need to take as starting point sample "03 List Page".
20 |
21 | ## Steps
22 |
23 | ### Navigation
24 |
25 | Let's start by adding a link navigation, whenever we click on a given appointment
26 | (in the list page) it should jump into the edit appointment entry.
27 |
28 | Since we are going to introduce a new route _patient/id_ let's add it into the
29 | router (_app-routes.ts_):
30 |
31 | ```javascript
32 | $stateProvider.state('patientEdit', {
33 | url: '/patient/{patientId:[0-9]{1,8}}',
34 | views: {
35 | 'content@': { template: ' ' }
36 | }
37 | }
38 | );
39 | ```
40 |
41 | Now in _ListPage.ts_ let's create a link that will point to that route:
42 |
43 | ```html
44 |
45 | {{patient.time}}
46 |
47 |
49 |
50 |
51 |
52 | ```
53 |
54 |
55 | Let's run _npm start_ from the command line and check that the navigation is being
56 | performed:
57 |
58 | ```javascript
59 | npm start
60 | ```
61 |
62 |
63 | ### Layout
64 |
65 | It's time to build the appointment edition layout, let's jump into the _patient/patient.ts_ file and replace
66 | the component template with the following one.
67 |
68 | ```html
69 |
70 |
71 |
72 |
Update Appointment
73 |
74 |
75 |
76 |
117 |
118 | ```
119 |
120 | ### Data
121 |
122 | Since we are just mocking data, we are going to add an entry to the _api/patientAPI_ that will load a single
123 | appointment, by passing as entry param it's ID (we will load the whole json file then filter in memory, **remark:
124 | this is just a mock dummy data layer, do not do this in a real project**).
125 |
126 | We will use promises for this, since angular needs it's special tricks to get on the $scope digest cycle, we
127 | cannot directly use ES6 promises, we have to use $q.
128 |
129 | Let's request this service into the patientAPI
130 |
131 | ```javascript
132 | export class PatientAPI {
133 | public static $inject: Array = ['$http', '$q'];
134 |
135 | private baseUrl: string = './mockData/patients.json';
136 |
137 | constructor(private $http : angular.IHttpService, private $q : angular.IQService) {
138 | ```
139 |
140 |
141 | ```javascript
142 | getPatientById(id: number) : Promise {
143 | const defer = this.$q.defer();
144 |
145 | this.getAllPatientsAsync().then((patients) => {
146 | // refine this later one
147 | const nonTypedPatient = patients.filter(
148 | (patient) => {
149 | return (patient.id == id);
150 | }
151 | )[0];
152 |
153 | const patient : Patient = nonTypedPatient;
154 |
155 | // Mapping should be placed in a separate map
156 | patient.date = new Date(nonTypedPatient.date);
157 | patient.time = new Date(nonTypedPatient.time)
158 |
159 | defer.resolve(patient);
160 | });
161 |
162 | return defer.promise;
163 | }
164 | ```
165 |
166 | ## Interaction
167 |
168 | In the _patient/patient.ts_ component we are going to load the appointment information by getting the Id from
169 | the route param, and the call the api _loadPatient_ method.
170 |
171 | ```javascript
172 | import * as angular from 'angular';
173 | import {PatientAPI} from "../../api/patientAPI";
174 | import {Patient} from '../../model/patient';
175 |
176 | class PatientController {
177 | public static $inject: Array = ['PatientAPI', '$stateParams'];
178 | public patient : Patient = null;
179 |
180 | constructor(patientAPI : PatientAPI, $stateParams : angular.ui.IStateParamsService) {
181 | const patientId : number = $stateParams['patientId'];
182 |
183 | patientAPI.getPatientById(patientId).then((data) => {
184 | this.patient = data;
185 | });
186 |
187 | }
188 | }
189 | ```
190 |
191 | We have the data loaded in our component let's bind it and display it in our form (let's part of the
192 | _patient/patient.ts_ template content, not down: we are using ng-model directive to bind the forms controls
193 | to the patient/appointment info).
194 |
195 | Let's bind first the straight forward fields (ng-model inputs)
196 |
197 | ```html
198 |
199 | DNI
200 |
205 |
206 |
207 | Name
208 |
213 |
214 |
215 | Appointment info
216 |
217 |
218 | Date
219 |
224 |
225 |
226 | Time
227 |
232 |
233 | ```
234 |
235 | Now let's jump into feeding dropdown like entries.
236 |
237 | ```html
238 |
239 | Specialty
240 |
244 | {{option}}
245 |
246 |
247 |
248 | Doctor
249 |
253 | {{option}}
254 |
255 |
256 | ```
257 |
258 | Let's add an implementation for the save button:
259 |
260 | For this sample we will just dump the updated entity into the console log in order
261 | to do that we have to request angular IOC for the $log service
262 |
263 | ```
264 | public static $inject: Array = ['PatientAPI', '$stateParams', '$log'];
265 |
266 | constructor(patientAPI : PatientAPI,
267 | $stateParams : angular.ui.IStateParamsService,
268 | private $log : angular.ILogService) {
269 | ```
270 |
271 |
272 | ```
273 | save() {
274 | this.$log.log(this.patient);
275 | }
276 | ```
277 |
278 | And let's bind it to the button click event
279 |
280 | ```
281 | Guardar
282 | ```
283 |
--------------------------------------------------------------------------------
/04 Form Page/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular1_5-sample-app",
3 | "version": "1.0.0",
4 | "description": "Angular 1.5 Sample App",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/Lemoncode/angular1_5-sample-app.git"
12 | },
13 | "homepage": "https://github.com/Lemoncode/angular1_5-sample-app",
14 | "keywords": [
15 | "angular",
16 | "sample",
17 | "app"
18 | ],
19 | "author": "Lemoncode",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "@types/angular": "^1.6.7",
23 | "@types/angular-ui-router": "^1.1.36",
24 | "@types/es6-promise": "0.0.32",
25 | "@types/jquery": "^2.0.40",
26 | "copy-webpack-plugin": "^3.0.1",
27 | "css-loader": "^0.25.0",
28 | "extract-text-webpack-plugin": "^1.0.1",
29 | "file-loader": "^0.9.0",
30 | "html-webpack-plugin": "^2.22.0",
31 | "style-loader": "^0.13.1",
32 | "ts-loader": "^2.0.1",
33 | "typescript": "^2.2.1",
34 | "url-loader": "^0.5.7",
35 | "webpack": "^1.13.2",
36 | "webpack-dev-server": "^1.15.1"
37 | },
38 | "dependencies": {
39 | "angular": "^1.5.8",
40 | "angular-ui-router": "^0.4.2",
41 | "bootstrap": "^3.3.7",
42 | "jquery": "^3.1.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/04 Form Page/src/api/patientAPI.ts:
--------------------------------------------------------------------------------
1 | import { Patient } from '../model/patient';
2 |
3 | export class PatientAPI {
4 | public static $inject: Array = ['$http', '$q'];
5 |
6 | private baseUrl: string = './mockData/patients.json';
7 |
8 | constructor(private $http : angular.IHttpService, private $q : angular.IQService) {
9 |
10 | }
11 |
12 | getAllPatientsAsync(): Promise> {
13 | return this.$http.get(this.baseUrl).then(response => response.data);
14 | };
15 |
16 | getPatientById(id: number) : Promise {
17 | const defer = this.$q.defer();
18 |
19 | this.getAllPatientsAsync().then((patients) => {
20 | // refine this later one
21 | const nonTypedPatient = patients.filter(
22 | (patient) => {
23 | return (patient.id == id);
24 | }
25 | )[0];
26 |
27 | const patient : Patient = nonTypedPatient;
28 |
29 | // Mapping should be placed in a separate map
30 | patient.date = new Date(nonTypedPatient.date);
31 | patient.time = new Date(nonTypedPatient.time)
32 |
33 | defer.resolve(patient);
34 | });
35 |
36 | return defer.promise;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/04 Form Page/src/app-routes.ts:
--------------------------------------------------------------------------------
1 | function routing($locationProvider: ng.ILocationProvider,
2 | $stateProvider: angular.ui.IStateProvider,
3 | $urlRouterProvider: angular.ui.IUrlRouterProvider) {
4 |
5 | // html5 removes the need for # in URL
6 | $locationProvider.html5Mode({
7 | enabled: false
8 | });
9 |
10 | $stateProvider.state('home', {
11 | url: '/home',
12 | views: {
13 | 'content@': { template: ' ' }
14 | }
15 | }
16 | );
17 |
18 | $stateProvider.state('patients', {
19 | url: '/patients',
20 | views: {
21 | 'content@': { template: ' ' }
22 | }
23 | }
24 | );
25 |
26 | $stateProvider.state('patient', {
27 | url: '/patient',
28 | views: {
29 | 'content@': { template: ' ' }
30 | }
31 | }
32 | );
33 |
34 | $stateProvider.state('patientEdit', {
35 | url: '/patient/{patientId:[0-9]{1,8}}',
36 | views: {
37 | 'content@': { template: ' ' }
38 | }
39 | }
40 | );
41 |
42 |
43 | $urlRouterProvider.otherwise('/home');
44 | }
45 |
46 | export default routing;
47 |
--------------------------------------------------------------------------------
/04 Form Page/src/components/common/header.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class HeaderController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Testing binding header component";
8 | }
9 | }
10 |
11 | export const header = {
12 | template: 'Header testing bindings: {{$ctrl.sampleBinding}} ',
13 | controller: HeaderController
14 | }
15 |
--------------------------------------------------------------------------------
/04 Form Page/src/components/login/login.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class LoginController {
4 | sampleBinding : string;
5 |
6 | constructor() {
7 | this.sampleBinding = "Hello from Login";
8 | }
9 | }
10 |
11 | export const login = {
12 | template: `
13 | bindings test: {{$ctrl.sampleBinding}}
14 | Navigate to patients
15 | `,
16 | controller: LoginController
17 | }
18 |
--------------------------------------------------------------------------------
/04 Form Page/src/components/patient/patient.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 | import {PatientAPI} from "../../api/patientAPI";
3 | import {Patient} from '../../model/patient';
4 |
5 | class PatientController {
6 | public static $inject: Array = ['PatientAPI', '$stateParams', '$log'];
7 | public patient : Patient = null;
8 | public specialties : Array;
9 | public doctors : Array;
10 |
11 | constructor(patientAPI : PatientAPI,
12 | $stateParams : angular.ui.IStateParamsService,
13 | private $log : angular.ILogService) {
14 | const patientId : number = $stateParams['patientId'];
15 |
16 | patientAPI.getPatientById(patientId).then((data) => {
17 | this.patient = data;
18 | });
19 |
20 | // TODO: We could load this info form a service
21 | // and use id / value
22 | this.specialties = ['Traumatology', 'Surgery', 'Ophthalmology']
23 | this.doctors = ['Karl J. Linville', 'Gladys C. Horton','Ruthie A. Nemeth']
24 | // More info about how to bind combo's / lists...
25 | // https://docs.angularjs.org/api/ng/directive/select
26 | // https://docs.angularjs.org/api/ng/directive/ngOptions
27 | }
28 |
29 | save() {
30 | this.$log.log(this.patient);
31 | }
32 | }
33 |
34 | export const patient = {
35 | template: `
36 |
37 |
38 |
39 |
Update Appointment
40 |
41 |
42 |
43 |
108 |
109 | `,
110 | controller: PatientController
111 | }
112 |
--------------------------------------------------------------------------------
/04 Form Page/src/components/patients/patients.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class PatientsController {
4 | constructor() {
5 |
6 | }
7 | }
8 |
9 | export const patients = {
10 | template: `
11 |
17 | `,
18 | controller: PatientsController
19 | }
20 |
--------------------------------------------------------------------------------
/04 Form Page/src/components/patients/patientsList.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 | import {PatientAPI} from "../../api/patientAPI";
3 | import {Patient} from '../../model/patient';
4 |
5 | class PatientsListController {
6 | public static $inject: Array = ["PatientAPI"];
7 | public patients : Array = []
8 |
9 | constructor(patientAPI : PatientAPI) {
10 | patientAPI.getAllPatientsAsync().then((data) => {
11 | this.patients = data;
12 | }
13 | );
14 | }
15 | }
16 |
17 | export const patientsList = {
18 | template:
19 | `
20 |
21 |
28 |
29 |
30 |
31 |
32 | DNI
33 | Patient
34 | Specialty
35 | Doctor
36 | Cita
37 | Hora
38 |
39 |
40 |
41 |
42 | {{patient.dni}}
43 | {{patient.name}}
44 |
45 | {{patient.specialty}}
46 |
48 |
49 |
50 | {{patient.doctor}}
51 | {{patient.date}}
52 |
53 | {{patient.time}}
54 |
55 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | `,
66 | controller: PatientsListController
67 | }
68 |
--------------------------------------------------------------------------------
/04 Form Page/src/components/patients/searchPatient.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular';
2 |
3 | class SearchPatientController {
4 | constructor() {
5 | }
6 | }
7 |
8 | export const searchPatient = {
9 | template:
10 | `
11 |
12 |
13 |
14 |
15 |
Buscar paciente
16 |
18 |
19 |
20 |
43 |
44 |
45 | `,
46 | controller: SearchPatientController
47 | }
48 |
--------------------------------------------------------------------------------
/04 Form Page/src/css/site.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | color: #ACDF2C
3 | }
4 |
--------------------------------------------------------------------------------
/04 Form Page/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 1.5 Sample App
6 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/04 Form Page/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as angular from 'angular'
2 | import 'angular-ui-router';
3 | import routing from './app-routes';
4 | import {header} from './components/common/header';
5 | import {login} from './components/login/login';
6 | import {patients} from './components/patients/patients';
7 | import {patient} from './components/patient/patient';
8 | import {searchPatient} from './components/patients/searchPatient';
9 | import {patientsList} from './components/patients/patientsList';
10 | import {PatientAPI} from './api/patientAPI';
11 |
12 | var app = angular.module('myAppointmentsApp', ['ui.router'])
13 | .config(routing);
14 |
15 | app.service('PatientAPI', PatientAPI);
16 |
17 | app.component('header', header);
18 | app.component('login', login);
19 | app.component('patients', patients);
20 | app.component('patient', patient);
21 | app.component('searchPatient', searchPatient);
22 | app.component('patientsList', patientsList);
23 |
--------------------------------------------------------------------------------
/04 Form Page/src/mockData/patients.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "dni": "1234567A",
5 | "name": "John Doe",
6 | "specialty": "Traumatology",
7 | "doctor": "Karl J. Linville",
8 | "date": "2017-02-15T00:00:00Z",
9 | "time": "2017-02-15T00:08:30Z"
10 | },
11 | {
12 | "id": 2,
13 | "dni": "5067254B",
14 | "name": "Anna S. Batiste",
15 | "specialty": "Surgery",
16 | "doctor": "Gladys C. Horton",
17 | "date": "2017-02-15T00:00:00Z",
18 | "time": "2017-02-15T09:10:00Z"
19 | },
20 | {
21 | "id": 3,
22 | "dni": "1902045C",
23 | "name": "Octavia L. Hilton",
24 | "specialty": "Traumatology",
25 | "doctor": "Karl J. Linville",
26 | "date": "2017-03-17T00:00:00Z",
27 | "time": "2017-03-17T10:10:00Z"
28 | },
29 | {
30 | "id": 4,
31 | "dni": "1880514D",
32 | "name": "Tony M. Herrera",
33 | "specialty": "Ophthalmology",
34 | "doctor": "Ruthie A. Nemeth",
35 | "date": "2017-03-17T00:00:00Z",
36 | "time": "2017-03-17T11:00:00Z"
37 | },
38 | {
39 | "id": 5,
40 | "dni": "6810774E",
41 | "name": "Robert J. Macias",
42 | "specialty": "Surgery",
43 | "doctor": "Gladys C. Horton",
44 | "date": "2017-02-15T00:00:00Z",
45 | "time": "2017-02-15T11:30:00Z"
46 | }
47 | ]
48 |
--------------------------------------------------------------------------------
/04 Form Page/src/model/patient.ts:
--------------------------------------------------------------------------------
1 |
2 | export class Patient {
3 | id: number;
4 | dni: string;
5 | name: string;
6 | specialty: string;
7 | doctor: string;
8 | date: Date;
9 | time: Date;
10 | }
11 |
--------------------------------------------------------------------------------
/04 Form Page/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "declaration": false,
6 | "noImplicitAny": false,
7 | "removeComments": true,
8 | "sourceMap": true,
9 | "jsx": "react",
10 | "experimentalDecorators": true,
11 | "emitDecoratorMetadata": true,
12 | "noLib": false,
13 | "preserveConstEnums": true,
14 | "suppressImplicitAnyIndexErrors": true
15 | },
16 | "compileOnSave": false,
17 | "exclude": [
18 | "node_modules"
19 | ],
20 | "atom": {
21 | "rewriteTsconfig": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/04 Form Page/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var HtmlWebpackPlugin = require('html-webpack-plugin');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | var CopyWebpackPlugin = require('copy-webpack-plugin');
6 | var basePath = __dirname;
7 |
8 | module.exports = {
9 | context: path.join(basePath, "src"),
10 | resolve: {
11 | extensions: ['', '.js', '.ts']
12 | },
13 |
14 | entry: {
15 | app: './index.ts',
16 | styles: [
17 | '../node_modules/bootstrap/dist/css/bootstrap.css',
18 | './css/site.css'
19 | ],
20 | vendor: [
21 | 'bootstrap'
22 | ]
23 | },
24 |
25 | output: {
26 | path: path.join(basePath, "dist"),
27 | filename: '[name].js'
28 | },
29 |
30 | devServer: {
31 | contentBase: './dist', //Content base
32 | inline: true, //Enable watch and live reload
33 | host: 'localhost',
34 | port: 8080
35 | },
36 |
37 | devtool: 'source-map',
38 |
39 | module: {
40 | loaders: [
41 | {
42 | test: /\.ts$/,
43 | exclude: /node_modules/,
44 | loader: 'ts'
45 | },
46 | {
47 | test: /\.css$/,
48 | loader: ExtractTextPlugin.extract('style','css')
49 | },
50 | //Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack
51 | {test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff" },
52 | {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream" },
53 | {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
54 | {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml" },
55 | {
56 | test: /\.png$/,
57 | loader: 'file?limit=0&name=[path][name].[hash].',
58 | exclude: /node_modules/
59 | }
60 |
61 | ]
62 | },
63 |
64 | plugins: [
65 | new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
66 | new ExtractTextPlugin('[name].css'),
67 | new HtmlWebpackPlugin({
68 | filename: 'index.html',
69 | template: 'index.html'
70 | }),
71 | new webpack.ProvidePlugin({
72 | $: "jquery",
73 | jQuery: "jquery"
74 | }),
75 | new CopyWebpackPlugin([
76 | { from: 'mockData/*'},
77 | ])
78 | ]
79 | }
80 |
--------------------------------------------------------------------------------
/05 Form Validation/README.md:
--------------------------------------------------------------------------------
1 | # 05 Form Validation
2 |
3 | In this sample we are going to add validation to the appoint form that we have
4 | previously created.
5 |
6 | We are going to take as startup point _04 Form Page_
7 |
8 | # Summary steps:
9 |
10 | - Add message validation support.
11 | - Add basic validations.
12 | - Add a custom validation (NIF).
13 |
14 | # Steps to build it
15 |
16 | ## Prerequisites
17 |
18 | Prerequisites, you will need to have nodejs installed in your computer. If you want
19 | to follow this step guides you will need to take as starting point sample "04 From Page".
20 |
21 | ## Steps
22 |
23 | ### Adding libraries
24 |
25 | First we are going to install and add to the project an angular library to
26 | display error messages (angular-messages). In this case we need to make some special
27 | tweaking
28 |
29 | Install packages:
30 |
31 |
32 | ```
33 | npm install angular-messages --save
34 | ```
35 |
36 | ```
37 | npm install @types/node --save-dev
38 | ```
39 |
40 |
41 | Let's include this dependency in our project (_index.ts_).
42 |
43 |
44 | ```javascript
45 | const angMessages = require('angular-messages');
46 | var app = angular.module('myAppointmentsApp', ['ui.router',angMessages]).config(routing);
47 | ```
48 |
49 |
50 |
51 | ### Adding basic validation
52 |
53 | Let's start by making the NIF field required and notify the user in case this
54 | validation fails.
55 |
56 | First we need to add a name to the form element, then add a name as well to
57 | the input that will hold the input tag.
58 |
59 | ```html
60 |