├── Views ├── Home │ └── Index.cshtml ├── _ViewStart.cshtml ├── _ViewImports.cshtml └── Shared │ └── _Layout.cshtml ├── src ├── assets │ ├── .gitkeep │ ├── client-config.json │ ├── resume.png │ └── sample.pdf ├── app │ ├── app.component.css │ ├── welcome │ │ ├── welcome.component.css │ │ ├── welcome.component.ts │ │ └── welcome.component.html │ ├── seo │ │ ├── seo-tests │ │ │ ├── seo-tests.component.css │ │ │ └── seo-tests.component.html │ │ ├── seo.module.ts │ │ └── seo-routing.module.ts │ ├── client-ip-address │ │ ├── get-ip │ │ │ ├── get-ip.component.css │ │ │ ├── get-ip.component.html │ │ │ └── get-ip.component.ts │ │ ├── ip.ts │ │ ├── client-ip-address-routing.module.ts │ │ ├── client-ip-address.module.ts │ │ └── job.service.ts │ ├── core │ │ ├── bread-crumb │ │ │ ├── bread-crumb.component.css │ │ │ ├── bread-crumb.ts │ │ │ ├── bread-crumb.component.html │ │ │ └── bread-crumb.component.ts │ │ ├── index.ts │ │ ├── window.service.ts │ │ ├── modal.service.ts │ │ ├── app-config.service.ts │ │ ├── browser-storage.service.ts │ │ ├── interceptors │ │ │ ├── loader-interceptor.service.ts │ │ │ └── retry.interceptor.ts │ │ ├── seo-service.ts │ │ ├── core.module.ts │ │ └── app.error-handler.ts │ ├── display-pdf │ │ ├── view-pdf │ │ │ ├── view-pdf.component.css │ │ │ ├── view-pdf.component.html │ │ │ └── view-pdf.component.ts │ │ ├── display-pdf-routing.module.ts │ │ ├── download-pdf-data.service.ts │ │ └── display-pdf.module.ts │ ├── page-not-found │ │ ├── page-not-found.component.css │ │ ├── page-not-found.component.html │ │ └── page-not-found.component.ts │ ├── angular-security │ │ ├── show-html │ │ │ ├── show-html.component.css │ │ │ ├── show-html.component.html │ │ │ ├── safe.pipe.ts │ │ │ └── show-html.component.ts │ │ ├── angular-security-routing.module.ts │ │ └── angular-security.module.ts │ ├── bread-crumb-sample │ │ ├── parent1 │ │ │ ├── parent1.component.css │ │ │ ├── parent1.component.html │ │ │ └── parent1.component.ts │ │ ├── parent1-child1 │ │ │ ├── parent1-child1.component.css │ │ │ ├── parent1-child1.component.html │ │ │ └── parent1-child1.component.ts │ │ ├── parent1-child1-child1 │ │ │ ├── parent1-child1-child1.component.css │ │ │ ├── parent1-child1-child1.component.html │ │ │ └── parent1-child1-child1.component.ts │ │ ├── bread-crumb-sample.module.ts │ │ └── bread-crumb-sample-routing.module.ts │ ├── product │ │ ├── product-group │ │ │ ├── product-group.component.css │ │ │ ├── product-group.component.html │ │ │ └── product-group.component.ts │ │ ├── category.ts │ │ ├── product.ts │ │ ├── product-group-form.ts │ │ ├── product-routing.module.ts │ │ ├── product.module.ts │ │ └── product-items.service.ts │ ├── shared │ │ ├── confirm-modal │ │ │ ├── confirm-modal.component.css │ │ │ ├── confirm-modal.component.ts │ │ │ └── confirm-modal.component.html │ │ ├── shared.bootstrap.module.ts │ │ └── shared.module.ts │ ├── timers │ │ ├── using-timers │ │ │ ├── using-timers.component.css │ │ │ ├── using-timers.component.html │ │ │ └── using-timers.component.ts │ │ ├── timers.module.ts │ │ └── timers-routing.module.ts │ ├── custom-two-way-data-binding │ │ ├── child │ │ │ ├── child.component.css │ │ │ ├── child.component.html │ │ │ └── child.component.ts │ │ ├── parent │ │ │ ├── parent.component.css │ │ │ ├── parent.component.ts │ │ │ └── parent.component.html │ │ ├── custom-two-way-data-binding-routing.module.ts │ │ └── custom-two-way-data-binding.module.ts │ ├── linq-like-methods │ │ ├── linq-tests │ │ │ ├── linq-tests.component.css │ │ │ └── linq-tests.component.html │ │ ├── person.ts │ │ ├── linq-like-methods-routing.module.ts │ │ └── linq-like-methods.module.ts │ ├── custom-validators │ │ ├── user-register │ │ │ ├── user-register.component.css │ │ │ └── user-register.component.ts │ │ ├── user.ts │ │ ├── custom-validators-routing.module.ts │ │ ├── email-validator.directive.ts │ │ ├── custom-validators.module.ts │ │ └── equal-validator.directive.ts │ ├── employee │ │ ├── employee-register │ │ │ ├── employee-register.component.css │ │ │ └── employee-register.component.ts │ │ ├── employee.ts │ │ ├── employee-routing.module.ts │ │ ├── employee.module.ts │ │ └── form-poster.service.ts │ ├── service-component-communication │ │ ├── final │ │ │ ├── final.component.css │ │ │ ├── final.component.html │ │ │ └── final.component.ts │ │ ├── first │ │ │ ├── first.component.css │ │ │ ├── first.component.html │ │ │ └── first.component.ts │ │ ├── second │ │ │ ├── second.component.css │ │ │ ├── second.component.html │ │ │ └── second.component.ts │ │ ├── third │ │ │ ├── third.component.css │ │ │ ├── third.component.html │ │ │ └── third.component.ts │ │ ├── sample.service.ts │ │ ├── service-component-communication-routing.module.ts │ │ └── service-component-communication.module.ts │ ├── custom-pipe │ │ ├── moment-jalaali-test │ │ │ ├── moment-jalaali-test.component.css │ │ │ ├── moment-jalaali-test.component.html │ │ │ └── moment-jalaali-test.component.ts │ │ ├── moment-jalaali.pipe.ts │ │ ├── custom-pipe-routing.module.ts │ │ ├── custom-pipe.module.ts │ │ └── json-dates.service.ts │ ├── upload-file │ │ ├── upload-file-simple │ │ │ ├── upload-file-simple.component.css │ │ │ ├── upload-file-simple.component.html │ │ │ └── upload-file-simple.component.ts │ │ ├── ng2-file-upload-test │ │ │ ├── ng2-file-upload-test.component.css │ │ │ └── ng2-file-upload-test.component.html │ │ ├── upload-file-with-progress-bar │ │ │ ├── upload-file-with-progress-bar.component.css │ │ │ └── upload-file-with-progress-bar.component.html │ │ ├── ticket.ts │ │ ├── upload-file-routing.module.ts │ │ ├── upload-file.module.ts │ │ ├── upload-file-simple.service.ts │ │ └── upload-file-with-progress-bar.service.ts │ ├── using-third-party-libraries │ │ ├── typed-sha │ │ │ ├── typed-sha.component.css │ │ │ ├── typed-sha.component.html │ │ │ └── typed-sha.component.ts │ │ ├── untyped-sha │ │ │ ├── untyped-sha.component.css │ │ │ ├── untyped-sha.component.html │ │ │ └── untyped-sha.component.ts │ │ ├── using-third-party-libraries.module.ts │ │ └── using-third-party-libraries-routing.module.ts │ ├── angular-http-client-blob │ │ ├── download-blobs │ │ │ ├── download-blobs.component.css │ │ │ ├── download-blobs.component.html │ │ │ └── download-blobs.component.ts │ │ ├── download-binary-data.service.ts │ │ ├── angular-http-client-blob-routing.module.ts │ │ └── angular-http-client-blob.module.ts │ ├── autocomplete │ │ ├── autocomplete-sample │ │ │ ├── autocomplete-sample.component.css │ │ │ ├── autocomplete-sample.component.html │ │ │ └── autocomplete-sample.component.ts │ │ ├── autocomplete-routing.module.ts │ │ ├── autocomplete.module.ts │ │ └── search.service.ts │ ├── injection-beyond-classes │ │ ├── test-providers │ │ │ ├── test-providers.component.css │ │ │ ├── test-providers.component.html │ │ │ └── test-providers.component.ts │ │ ├── thismodule.config.ts │ │ ├── injection-beyond-classes-routing.module.ts │ │ ├── car.service.ts │ │ └── injection-beyond-classes.module.ts │ ├── post-form-urlencoded │ │ ├── test-urlencoded │ │ │ ├── test-urlencoded.component.css │ │ │ ├── test-urlencoded.component.html │ │ │ └── test-urlencoded.component.ts │ │ ├── post-form-urlencoded-routing.module.ts │ │ ├── post-form-urlencoded.module.ts │ │ └── urlencoded.service.ts │ ├── modal-bootstrap-dialogs │ │ ├── modal-dialog-test │ │ │ ├── modal-dialog-test.component.css │ │ │ ├── modal-dialog-test.component.html │ │ │ └── modal-dialog-test.component.ts │ │ ├── modal-bootstrap-dialogs-routing.module.ts │ │ └── modal-bootstrap-dialogs.module.ts │ ├── read-app-config │ │ ├── read-app-config-test │ │ │ ├── read-app-config-test.component.css │ │ │ ├── read-app-config-test.component.html │ │ │ └── read-app-config-test.component.ts │ │ ├── read-app-config-routing.module.ts │ │ └── read-app-config.module.ts │ ├── detect-common-errors │ │ ├── detect-common-errors-test │ │ │ ├── detect-common-errors-test.component.css │ │ │ └── detect-common-errors-test.component.html │ │ ├── pony.ts │ │ ├── detect-common-errors-routing.module.ts │ │ └── detect-common-errors.module.ts │ ├── browser-storage-sample │ │ ├── browser-storage-sample-test │ │ │ ├── browser-storage-sample-test.component.css │ │ │ ├── browser-storage-sample-test.component.html │ │ │ └── browser-storage-sample-test.component.ts │ │ ├── browser-storage-sample-routing.module.ts │ │ └── browser-storage-sample.module.ts │ ├── model-state-validation │ │ ├── model-state-validation-test │ │ │ ├── model-state-validation-test.component.css │ │ │ ├── model-state-validation-test.component.ts │ │ │ └── model-state-validation-test.component.html │ │ ├── movie.ts │ │ ├── model-state-validation-routing.module.ts │ │ ├── model-state-validation.module.ts │ │ └── movie.service.ts │ ├── simple-grid │ │ ├── products-list │ │ │ └── products-list.component.css │ │ ├── paged-query-result.ts │ │ ├── app-product.ts │ │ ├── grid-column.ts │ │ ├── paged-query-model.ts │ │ ├── simple-grid-routing.module.ts │ │ ├── simple-grid.module.ts │ │ └── products-list.service.ts │ ├── app.component.ts │ └── app-routing.module.ts ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── typings.d.ts ├── tsconfig.app.json ├── index.html ├── main.ts ├── tsconfig.spec.json ├── styles.css ├── test.ts └── polyfills.ts ├── _2-dotnet_run.bat ├── _1-ng-build-dev.bat ├── _1-ng-build-prod.bat ├── _2-ng-serve-proxy.bat ├── _0-restore.bat ├── favicon.ico ├── _3_update_npm_packages.bat ├── proxy.config.json ├── Models ├── Ticket.cs ├── PagedQueryResult.cs ├── Product.cs ├── Category.cs ├── Employee.cs ├── ProductQueryViewModel.cs ├── Movie.cs ├── ProductDataSource.cs └── CategoriesDataSource.cs ├── e2e ├── tsconfig.e2e.json ├── app.po.ts └── app.e2e-spec.ts ├── Controllers ├── HomeController.cs ├── ShowImagesController.cs ├── UrlencodedController.cs ├── ReportsController.cs ├── MomentJalaaliController.cs ├── MoviesController.cs ├── TypeaheadController.cs ├── EmployeeController.cs └── SimpleUploadController.cs ├── .editorconfig ├── .github ├── lock.yml └── issue_template.md ├── .vscode ├── tasks.json └── launch.json ├── Program.cs ├── protractor.conf.js ├── Properties └── launchSettings.json ├── AngularTemplateDrivenFormsLab.csproj ├── AngularTemplateDrivenFormsLab.sln ├── karma.conf.js ├── tsconfig.json ├── Utils ├── AngularPushStateRoutingMiddleware.cs ├── AntiforgeryTokenMiddleware.cs ├── ContentSecurityPolicyMiddleware.cs └── IQueryableExtensions.cs ├── web.config ├── package.json └── Startup.cs /Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_2-dotnet_run.bat: -------------------------------------------------------------------------------- 1 | dotnet watch run -------------------------------------------------------------------------------- /_1-ng-build-dev.bat: -------------------------------------------------------------------------------- 1 | ng build --watch -------------------------------------------------------------------------------- /src/app/welcome/welcome.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_1-ng-build-prod.bat: -------------------------------------------------------------------------------- 1 | ng build --prod --watch -------------------------------------------------------------------------------- /src/app/seo/seo-tests/seo-tests.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/client-ip-address/get-ip/get-ip.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/core/bread-crumb/bread-crumb.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/display-pdf/view-pdf/view-pdf.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/page-not-found/page-not-found.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/angular-security/show-html/show-html.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1/parent1.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/product/product-group/product-group.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/confirm-modal/confirm-modal.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/timers/using-timers/using-timers.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/child/child.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/parent/parent.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/linq-like-methods/linq-tests/linq-tests.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /_2-ng-serve-proxy.bat: -------------------------------------------------------------------------------- 1 | ng serve --proxy-config proxy.config.json -o -------------------------------------------------------------------------------- /src/app/custom-validators/user-register/user-register.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/employee/employee-register/employee-register.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/service-component-communication/final/final.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/service-component-communication/first/first.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/service-component-communication/second/second.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/service-component-communication/third/third.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1-child1/parent1-child1.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/custom-pipe/moment-jalaali-test/moment-jalaali-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-simple/upload-file-simple.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/typed-sha/typed-sha.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/untyped-sha/untyped-sha.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/angular-http-client-blob/download-blobs/download-blobs.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/autocomplete/autocomplete-sample/autocomplete-sample.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/test-providers/test-providers.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/post-form-urlencoded/test-urlencoded/test-urlencoded.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/upload-file/ng2-file-upload-test/ng2-file-upload-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 2 | -------------------------------------------------------------------------------- /src/app/modal-bootstrap-dialogs/modal-dialog-test/modal-dialog-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/read-app-config/read-app-config-test/read-app-config-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/client-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "http://localhost:5000" 3 | } 4 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1-child1-child1/parent1-child1-child1.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/seo/seo-tests/seo-tests.component.html: -------------------------------------------------------------------------------- 1 |

Search Engine Optimizations

2 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-with-progress-bar/upload-file-with-progress-bar.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_0-restore.bat: -------------------------------------------------------------------------------- 1 | rmdir /S /Q bin 2 | rmdir /S /Q obj 3 | dotnet restore 4 | npm install 5 | pause -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VahidN/angular-template-driven-forms-lab/HEAD/favicon.ico -------------------------------------------------------------------------------- /src/app/detect-common-errors/detect-common-errors-test/detect-common-errors-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/detect-common-errors/pony.ts: -------------------------------------------------------------------------------- 1 | export interface PonyModel { 2 | name: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/page-not-found/page-not-found.component.html: -------------------------------------------------------------------------------- 1 |

2 | page-not-found works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/browser-storage-sample/browser-storage-sample-test/browser-storage-sample-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/model-state-validation/model-state-validation-test/model-state-validation-test.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VahidN/angular-template-driven-forms-lab/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/app/linq-like-methods/linq-tests/linq-tests.component.html: -------------------------------------------------------------------------------- 1 |

Using LINQ like methods in TypeScript

2 | -------------------------------------------------------------------------------- /src/app/linq-like-methods/person.ts: -------------------------------------------------------------------------------- 1 | export interface Person { 2 | name: string; 3 | age: number; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/simple-grid/products-list/products-list.component.css: -------------------------------------------------------------------------------- 1 | .checkbox label { 2 | padding-left: 35px; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/upload-file/ticket.ts: -------------------------------------------------------------------------------- 1 | export class Ticket { 2 | constructor(public description: string = "") {} 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/resume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VahidN/angular-template-driven-forms-lab/HEAD/src/assets/resume.png -------------------------------------------------------------------------------- /src/assets/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VahidN/angular-template-driven-forms-lab/HEAD/src/assets/sample.pdf -------------------------------------------------------------------------------- /_3_update_npm_packages.bat: -------------------------------------------------------------------------------- 1 | npm install npm-check-updates -g 2 | ncu 3 | ncu -a 4 | npm update 5 | rem ncu -u 6 | pause -------------------------------------------------------------------------------- /proxy.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api": { 3 | "target": "http://localhost:5000", 4 | "secure": false 5 | } 6 | } -------------------------------------------------------------------------------- /src/app/post-form-urlencoded/test-urlencoded/test-urlencoded.component.html: -------------------------------------------------------------------------------- 1 |

2 | test-urlencoded works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1-child1-child1/parent1-child1-child1.component.html: -------------------------------------------------------------------------------- 1 |

2 | parent1-child1-child1 works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/simple-grid/paged-query-result.ts: -------------------------------------------------------------------------------- 1 | export interface PagedQueryResult { 2 | totalItems: number; 3 | items: T[]; 4 | } 5 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1/parent1.component.html: -------------------------------------------------------------------------------- 1 |

parent1 works!

2 | Go to Parent1Child1 3 | -------------------------------------------------------------------------------- /src/app/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./browser-storage.service"; 2 | export * from "./app-config.service"; 3 | export * from "./seo-service"; 4 | -------------------------------------------------------------------------------- /src/app/core/bread-crumb/bread-crumb.ts: -------------------------------------------------------------------------------- 1 | export interface BreadCrumb { 2 | label: string; 3 | url?: string; 4 | glyphIcon?: string; 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/product/category.ts: -------------------------------------------------------------------------------- 1 | export class Category { 2 | constructor( 3 | public categoryId: number, 4 | public categoryName: string 5 | ) { } 6 | } 7 | -------------------------------------------------------------------------------- /src/app/product/product.ts: -------------------------------------------------------------------------------- 1 | export class Product { 2 | constructor( 3 | public productId: number, 4 | public productName: string 5 | ) { } 6 | } 7 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/typed-sha/typed-sha.component.html: -------------------------------------------------------------------------------- 1 |

SHA-512 Hash / Typed

2 | 3 |

String: This is a test

4 |

HEX: {{hash}}

5 | -------------------------------------------------------------------------------- /src/app/client-ip-address/get-ip/get-ip.component.html: -------------------------------------------------------------------------------- 1 |

Client's IP Address

2 |
3 |
{{ clientIp | json}}
4 |
5 | -------------------------------------------------------------------------------- /src/app/simple-grid/app-product.ts: -------------------------------------------------------------------------------- 1 | export interface AppProduct { 2 | productId: number; 3 | productName: string; 4 | price: number; 5 | isAvailable: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/simple-grid/grid-column.ts: -------------------------------------------------------------------------------- 1 | export interface GridColumn { 2 | title: string; 3 | propertyName: string; 4 | isSortable: boolean; 5 | isVisible: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/untyped-sha/untyped-sha.component.html: -------------------------------------------------------------------------------- 1 |

SHA-512 Hash / UnTyped

2 | 3 |

String: This is a test

4 |

HEX: {{hash}}

5 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1-child1/parent1-child1.component.html: -------------------------------------------------------------------------------- 1 |

parent1-child1 works!

2 | Go to Parent1Child1Child1 3 | -------------------------------------------------------------------------------- /src/app/product/product-group-form.ts: -------------------------------------------------------------------------------- 1 | export class ProductGroupForm { 2 | constructor( 3 | public categoryId?: number, 4 | public productId?: number 5 | ) { } 6 | } 7 | -------------------------------------------------------------------------------- /src/app/read-app-config/read-app-config-test/read-app-config-test.component.html: -------------------------------------------------------------------------------- 1 |

Read config @application startup

2 | 3 |
4 | host: {{host}} 5 |
6 | -------------------------------------------------------------------------------- /Models/Ticket.cs: -------------------------------------------------------------------------------- 1 | namespace AngularTemplateDrivenFormsLab.Models 2 | { 3 | public class Ticket 4 | { 5 | public int Id { set; get; } 6 | public string Description { set; get; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/app/detect-common-errors/detect-common-errors-test/detect-common-errors-test.component.html: -------------------------------------------------------------------------------- 1 |

2 | detect-common-errors-test works! 3 |

4 | 5 |

Hello {{ponyModel.name}} 6 | 7 |

{{ 12.3 | lowercase }}
8 | -------------------------------------------------------------------------------- /src/app/model-state-validation/movie.ts: -------------------------------------------------------------------------------- 1 | export class Movie { 2 | constructor( 3 | public title: string, 4 | public director: string, 5 | public ticketPrice: number, 6 | public releaseDate: string 7 | ) { } 8 | } 9 | -------------------------------------------------------------------------------- /src/app/simple-grid/paged-query-model.ts: -------------------------------------------------------------------------------- 1 | export interface PagedQueryModel { 2 | sortBy: string; 3 | isAscending: boolean; 4 | page: number; 5 | pageSize: number; 6 | filterByColumn: string; 7 | filterByValue: string; 8 | } 9 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "node" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/employee/employee.ts: -------------------------------------------------------------------------------- 1 | export class Employee { 2 | constructor( 3 | public firstName: string, 4 | public lastName: string, 5 | public isFullTime: boolean, 6 | public paymentType: string, 7 | public primaryLanguage: string 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /src/app/service-component-communication/second/second.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Second Component

4 |
5 |
6 |

{{message}}

7 |
8 |
9 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "baseUrl": "", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from "protractor"; 2 | 3 | export class AngularTemplateDrivenFormsLabPage { 4 | navigateTo() { 5 | return browser.get("/"); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css("app-root h1")).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/core/window.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | 3 | function getWindow(): any { 4 | return window; 5 | } 6 | 7 | @Injectable() 8 | export class WindowRefService { 9 | get nativeWindow(): any { 10 | return getWindow(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Models/PagedQueryResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Models 4 | { 5 | public class PagedQueryResult 6 | { 7 | public int TotalItems { get; set; } 8 | public IEnumerable Items { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public IActionResult Index() 8 | { 9 | return View(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/angular-http-client-blob/download-blobs/download-blobs.component.html: -------------------------------------------------------------------------------- 1 |

Angular HttpClient Blob

2 | 3 |

#sampleImage1

4 | 5 | 6 |

imageBlobUrl

7 | 8 | 9 |

sanitizedImageBlobUrl

10 | 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/app/service-component-communication/final/final.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 7 |
8 |
9 | 10 |
11 |
12 | -------------------------------------------------------------------------------- /Models/Product.cs: -------------------------------------------------------------------------------- 1 | namespace AngularTemplateDrivenFormsLab.Models 2 | { 3 | public class Product 4 | { 5 | public int ProductId { set; get; } 6 | public string ProductName { set; get; } 7 | public decimal Price { set; get; } 8 | public bool IsAvailable { set; get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/app/client-ip-address/ip.ts: -------------------------------------------------------------------------------- 1 | export interface IP { 2 | ip: string; 3 | country_code: string; 4 | country_name: string; 5 | region_code: string; 6 | region_name: string; 7 | city: string; 8 | zip_code: string; 9 | time_zone: string; 10 | latitude: number; 11 | longitude: number; 12 | metro_code: number; 13 | } 14 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | daysUntilLock: 90 2 | 3 | skipCreatedBefore: false 4 | 5 | exemptLabels: [] 6 | 7 | lockLabel: false 8 | 9 | lockComment: > 10 | This thread has been automatically locked since there has not been 11 | any recent activity after it was closed. Please open a new issue for 12 | related problems. 13 | 14 | setLockReason: true 15 | -------------------------------------------------------------------------------- /src/app/core/bread-crumb/bread-crumb.component.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /src/app/service-component-communication/third/third.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Third Component

4 |
5 |
6 |

{{message}}

7 | 8 |
9 |
10 | -------------------------------------------------------------------------------- /src/app/detect-common-errors/detect-common-errors-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forChild(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class DetectCommonErrorsRoutingModule { } 11 | -------------------------------------------------------------------------------- /src/app/welcome/welcome.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-welcome", 5 | templateUrl: "./welcome.component.html", 6 | styleUrls: ["./welcome.component.css"] 7 | }) 8 | export class WelcomeComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | # Summary of the issue 2 | 3 | 4 | 5 | ## Environment 6 | 7 | ``` 8 | The in-use version: 9 | Operating system: 10 | IDE: (e.g. Visual Studio 2015) 11 | ``` 12 | 13 | ## Example code/Steps to reproduce: 14 | 15 | ``` 16 | paste your core code 17 | ``` 18 | 19 | ## Output: 20 | 21 | ``` 22 | Exception message: 23 | Full Stack trace: 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /src/app/custom-pipe/moment-jalaali.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from "@angular/core"; 2 | 3 | import * as momentJalaali from "moment-jalaali"; 4 | 5 | @Pipe({ 6 | name: "momentJalaali" 7 | }) 8 | export class MomentJalaaliPipe implements PipeTransform { 9 | transform(value: any, args?: any): any { 10 | return momentJalaali(value).format(args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularTemplateDrivenFormsLab 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from "@angular/core"; 2 | import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; 3 | 4 | import { AppModule } from "./app/app.module"; 5 | import { environment } from "./environments/environment"; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1/parent1.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-parent1", 5 | templateUrl: "./parent1.component.html", 6 | styleUrls: ["./parent1.component.css"] 7 | }) 8 | export class Parent1Component implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/child/child.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Child Component

4 |
5 |
6 | {{amount}} 7 | 8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/thismodule.config.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from "@angular/core"; 2 | 3 | export let APP_CONFIG = new InjectionToken("this.module.config"); 4 | 5 | export interface IThisModuleConfig { 6 | apiEndpoint: string; 7 | } 8 | 9 | export const ThisModuleConfig: IThisModuleConfig = { 10 | apiEndpoint: "http://localhost:45043/api/" 11 | }; 12 | -------------------------------------------------------------------------------- /src/app/service-component-communication/final/final.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-final", 5 | templateUrl: "./final.component.html", 6 | styleUrls: ["./final.component.css"] 7 | }) 8 | export class FinalComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/custom-validators/user.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | constructor( 3 | public username: string = "", // required, must be 5-8 characters 4 | public email: string = "", // required, must be valid email format 5 | public password: string = "", // required, value must be equal to confirm password. 6 | public confirmPassword: string = "" // required, value must be equal to password. 7 | ) {} 8 | } 9 | -------------------------------------------------------------------------------- /src/app/page-not-found/page-not-found.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-page-not-found", 5 | templateUrl: "./page-not-found.component.html", 6 | styleUrls: ["./page-not-found.component.css"] 7 | }) 8 | export class PageNotFoundComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Models/Category.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace AngularTemplateDrivenFormsLab.Models 5 | { 6 | public class Category 7 | { 8 | public int CategoryId { set; get; } 9 | public string CategoryName { set; get; } 10 | 11 | [JsonIgnore] 12 | public IList Products { set; get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Controllers/ShowImagesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Controllers 4 | { 5 | [Route("api/[controller]")] 6 | public class ShowImagesController : Controller 7 | { 8 | [HttpGet("[action]")] 9 | public IActionResult GetImage() 10 | { 11 | return File("~/assets/resume.png", "image/png"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Controllers/UrlencodedController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Controllers 4 | { 5 | [Route("api/[controller]")] 6 | public class UrlencodedController : Controller 7 | { 8 | [HttpPost] 9 | public IActionResult Post(string refresh_token) 10 | { 11 | return Ok(new { refresh_token = refresh_token }); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1-child1/parent1-child1.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-parent1-child1", 5 | templateUrl: "./parent1-child1.component.html", 6 | styleUrls: ["./parent1-child1.component.css"] 7 | }) 8 | export class Parent1Child1Component implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "dotnet", 4 | "isShellCommand": true, 5 | "args": [], 6 | "tasks": [ 7 | { 8 | "taskName": "build", 9 | "args": [ 10 | "${workspaceRoot}/AngularTemplateDrivenFormsLab.csproj" 11 | ], 12 | "isBuildCommand": true, 13 | "problemMatcher": "$msCompile" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /Models/Employee.cs: -------------------------------------------------------------------------------- 1 | namespace AngularTemplateDrivenFormsLab.Models 2 | { 3 | public class Employee 4 | { 5 | public int Id { set; get; } 6 | public string FirstName { get; set; } 7 | public string LastName { get; set; } 8 | public bool IsFullTime { get; set; } 9 | public string PaymentType { get; set; } 10 | public string PrimaryLanguage { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/app/seo/seo.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { SeoRoutingModule } from "./seo-routing.module"; 5 | import { SeoTestsComponent } from "./seo-tests/seo-tests.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | SeoRoutingModule 11 | ], 12 | declarations: [SeoTestsComponent] 13 | }) 14 | export class SeoModule { } 15 | -------------------------------------------------------------------------------- /src/app/service-component-communication/first/first.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

First Component

4 |
5 |
6 |

{{editedMsg}}

7 | 8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "baseUrl": "", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts", 15 | "polyfills.ts" 16 | ], 17 | "include": [ 18 | "**/*.spec.ts", 19 | "**/*.d.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | import { SeoService } from "./core/seo-service"; 4 | 5 | @Component({ 6 | selector: "app-root", 7 | templateUrl: "./app.component.html", 8 | styleUrls: ["./app.component.css"] 9 | }) 10 | export class AppComponent { 11 | 12 | title = "T.D. Forms Lab"; 13 | 14 | constructor(private seoService: SeoService) { 15 | this.seoService.enableSeo(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/app/shared/confirm-modal/confirm-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-confirm-modal", 5 | templateUrl: "./confirm-modal.component.html", 6 | styleUrls: ["./confirm-modal.component.css"] 7 | }) 8 | export class ConfirmModalComponent { 9 | 10 | args: { 11 | title: string; 12 | message: string; 13 | } | null = null; 14 | 15 | close!: (val?: any) => void; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/app/timers/timers.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { TimersRoutingModule } from "./timers-routing.module"; 5 | import { UsingTimersComponent } from "./using-timers/using-timers.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | TimersRoutingModule 11 | ], 12 | declarations: [UsingTimersComponent] 13 | }) 14 | export class TimersModule { } 15 | -------------------------------------------------------------------------------- /e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AngularTemplateDrivenFormsLabPage } from "./app.po"; 2 | 3 | describe("angular-template-driven-forms-lab App", () => { 4 | let page: AngularTemplateDrivenFormsLabPage; 5 | 6 | beforeEach(() => { 7 | page = new AngularTemplateDrivenFormsLabPage(); 8 | }); 9 | 10 | it("should display welcome message", () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual("Welcome to app!!"); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/parent1-child1-child1/parent1-child1-child1.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-parent1-child1-child1", 5 | templateUrl: "./parent1-child1-child1.component.html", 6 | styleUrls: ["./parent1-child1-child1.component.css"] 7 | }) 8 | export class Parent1Child1Child1Component implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/app/display-pdf/display-pdf-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ViewPdfComponent } from "./view-pdf/view-pdf.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "viewPdf", component: ViewPdfComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class DisplayPdfRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/service-component-communication/sample.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { BehaviorSubject } from "rxjs"; 3 | 4 | @Injectable() 5 | export class SampleService { 6 | 7 | private msgSource = new BehaviorSubject("default service value"); 8 | 9 | telecast$ = this.msgSource.asObservable(); 10 | 11 | constructor() { } 12 | 13 | editMsg(newMsg: string) { 14 | this.msgSource.next(newMsg); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/parent/parent.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-parent", 5 | templateUrl: "./parent.component.html", 6 | styleUrls: ["./parent.component.css"] 7 | }) 8 | export class ParentComponent implements OnInit { 9 | 10 | amount = 500; 11 | 12 | constructor() { } 13 | 14 | ngOnInit() { 15 | } 16 | 17 | deposit() { 18 | this.amount += 100; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/timers/timers-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { UsingTimersComponent } from "./using-timers/using-timers.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "usingTimers", component: UsingTimersComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class TimersRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/display-pdf/download-pdf-data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { Observable } from "rxjs"; 3 | import { HttpClient } from "@angular/common/http"; 4 | 5 | @Injectable() 6 | export class DownloadPdfDataService { 7 | 8 | constructor(private httpClient: HttpClient) { } 9 | 10 | public getReport(): Observable { 11 | return this.httpClient.get("/api/Reports/GetPdfReport", { responseType: "blob" }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/angular-http-client-blob/download-binary-data.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable } from "rxjs"; 4 | 5 | @Injectable() 6 | export class DownloadBinaryDataService { 7 | 8 | constructor(private httpClient: HttpClient) { } 9 | 10 | public getImage(): Observable { 11 | return this.httpClient.get("/api/ShowImages/GetImage", { responseType: "blob" }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/app/client-ip-address/client-ip-address-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | import { GetIpComponent } from "./get-ip/get-ip.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "getIp", component: GetIpComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class ClientIpAddressRoutingModule { } 15 | -------------------------------------------------------------------------------- /src/app/product/product-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ProductGroupComponent } from "./product-group/product-group.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "productCascadingList", component: ProductGroupComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class ProductRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/simple-grid/simple-grid-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ProductsListComponent } from "./products-list/products-list.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "simpleGrid", component: ProductsListComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class SimpleGridRoutingModule {} 14 | -------------------------------------------------------------------------------- /src/app/angular-security/angular-security-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | import { ShowHtmlComponent } from "./show-html/show-html.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "showHtml", component: ShowHtmlComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class AngularSecurityRoutingModule { } 15 | -------------------------------------------------------------------------------- /src/app/employee/employee-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | import { EmployeeRegisterComponent } from "./employee-register/employee-register.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "register", component: EmployeeRegisterComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class EmployeeRoutingModule { } 15 | -------------------------------------------------------------------------------- /src/app/linq-like-methods/linq-like-methods-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | import { LinqTestsComponent } from "./linq-tests/linq-tests.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "linqTests", component: LinqTestsComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class LinqLikeMethodsRoutingModule { } 15 | -------------------------------------------------------------------------------- /src/app/custom-validators/custom-validators-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { UserRegisterComponent } from "./user-register/user-register.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "confirmPassword", component: UserRegisterComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class CustomValidatorsRoutingModule {} 14 | -------------------------------------------------------------------------------- /src/app/linq-like-methods/linq-like-methods.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { LinqLikeMethodsRoutingModule } from "./linq-like-methods-routing.module"; 5 | import { LinqTestsComponent } from "./linq-tests/linq-tests.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | LinqLikeMethodsRoutingModule 11 | ], 12 | declarations: [LinqTestsComponent] 13 | }) 14 | export class LinqLikeMethodsModule { } 15 | -------------------------------------------------------------------------------- /src/app/custom-pipe/custom-pipe-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | 4 | import { MomentJalaaliTestComponent } from "./moment-jalaali-test/moment-jalaali-test.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "momentJalaali", component: MomentJalaaliTestComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class CustomPipeRoutingModule {} 15 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/custom-two-way-data-binding-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ParentComponent } from "./parent/parent.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "customTwoWayDataBinding", component: ParentComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class CustomTwoWayDataBindingRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/service-component-communication/service-component-communication-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { FinalComponent } from "./final/final.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "finalComponent", component: FinalComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class ServiceComponentCommunicationRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/autocomplete/autocomplete-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | import { AutocompleteSampleComponent } from "./autocomplete-sample/autocomplete-sample.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "autocomplete", component: AutocompleteSampleComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class AutocompleteRoutingModule { } 15 | -------------------------------------------------------------------------------- /src/app/read-app-config/read-app-config-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ReadAppConfigTestComponent } from "./read-app-config-test/read-app-config-test.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "readAppConfig", component: ReadAppConfigTestComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class ReadAppConfigRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/angular-http-client-blob/angular-http-client-blob-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { DownloadBlobsComponent } from "./download-blobs/download-blobs.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "downloadBlobs", component: DownloadBlobsComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class AngularHttpClientBlobRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/post-form-urlencoded/post-form-urlencoded-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { RouterModule, Routes } from "@angular/router"; 3 | 4 | import { TestUrlencodedComponent } from "./test-urlencoded/test-urlencoded.component"; 5 | 6 | const routes: Routes = [ 7 | { path: "postFormUrlencoded", component: TestUrlencodedComponent } 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [RouterModule.forChild(routes)], 12 | exports: [RouterModule] 13 | }) 14 | export class PostFormUrlencodedRoutingModule { } 15 | -------------------------------------------------------------------------------- /src/app/read-app-config/read-app-config.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { ReadAppConfigRoutingModule } from "./read-app-config-routing.module"; 5 | import { ReadAppConfigTestComponent } from "./read-app-config-test/read-app-config-test.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | ReadAppConfigRoutingModule 11 | ], 12 | declarations: [ReadAppConfigTestComponent] 13 | }) 14 | export class ReadAppConfigModule { } 15 | -------------------------------------------------------------------------------- /Controllers/ReportsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Controllers 4 | { 5 | [Route("api/[controller]")] 6 | public class ReportsController : Controller 7 | { 8 | [HttpGet("[action]")] 9 | public IActionResult GetPdfReport() 10 | { 11 | return File(virtualPath: "~/assets/sample.pdf", 12 | contentType: "application/pdf", 13 | fileDownloadName: "sample.pdf"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/injection-beyond-classes-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { TestProvidersComponent } from "./test-providers/test-providers.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "injectionBeyondClasses", component: TestProvidersComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class InjectionBeyondClassesRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/modal-bootstrap-dialogs/modal-bootstrap-dialogs-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ModalDialogTestComponent } from "./modal-dialog-test/modal-dialog-test.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "modalDialogTest", component: ModalDialogTestComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class ModalBootstrapDialogsRoutingModule { } 14 | -------------------------------------------------------------------------------- /Models/ProductQueryViewModel.cs: -------------------------------------------------------------------------------- 1 | using AngularTemplateDrivenFormsLab.Utils; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Models 4 | { 5 | public class ProductQueryViewModel : IPagedQueryModel 6 | { 7 | // ... other properties ... 8 | 9 | public string SortBy { get; set; } 10 | public bool IsAscending { get; set; } 11 | public int Page { get; set; } 12 | public int PageSize { get; set; } 13 | public string FilterByColumn { get; set; } 14 | public string FilterByValue { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/angular-security/angular-security.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | 4 | import { AngularSecurityRoutingModule } from "./angular-security-routing.module"; 5 | import { SafePipe } from "./show-html/safe.pipe"; 6 | import { ShowHtmlComponent } from "./show-html/show-html.component"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | AngularSecurityRoutingModule 12 | ], 13 | declarations: [ShowHtmlComponent, SafePipe] 14 | }) 15 | export class AngularSecurityModule { } 16 | -------------------------------------------------------------------------------- /src/app/shared/confirm-modal/confirm-modal.component.html: -------------------------------------------------------------------------------- 1 | 7 | 10 | 14 | -------------------------------------------------------------------------------- /src/app/client-ip-address/client-ip-address.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | 4 | import { ClientIpAddressRoutingModule } from "./client-ip-address-routing.module"; 5 | import { GetIpComponent } from "./get-ip/get-ip.component"; 6 | import { JobService } from "./job.service"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | ClientIpAddressRoutingModule 12 | ], 13 | declarations: [GetIpComponent], 14 | providers: [JobService] 15 | }) 16 | export class ClientIpAddressModule { } 17 | -------------------------------------------------------------------------------- /src/app/angular-security/show-html/show-html.component.html: -------------------------------------------------------------------------------- 1 |

Binding innerHTML

2 | 3 |

Bound value:

4 |

{{htmlContent}}

5 | 6 |

Result of binding to innerHTML:

7 |

8 | 9 |

Result of binding to dataContainer:

10 |

11 | 12 |

Result of bypassSecurityTrustHtml:

13 |

14 | 15 |

Result of binding to sanitizedHtml:

16 |

17 | 18 |

Result of using the safe pipe:

19 |

20 | -------------------------------------------------------------------------------- /src/app/browser-storage-sample/browser-storage-sample-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserStorageSampleTestComponent } from "./browser-storage-sample-test/browser-storage-sample-test.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "showBrowserStorageSample", component: BrowserStorageSampleTestComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class BrowserStorageSampleRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/custom-pipe/moment-jalaali-test/moment-jalaali-test.component.html: -------------------------------------------------------------------------------- 1 |

Using moment-jalaali

2 |
3 | اكنون: 4 | {{now}} 5 |
6 | {{nowLongDateFormat}} 7 |
8 | {{nowExtraLongDateFormat}} 9 |
10 | 11 |

Server side dates:

12 |
13 | {{date | momentJalaali:'jYYYY/jMM/jDD hh:mm' }}, 14 | {{date | momentJalaali:'jD jMMMM jYYYY [ساعت] LT'}} 15 |
16 | -------------------------------------------------------------------------------- /src/app/detect-common-errors/detect-common-errors.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | 4 | import { DetectCommonErrorsRoutingModule } from "./detect-common-errors-routing.module"; 5 | import { DetectCommonErrorsTestComponent } from "./detect-common-errors-test/detect-common-errors-test.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | DetectCommonErrorsRoutingModule 11 | ], 12 | declarations: [DetectCommonErrorsTestComponent] 13 | }) 14 | export class DetectCommonErrorsModule { } 15 | -------------------------------------------------------------------------------- /src/app/model-state-validation/model-state-validation-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ModelStateValidationTestComponent } from "./model-state-validation-test/model-state-validation-test.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { path: "modelStateValidationTest", component: ModelStateValidationTestComponent } 7 | ]; 8 | 9 | @NgModule({ 10 | imports: [RouterModule.forChild(routes)], 11 | exports: [RouterModule] 12 | }) 13 | export class ModelStateValidationRoutingModule { } 14 | -------------------------------------------------------------------------------- /src/app/display-pdf/display-pdf.module.ts: -------------------------------------------------------------------------------- 1 | import { DownloadPdfDataService } from "./download-pdf-data.service"; 2 | import { NgModule } from "@angular/core"; 3 | import { CommonModule } from "@angular/common"; 4 | 5 | import { DisplayPdfRoutingModule } from "./display-pdf-routing.module"; 6 | import { ViewPdfComponent } from "./view-pdf/view-pdf.component"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | DisplayPdfRoutingModule 12 | ], 13 | declarations: [ViewPdfComponent], 14 | providers: [DownloadPdfDataService] 15 | }) 16 | export class DisplayPdfModule { } 17 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/child/child.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; 2 | 3 | @Component({ 4 | selector: "app-child", 5 | templateUrl: "./child.component.html", 6 | styleUrls: ["./child.component.css"] 7 | }) 8 | export class ChildComponent implements OnInit { 9 | 10 | @Input() amount = 0; 11 | @Output() amountChange = new EventEmitter(); 12 | 13 | constructor() { } 14 | 15 | ngOnInit() { 16 | } 17 | 18 | withdraw() { 19 | this.amount -= 100; 20 | this.amountChange.emit(this.amount); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/browser-storage-sample/browser-storage-sample.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { BrowserStorageSampleRoutingModule } from "./browser-storage-sample-routing.module"; 5 | import { BrowserStorageSampleTestComponent } from "./browser-storage-sample-test/browser-storage-sample-test.component"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | BrowserStorageSampleRoutingModule 11 | ], 12 | declarations: [BrowserStorageSampleTestComponent] 13 | }) 14 | export class BrowserStorageSampleModule { } 15 | -------------------------------------------------------------------------------- /src/app/autocomplete/autocomplete.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | 4 | import { AutocompleteRoutingModule } from "./autocomplete-routing.module"; 5 | import { AutocompleteSampleComponent } from "./autocomplete-sample/autocomplete-sample.component"; 6 | import { SearchService } from "./search.service"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | AutocompleteRoutingModule 12 | ], 13 | declarations: [AutocompleteSampleComponent], 14 | providers: [SearchService] 15 | }) 16 | export class AutocompleteModule { } 17 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/typed-sha/typed-sha.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import * as jsSHA from "jssha"; 3 | 4 | @Component({ 5 | selector: "app-typed-sha", 6 | templateUrl: "./typed-sha.component.html", 7 | styleUrls: ["./typed-sha.component.css"] 8 | }) 9 | export class TypedShaComponent implements OnInit { 10 | hash: string | null = null; 11 | 12 | constructor() { } 13 | 14 | ngOnInit() { 15 | const shaObj = new jsSHA("SHA-512", "TEXT"); 16 | shaObj.update("This is a test"); 17 | this.hash = shaObj.getHash("HEX"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/untyped-sha/untyped-sha.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | declare var jsSHA: any; 4 | 5 | @Component({ 6 | selector: "app-untyped-sha", 7 | templateUrl: "./untyped-sha.component.html", 8 | styleUrls: ["./untyped-sha.component.css"] 9 | }) 10 | export class UntypedShaComponent implements OnInit { 11 | hash: String | null = null; 12 | 13 | constructor() { } 14 | 15 | ngOnInit() { 16 | const shaObj = new jsSHA("SHA-512", "TEXT"); 17 | shaObj.update("This is a test"); 18 | this.hash = shaObj.getHash("HEX"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/custom-two-way-data-binding.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { CustomTwoWayDataBindingRoutingModule } from "./custom-two-way-data-binding-routing.module"; 5 | import { ParentComponent } from "./parent/parent.component"; 6 | import { ChildComponent } from "./child/child.component"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | CustomTwoWayDataBindingRoutingModule 12 | ], 13 | declarations: [ParentComponent, ChildComponent] 14 | }) 15 | export class CustomTwoWayDataBindingModule { } 16 | -------------------------------------------------------------------------------- /src/app/custom-two-way-data-binding/parent/parent.component.html: -------------------------------------------------------------------------------- 1 |

Custom two way data binding

2 | 3 |
4 |
5 |

Parnet Component

6 |
7 |
8 | {{amount}} 9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/using-third-party-libraries.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { UsingThirdPartyLibrariesRoutingModule } from "./using-third-party-libraries-routing.module"; 5 | import { TypedShaComponent } from "./typed-sha/typed-sha.component"; 6 | import { UntypedShaComponent } from "./untyped-sha/untyped-sha.component"; 7 | 8 | @NgModule({ 9 | imports: [CommonModule, UsingThirdPartyLibrariesRoutingModule], 10 | declarations: [TypedShaComponent, UntypedShaComponent] 11 | }) 12 | export class UsingThirdPartyLibrariesModule {} 13 | -------------------------------------------------------------------------------- /src/app/modal-bootstrap-dialogs/modal-bootstrap-dialogs.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | import { FormsModule } from "@angular/forms"; 4 | 5 | import { ModalBootstrapDialogsRoutingModule } from "./modal-bootstrap-dialogs-routing.module"; 6 | import { ModalDialogTestComponent } from "./modal-dialog-test/modal-dialog-test.component"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | FormsModule, 12 | ModalBootstrapDialogsRoutingModule 13 | ], 14 | declarations: [ModalDialogTestComponent] 15 | }) 16 | export class ModalBootstrapDialogsModule { } 17 | -------------------------------------------------------------------------------- /Controllers/MomentJalaaliController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace AngularTemplateDrivenFormsLab.Controllers 6 | { 7 | [Route("api/[controller]")] 8 | public class MomentJalaaliController : Controller 9 | { 10 | [HttpGet("[action]")] 11 | public IActionResult GetDates() 12 | { 13 | var dates = new List(); 14 | for (var i = 0; i < 20; i++) 15 | { 16 | dates.Add(DateTime.Now.AddDays(-i)); 17 | } 18 | return Json(dates); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | 4 | import { PageNotFoundComponent } from "./page-not-found/page-not-found.component"; 5 | import { WelcomeComponent } from "./welcome/welcome.component"; 6 | 7 | const routes: Routes = [ 8 | { path: "welcome", component: WelcomeComponent }, 9 | { path: "", redirectTo: "welcome", pathMatch: "full" }, 10 | { path: "**", component: PageNotFoundComponent } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [RouterModule.forRoot(routes)], 15 | exports: [RouterModule] 16 | }) 17 | export class AppRoutingModule {} 18 | -------------------------------------------------------------------------------- /src/app/post-form-urlencoded/post-form-urlencoded.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | 4 | import { PostFormUrlencodedRoutingModule } from "./post-form-urlencoded-routing.module"; 5 | import { TestUrlencodedComponent } from "./test-urlencoded/test-urlencoded.component"; 6 | import { UrlencodedService } from "./urlencoded.service"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | PostFormUrlencodedRoutingModule 12 | ], 13 | declarations: [TestUrlencodedComponent], 14 | providers: [UrlencodedService] 15 | }) 16 | export class PostFormUrlencodedModule { } 17 | -------------------------------------------------------------------------------- /src/app/product/product.module.ts: -------------------------------------------------------------------------------- 1 | import { FormsModule } from "@angular/forms"; 2 | import { NgModule } from "@angular/core"; 3 | import { CommonModule } from "@angular/common"; 4 | 5 | import { ProductRoutingModule } from "./product-routing.module"; 6 | import { ProductGroupComponent } from "./product-group/product-group.component"; 7 | import { ProductItemsService } from "./product-items.service"; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | ProductRoutingModule, 13 | FormsModule 14 | ], 15 | declarations: [ProductGroupComponent], 16 | providers: [ProductItemsService] 17 | }) 18 | export class ProductModule { } 19 | -------------------------------------------------------------------------------- /src/app/shared/shared.bootstrap.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | import { BsDropdownModule } from "ngx-bootstrap/dropdown"; 4 | import { TooltipModule } from "ngx-bootstrap/tooltip"; 5 | import { ModalModule } from "ngx-bootstrap/modal"; 6 | 7 | @NgModule({ 8 | imports: [ 9 | CommonModule, 10 | BsDropdownModule.forRoot(), 11 | TooltipModule.forRoot(), 12 | ModalModule.forRoot() 13 | ], 14 | exports: [ 15 | BsDropdownModule, 16 | TooltipModule, 17 | ModalModule 18 | ] 19 | }) 20 | export class SharedBootstrapModule { } 21 | -------------------------------------------------------------------------------- /src/app/custom-pipe/custom-pipe.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { CustomPipeRoutingModule } from "./custom-pipe-routing.module"; 5 | import { MomentJalaaliTestComponent } from "./moment-jalaali-test/moment-jalaali-test.component"; 6 | import { MomentJalaaliPipe } from "./moment-jalaali.pipe"; 7 | import { JsonDatesService } from "./json-dates.service"; 8 | 9 | @NgModule({ 10 | imports: [CommonModule, CustomPipeRoutingModule], 11 | declarations: [MomentJalaaliTestComponent, MomentJalaaliPipe], 12 | providers: [JsonDatesService] 13 | }) 14 | export class CustomPipeModule {} 15 | -------------------------------------------------------------------------------- /src/app/employee/employee.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | import { FormsModule } from "@angular/forms"; 4 | 5 | import { EmployeeRegisterComponent } from "./employee-register/employee-register.component"; 6 | import { EmployeeRoutingModule } from "./employee-routing.module"; 7 | import { FormPosterService } from "./form-poster.service"; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | EmployeeRoutingModule, 13 | FormsModule 14 | ], 15 | declarations: [EmployeeRegisterComponent], 16 | providers: [FormPosterService] 17 | }) 18 | export class EmployeeModule { } 19 | -------------------------------------------------------------------------------- /src/app/read-app-config/read-app-config-test/read-app-config-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | import { AppConfigService } from "./../../core/app-config.service"; 4 | 5 | @Component({ 6 | selector: "app-read-app-config-test", 7 | templateUrl: "./read-app-config-test.component.html", 8 | styleUrls: ["./read-app-config-test.component.css"] 9 | }) 10 | export class ReadAppConfigTestComponent implements OnInit { 11 | 12 | host: string | null = null; 13 | 14 | constructor(private appConfig: AppConfigService) { } 15 | 16 | ngOnInit() { 17 | this.host = this.appConfig.configuration.host; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/app/using-third-party-libraries/using-third-party-libraries-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { Routes, RouterModule } from "@angular/router"; 3 | 4 | import { UntypedShaComponent } from "./untyped-sha/untyped-sha.component"; 5 | import { TypedShaComponent } from "./typed-sha/typed-sha.component"; 6 | 7 | const routes: Routes = [ 8 | { path: "untypedSha", component: UntypedShaComponent }, 9 | { path: "typedSha", component: TypedShaComponent } 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [RouterModule.forChild(routes)], 14 | exports: [RouterModule] 15 | }) 16 | export class UsingThirdPartyLibrariesRoutingModule {} 17 | -------------------------------------------------------------------------------- /src/app/client-ip-address/get-ip/get-ip.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | import { IP } from "./../ip"; 4 | import { JobService } from "./../job.service"; 5 | 6 | @Component({ 7 | selector: "app-get-ip", 8 | templateUrl: "./get-ip.component.html", 9 | styleUrls: ["./get-ip.component.css"] 10 | }) 11 | export class GetIpComponent implements OnInit { 12 | 13 | clientIp: IP | null = null; 14 | 15 | constructor(private jobService: JobService) { } 16 | 17 | ngOnInit() { 18 | this.jobService.getIpAddress().subscribe(data => { 19 | this.clientIp = data; 20 | console.log(data); 21 | }); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/custom-validators/user-register/user-register.component.ts: -------------------------------------------------------------------------------- 1 | import { NgForm } from "@angular/forms"; 2 | import { User } from "./../user"; 3 | import { Component, OnInit } from "@angular/core"; 4 | 5 | @Component({ 6 | selector: "app-user-register", 7 | templateUrl: "./user-register.component.html", 8 | styleUrls: ["./user-register.component.css"] 9 | }) 10 | export class UserRegisterComponent implements OnInit { 11 | remoteUsernameValidationUrl = "api/Employee/CheckUser"; 12 | model = new User(); 13 | 14 | constructor() {} 15 | 16 | ngOnInit() {} 17 | 18 | submitForm(form: NgForm) { 19 | console.log(this.model); 20 | console.log(form.value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/autocomplete/search.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | @Injectable() 7 | export class SearchService { 8 | 9 | constructor(private http: HttpClient) { } 10 | 11 | searchCountries(term: string): Observable { 12 | return this.http 13 | .get(`/api/Typeahead/SearchCountries?term=${encodeURIComponent(term)}`) 14 | .pipe( 15 | map(response => response || {}), 16 | catchError((error: HttpErrorResponse) => throwError(error)) 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Controllers/MoviesController.cs: -------------------------------------------------------------------------------- 1 | using AngularTemplateDrivenFormsLab.Models; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace AngularTemplateDrivenFormsLab.Controllers 5 | { 6 | [Route("api/[controller]")] 7 | public class MoviesController : Controller 8 | { 9 | [HttpPost] 10 | public IActionResult Post([FromBody]Movie movie) 11 | { 12 | if (ModelState.IsValid) 13 | { 14 | // TODO: save ... 15 | return Ok(movie); 16 | } 17 | 18 | ModelState.AddModelError("", "This record already exists."); // a cross field validation 19 | return BadRequest(ModelState); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/angular-http-client-blob/angular-http-client-blob.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { AngularHttpClientBlobRoutingModule } from "./angular-http-client-blob-routing.module"; 5 | import { DownloadBlobsComponent } from "./download-blobs/download-blobs.component"; 6 | import { DownloadBinaryDataService } from "app/angular-http-client-blob/download-binary-data.service"; 7 | 8 | @NgModule({ 9 | imports: [ 10 | CommonModule, 11 | AngularHttpClientBlobRoutingModule 12 | ], 13 | declarations: [DownloadBlobsComponent], 14 | providers: [DownloadBinaryDataService] 15 | }) 16 | export class AngularHttpClientBlobModule { } 17 | -------------------------------------------------------------------------------- /src/app/post-form-urlencoded/test-urlencoded/test-urlencoded.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | 3 | import { UrlencodedService } from "./../urlencoded.service"; 4 | 5 | @Component({ 6 | selector: "app-test-urlencoded", 7 | templateUrl: "./test-urlencoded.component.html", 8 | styleUrls: ["./test-urlencoded.component.css"] 9 | }) 10 | export class TestUrlencodedComponent implements OnInit { 11 | 12 | constructor(private urlencodedService: UrlencodedService) { } 13 | 14 | ngOnInit() { 15 | this.urlencodedService.doRefreshToken("aaa-bbbb-cccc rrr") 16 | .subscribe(result => { 17 | console.log("RefreshToken Result", result); 18 | }); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/seo/seo-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { SeoTestsComponent } from "./seo-tests/seo-tests.component"; 2 | import { NgModule } from "@angular/core"; 3 | import { Routes, RouterModule } from "@angular/router"; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: "seo", component: SeoTestsComponent, 8 | data: { 9 | title: "SeoTestsComponent Title", 10 | metaTags: { 11 | description: "SeoTestsComponent Description or some content here", 12 | keywords: "seo, some, keywords, here, separated, by, a comma" 13 | } 14 | } 15 | } 16 | ]; 17 | 18 | @NgModule({ 19 | imports: [RouterModule.forChild(routes)], 20 | exports: [RouterModule] 21 | }) 22 | export class SeoRoutingModule { } 23 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/bread-crumb-sample.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { BreadCrumbSampleRoutingModule } from "./bread-crumb-sample-routing.module"; 5 | import { Parent1Component } from "./parent1/parent1.component"; 6 | import { Parent1Child1Component } from "./parent1-child1/parent1-child1.component"; 7 | import { Parent1Child1Child1Component } from "./parent1-child1-child1/parent1-child1-child1.component"; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | BreadCrumbSampleRoutingModule 13 | ], 14 | declarations: [Parent1Component, Parent1Child1Component, Parent1Child1Child1Component] 15 | }) 16 | export class BreadCrumbSampleModule { } 17 | -------------------------------------------------------------------------------- /src/app/model-state-validation/model-state-validation.module.ts: -------------------------------------------------------------------------------- 1 | import { MovieService } from "./movie.service"; 2 | import { FormsModule } from "@angular/forms"; 3 | import { NgModule } from "@angular/core"; 4 | import { CommonModule } from "@angular/common"; 5 | 6 | import { ModelStateValidationRoutingModule } from "./model-state-validation-routing.module"; 7 | import { ModelStateValidationTestComponent } from "./model-state-validation-test/model-state-validation-test.component"; 8 | 9 | @NgModule({ 10 | imports: [ 11 | CommonModule, 12 | FormsModule, 13 | ModelStateValidationRoutingModule 14 | ], 15 | declarations: [ModelStateValidationTestComponent], 16 | providers: [MovieService] 17 | }) 18 | export class ModelStateValidationModule { } 19 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/car.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpHeaders } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | @Injectable() 7 | export class CarService { 8 | 9 | constructor(private http: HttpClient) { } 10 | 11 | postData(): Observable { 12 | const headers = new HttpHeaders({ "Content-Type": "application/json" }); 13 | return this.http.post("/api/values", { data: "123" }, { headers: headers }) 14 | .pipe( 15 | map((response: any) => response["fields"] || {}), 16 | catchError(error => observableThrowError(error))); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/app/core/modal.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { BsModalService } from "ngx-bootstrap"; 3 | 4 | @Injectable() 5 | export class ModalService { 6 | 7 | constructor(private bsModalService: BsModalService) { } 8 | 9 | show(component: any, args?: any, options?: any): Promise { 10 | return new Promise(resolve => { 11 | options = options || {}; 12 | const modal = this.bsModalService.show(component, options); 13 | let result: any; 14 | const sub = this.bsModalService.onHidden.subscribe(() => { 15 | sub.unsubscribe(); 16 | resolve(result); 17 | }); 18 | modal.content.args = args; 19 | modal.content.close = (val?: any) => { 20 | result = val; 21 | modal.hide(); 22 | }; 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/display-pdf/view-pdf/view-pdf.component.html: -------------------------------------------------------------------------------- 1 |

Display PDF Files

2 | 3 |
4 | 5 | 6 | 7 | 8 |

using iframe

9 | 10 |

using object

11 | 13 |

usin embed

14 | 16 |
17 | -------------------------------------------------------------------------------- /Models/Movie.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace AngularTemplateDrivenFormsLab.Models 5 | { 6 | public class Movie 7 | { 8 | public int Id { get; set; } 9 | 10 | [Required(ErrorMessage = "Movie Title is Required")] 11 | [MinLength(3, ErrorMessage = "Movie Title must be at least 3 characters")] 12 | public string Title { get; set; } 13 | 14 | [Required(ErrorMessage = "Movie Director is Required.")] 15 | public string Director { get; set; } 16 | 17 | [Range(0, 100, ErrorMessage = "Ticket price must be between 0 and 100.")] 18 | public decimal TicketPrice { get; set; } 19 | 20 | [Required(ErrorMessage = "Movie Release Date is required")] 21 | public DateTime ReleaseDate { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app/custom-validators/email-validator.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from "@angular/core"; 2 | import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; 3 | 4 | @Directive({ 5 | selector: 6 | "[appEmailValidator][formControlName],[appEmailValidator][formControl],[appEmailValidator][ngModel]", 7 | providers: [ 8 | { 9 | provide: NG_VALIDATORS, 10 | useExisting: EmailValidatorDirective, 11 | multi: true 12 | } 13 | ] 14 | }) 15 | export class EmailValidatorDirective implements Validator { 16 | validate(element: AbstractControl): ValidationErrors | null { 17 | const emailRegex = /\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/; 18 | const valid = emailRegex.test(element.value); 19 | return valid ? null : { appEmailValidator: true }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AngularTemplateDrivenFormsLab 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } -------------------------------------------------------------------------------- /src/app/custom-pipe/json-dates.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | 7 | @Injectable() 8 | export class JsonDatesService { 9 | private baseUrl = "api/MomentJalaali"; 10 | 11 | constructor(private http: HttpClient) { } 12 | 13 | private handleError(error: Response): Observable { 14 | console.error("observable error: ", error); 15 | return observableThrowError(error); 16 | } 17 | 18 | getDates(): Observable { 19 | return this.http 20 | .get(`${this.baseUrl}/GetDates`) 21 | .pipe( 22 | map(response => response || {}), 23 | catchError(this.handleError)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/app/simple-grid/simple-grid.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | import { FormsModule } from "@angular/forms"; 4 | import { SharedModule } from "@app/shared/shared.module"; 5 | import { PaginationModule } from "ngx-bootstrap"; 6 | 7 | import { ProductsListService } from "./products-list.service"; 8 | import { ProductsListComponent } from "./products-list/products-list.component"; 9 | import { SimpleGridRoutingModule } from "./simple-grid-routing.module"; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | FormsModule, 15 | SharedModule, 16 | PaginationModule.forRoot(), 17 | SimpleGridRoutingModule 18 | ], 19 | declarations: [ProductsListComponent], 20 | providers: [ProductsListService] 21 | }) 22 | export class SimpleGridModule { } 23 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/app/client-ip-address/job.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | import { IP } from "./ip"; 7 | 8 | 9 | @Injectable() 10 | export class JobService { 11 | 12 | constructor(private http: HttpClient) { } 13 | 14 | getIpAddress(): Observable { 15 | return this.http 16 | .get("https://freegeoip.net/json/?callback").pipe( 17 | map(response => response || {}), 18 | catchError(this.handleError) 19 | ); 20 | } 21 | 22 | private handleError(error: HttpErrorResponse): Observable { 23 | console.error("observable error: ", error); 24 | return observableThrowError(error); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/browser-storage-sample/browser-storage-sample-test/browser-storage-sample-test.component.html: -------------------------------------------------------------------------------- 1 |

Browser storage sample

2 |
3 | 4 | 5 |
6 | {{fromSessionStorage}} 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 | {{fromLocalStorage}} 15 |
16 |
17 | -------------------------------------------------------------------------------- /src/app/custom-validators/custom-validators.module.ts: -------------------------------------------------------------------------------- 1 | import { FormsModule } from "@angular/forms"; 2 | import { NgModule } from "@angular/core"; 3 | import { CommonModule } from "@angular/common"; 4 | 5 | import { CustomValidatorsRoutingModule } from "./custom-validators-routing.module"; 6 | import { UserRegisterComponent } from "./user-register/user-register.component"; 7 | import { EqualValidatorDirective } from "./equal-validator.directive"; 8 | import { EmailValidatorDirective } from "./email-validator.directive"; 9 | import { RemoteValidatorDirective } from "./remote-validator.directive"; 10 | 11 | @NgModule({ 12 | imports: [CommonModule, FormsModule, CustomValidatorsRoutingModule], 13 | declarations: [ 14 | UserRegisterComponent, 15 | EqualValidatorDirective, 16 | EmailValidatorDirective, 17 | RemoteValidatorDirective 18 | ] 19 | }) 20 | export class CustomValidatorsModule {} 21 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | /* 4 | .ng-touched.ng-invalid{ 5 |     border: 1px solid red; 6 | } 7 | */ 8 | 9 | /* Spinner */ 10 | .spinner { 11 | font-size:15px; 12 | z-index:10 13 | } 14 | 15 | .glyphicon-spin { 16 | -webkit-animation: spin 1000ms infinite linear; 17 | animation: spin 1000ms infinite linear; 18 | } 19 | @-webkit-keyframes spin { 20 | 0% { 21 | -webkit-transform: rotate(0deg); 22 | transform: rotate(0deg); 23 | } 24 | 100% { 25 | -webkit-transform: rotate(359deg); 26 | transform: rotate(359deg); 27 | } 28 | } 29 | @keyframes spin { 30 | 0% { 31 | -webkit-transform: rotate(0deg); 32 | transform: rotate(0deg); 33 | } 34 | 100% { 35 | -webkit-transform: rotate(359deg); 36 | transform: rotate(359deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/service-component-communication/second/second.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from "@angular/core"; 2 | import { Subscription } from "rxjs"; 3 | 4 | import { SampleService } from "./../sample.service"; 5 | 6 | @Component({ 7 | selector: "app-second", 8 | templateUrl: "./second.component.html", 9 | styleUrls: ["./second.component.css"] 10 | }) 11 | export class SecondComponent implements OnInit, OnDestroy { 12 | 13 | message: string | null = null; 14 | sampleSubscription: Subscription | null = null; 15 | 16 | constructor(private sampleService: SampleService) { } 17 | 18 | ngOnInit() { 19 | this.sampleSubscription = this.sampleService.telecast$.subscribe(message => { 20 | this.message = message; 21 | }); 22 | } 23 | 24 | ngOnDestroy() { 25 | if (this.sampleSubscription) { 26 | this.sampleSubscription.unsubscribe(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/test-providers/test-providers.component.html: -------------------------------------------------------------------------------- 1 |

2 | Injection Beyond Classes 3 |

4 |
5 |
    6 |
  • API_BASE_HREF: {{apiBaseHref}}
  • 7 |
  • APP_BASE_HREF: {{appBaseHref}}
  • 8 |
  • IS_PROD: {{isProd}}
  • 9 |
  • APIKey: {{apiKey}}
  • 10 |
  • Random-1: {{random}}
  • 11 |
  • Random-2: {{random}}
  • 12 |
  • emailApiConfig {{emailApiConfig | json}}
  • 13 |
  • languages: {{languages | json}}
  • 14 |
  • config: {{config | json}}
  • 15 |
  • baseUrl: {{baseUrl}}
  • 16 |
  • randomFactory-1: {{randomFactory}}
  • 17 |
  • randomFactory-2: {{randomFactory}}
  • 18 |
19 |
20 |
21 | 22 | randomFactory: {{fromRandomFactory}} 23 |
24 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { UploadFileWithProgressBarComponent } from "./upload-file-with-progress-bar/upload-file-with-progress-bar.component"; 2 | import { Ng2FileUploadTestComponent } from "./ng2-file-upload-test/ng2-file-upload-test.component"; 3 | import { UploadFileSimpleComponent } from "./upload-file-simple/upload-file-simple.component"; 4 | import { NgModule } from "@angular/core"; 5 | import { Routes, RouterModule } from "@angular/router"; 6 | 7 | const routes: Routes = [ 8 | { path: "uploadFileSimple", component: UploadFileSimpleComponent }, 9 | { path: "ng2FileUploadTest", component: Ng2FileUploadTestComponent }, 10 | { 11 | path: "uploadFileWithProgressBar", 12 | component: UploadFileWithProgressBarComponent 13 | } 14 | ]; 15 | 16 | @NgModule({ 17 | imports: [RouterModule.forChild(routes)], 18 | exports: [RouterModule] 19 | }) 20 | export class UploadFileRoutingModule {} 21 | -------------------------------------------------------------------------------- /Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:45225", 7 | "sslPort": 44381 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "ASPNETCoreIdentitySample": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/app/service-component-communication/third/third.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from "@angular/core"; 2 | import { Subscription } from "rxjs"; 3 | 4 | import { SampleService } from "./../sample.service"; 5 | 6 | @Component({ 7 | selector: "app-third", 8 | templateUrl: "./third.component.html", 9 | styleUrls: ["./third.component.css"] 10 | }) 11 | export class ThirdComponent implements OnInit, OnDestroy { 12 | 13 | message: string | null = null; 14 | sampleSubscription: Subscription | null = null; 15 | 16 | constructor(private sampleService: SampleService) { } 17 | 18 | ngOnInit() { 19 | } 20 | 21 | subscribe() { 22 | this.sampleSubscription = this.sampleService.telecast$.subscribe(message => { 23 | this.message = message; 24 | }); 25 | } 26 | 27 | ngOnDestroy() { 28 | if (this.sampleSubscription) { 29 | this.sampleSubscription.unsubscribe(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/service-component-communication/service-component-communication.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | import { FormsModule } from "@angular/forms"; 4 | 5 | import { FinalComponent } from "./final/final.component"; 6 | import { FirstComponent } from "./first/first.component"; 7 | import { SampleService } from "./sample.service"; 8 | import { SecondComponent } from "./second/second.component"; 9 | import { ServiceComponentCommunicationRoutingModule } from "./service-component-communication-routing.module"; 10 | import { ThirdComponent } from "./third/third.component"; 11 | 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | FormsModule, 16 | ServiceComponentCommunicationRoutingModule 17 | ], 18 | declarations: [FirstComponent, SecondComponent, FinalComponent, ThirdComponent], 19 | providers: [SampleService] 20 | }) 21 | export class ServiceComponentCommunicationModule { } 22 | -------------------------------------------------------------------------------- /src/app/core/app-config.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | 4 | @Injectable() 5 | export class AppConfigService { 6 | 7 | private config: AppConfig | null = null; 8 | 9 | constructor(private http: HttpClient) { } 10 | 11 | loadClientConfig(): Promise { 12 | return this.http.get("assets/client-config.json") 13 | .toPromise() 14 | .then(config => { 15 | this.config = config; 16 | console.log("Config", this.config); 17 | }) 18 | .catch(err => { 19 | return Promise.reject(err); 20 | }); 21 | } 22 | 23 | get configuration(): AppConfig { 24 | if (!this.config) { 25 | throw new Error("Attempted to access configuration property before configuration data was loaded."); 26 | } 27 | return this.config; 28 | } 29 | } 30 | 31 | export class AppConfig { 32 | host: string | null = null; 33 | } 34 | -------------------------------------------------------------------------------- /src/app/model-state-validation/movie.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | import { Movie } from "./movie"; 7 | 8 | 9 | @Injectable() 10 | export class MovieService { 11 | 12 | private baseUrl = "api/movies"; 13 | 14 | constructor(private http: HttpClient) { } 15 | 16 | private handleError(error: HttpErrorResponse): Observable { 17 | console.error("observable error: ", error); 18 | return observableThrowError(error); 19 | } 20 | 21 | postMovieForm(movie: Movie): Observable { 22 | const headers = new HttpHeaders({ "Content-Type": "application/json" }); 23 | return this.http 24 | .post(this.baseUrl, movie, { headers: headers }) 25 | .pipe( 26 | map(response => response || {}), 27 | catchError(this.handleError)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/service-component-communication/first/first.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, OnInit } from "@angular/core"; 2 | import { Subscription } from "rxjs"; 3 | 4 | import { SampleService } from "./../sample.service"; 5 | 6 | @Component({ 7 | selector: "app-first", 8 | templateUrl: "./first.component.html", 9 | styleUrls: ["./first.component.css"] 10 | }) 11 | export class FirstComponent implements OnInit, OnDestroy { 12 | 13 | editedMsg: string | null = null; 14 | sampleSubscription: Subscription | null = null; 15 | 16 | constructor(private sampleService: SampleService) { } 17 | 18 | ngOnInit() { 19 | this.sampleSubscription = this.sampleService.telecast$.subscribe(message => { 20 | this.editedMsg = message; 21 | }); 22 | } 23 | 24 | editMsg() { 25 | if (this.editedMsg) { 26 | this.sampleService.editMsg(this.editedMsg); 27 | } 28 | } 29 | 30 | ngOnDestroy() { 31 | if (this.sampleSubscription) { 32 | this.sampleSubscription.unsubscribe(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/app/post-form-urlencoded/urlencoded.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | @Injectable() 7 | export class UrlencodedService { 8 | 9 | constructor(private http: HttpClient) { } 10 | 11 | doRefreshToken(refreshToken: string): Observable { 12 | const headers = new HttpHeaders({ "Content-Type": "application/x-www-form-urlencoded" }); 13 | // HttpParams is an `immutable` object an must be called this way. After the `;` you can't modify this object anymore. 14 | const body = new HttpParams() 15 | .set("grant_type", "refresh_token") 16 | .set("refresh_token", refreshToken); 17 | return this.http.post("/api/Urlencoded", body.toString(), { headers: headers }) 18 | .pipe( 19 | map(response => response || {}), 20 | catchError((error: HttpErrorResponse) => throwError(error)) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /AngularTemplateDrivenFormsLab.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/app/angular-security/show-html/safe.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from "@angular/core"; 2 | import { DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle, SafeUrl } from "@angular/platform-browser"; 3 | 4 | @Pipe({ 5 | name: "safe" 6 | }) 7 | export class SafePipe implements PipeTransform { 8 | constructor(protected sanitizer: DomSanitizer) { } 9 | 10 | public transform(value: any, type: string): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl { 11 | switch (type) { 12 | case "html": 13 | return this.sanitizer.bypassSecurityTrustHtml(value); 14 | case "style": 15 | return this.sanitizer.bypassSecurityTrustStyle(value); 16 | case "script": 17 | return this.sanitizer.bypassSecurityTrustScript(value); 18 | case "url": 19 | return this.sanitizer.bypassSecurityTrustUrl(value); 20 | case "resourceUrl": 21 | return this.sanitizer.bypassSecurityTrustResourceUrl(value); 22 | default: 23 | throw new Error(`Invalid safe type specified: ${type}`); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-simple/upload-file-simple.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Support Form (simple upload)

3 |
4 |
5 | 6 | 8 |
9 |
10 | description is required. 11 |
12 |
13 |
14 | 15 |
16 | 17 | 19 |
20 | 21 | 22 |
23 |
24 | -------------------------------------------------------------------------------- /AngularTemplateDrivenFormsLab.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AngularTemplateDrivenFormsLab", "AngularTemplateDrivenFormsLab.csproj", "{F2E13031-ADC5-4024-BB4F-91FF7CCB7113}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F2E13031-ADC5-4024-BB4F-91FF7CCB7113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F2E13031-ADC5-4024-BB4F-91FF7CCB7113}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F2E13031-ADC5-4024-BB4F-91FF7CCB7113}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F2E13031-ADC5-4024-BB4F-91FF7CCB7113}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /Controllers/TypeaheadController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace AngularTemplateDrivenFormsLab.Controllers 7 | { 8 | [Route("api/[controller]")] 9 | public class TypeaheadController : Controller 10 | { 11 | [HttpGet("[action]")] 12 | public async Task SearchCountries(string term) 13 | { 14 | await Task.Delay(1000); // simulating a slow operation 15 | 16 | var items = new[] 17 | { 18 | "Afghanistan", 19 | "Albania", 20 | "Algeria", 21 | "American Samoa", 22 | "Andorra", 23 | "Angola", 24 | "Anguilla", 25 | "Antarctica", 26 | "Antigua and/or Barbuda" 27 | }; 28 | var results = string.IsNullOrWhiteSpace(term) ? items : 29 | items.Where(item => item.StartsWith(term, StringComparison.OrdinalIgnoreCase)); 30 | return Json(results.ToArray()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | import "zone.js/dist/async-test"; 3 | import "zone.js/dist/fake-async-test"; 4 | import "zone.js/dist/jasmine-patch"; 5 | import "zone.js/dist/long-stack-trace-zone"; 6 | import "zone.js/dist/proxy.js"; 7 | import "zone.js/dist/sync-test"; 8 | 9 | import { getTestBed } from "@angular/core/testing"; 10 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from "@angular/platform-browser-dynamic/testing"; 11 | 12 | 13 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 14 | declare const __karma__: any; 15 | declare const require: any; 16 | 17 | // Prevent Karma from running prematurely. 18 | __karma__.loaded = function () { }; 19 | 20 | // First, initialize the Angular testing environment. 21 | getTestBed().initTestEnvironment( 22 | BrowserDynamicTestingModule, 23 | platformBrowserDynamicTesting() 24 | ); 25 | // Then we find all the tests. 26 | const context = require.context("./", true, /\.spec\.ts$/); 27 | // And load the modules. 28 | context.keys().map(context); 29 | // Finally, start Karma to run the tests. 30 | __karma__.start(); 31 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/test-providers/test-providers.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, OnInit } from "@angular/core"; 2 | 3 | import { APP_CONFIG, IThisModuleConfig } from "./../thismodule.config"; 4 | 5 | @Component({ 6 | selector: "app-test-providers", 7 | templateUrl: "./test-providers.component.html", 8 | styleUrls: ["./test-providers.component.css"] 9 | }) 10 | export class TestProvidersComponent implements OnInit { 11 | 12 | fromRandomFactory: string | null = null; 13 | 14 | constructor( 15 | @Inject("API_BASE_HREF") public apiBaseHref: string, 16 | @Inject("APP_BASE_HREF") public appBaseHref: string, 17 | @Inject("IS_PROD") public isProd: boolean, 18 | @Inject("APIKey") public apiKey: string, 19 | @Inject("Random") public random: string, 20 | @Inject("emailApiConfig") public emailApiConfig: any, 21 | @Inject("languages") public languages: string[], 22 | @Inject(APP_CONFIG) public config: IThisModuleConfig, 23 | @Inject("BASE_URL") public baseUrl: string, 24 | @Inject("RandomFactory") public randomFactory: string 25 | ) { } 26 | 27 | ngOnInit() { 28 | } 29 | 30 | callRandomFactory() { 31 | this.fromRandomFactory = this.randomFactory; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { NgModule } from "@angular/core"; 3 | import { FormsModule } from "@angular/forms"; 4 | import { FileUploadModule } from "ng2-file-upload"; 5 | 6 | import { Ng2FileUploadTestComponent } from "./ng2-file-upload-test/ng2-file-upload-test.component"; 7 | import { UploadFileRoutingModule } from "./upload-file-routing.module"; 8 | import { UploadFileSimpleService } from "./upload-file-simple.service"; 9 | import { UploadFileSimpleComponent } from "./upload-file-simple/upload-file-simple.component"; 10 | import { UploadFileWithProgressBarService } from "./upload-file-with-progress-bar.service"; 11 | import { UploadFileWithProgressBarComponent } from "./upload-file-with-progress-bar/upload-file-with-progress-bar.component"; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | FormsModule, 17 | FileUploadModule, 18 | UploadFileRoutingModule 19 | ], 20 | declarations: [ 21 | UploadFileSimpleComponent, 22 | Ng2FileUploadTestComponent, 23 | UploadFileWithProgressBarComponent 24 | ], 25 | providers: [UploadFileSimpleService, UploadFileWithProgressBarService] 26 | }) 27 | export class UploadFileModule { } 28 | -------------------------------------------------------------------------------- /src/app/product/product-items.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | import { Category } from "./category"; 7 | import { Product } from "./product"; 8 | 9 | 10 | @Injectable() 11 | export class ProductItemsService { 12 | private baseUrl = "api/product"; 13 | 14 | constructor(private http: HttpClient) { } 15 | 16 | private handleError(error: HttpErrorResponse): Observable { 17 | console.error("observable error: ", error); 18 | return observableThrowError(error); 19 | } 20 | 21 | getCategories(): Observable { 22 | return this.http 23 | .get(`${this.baseUrl}/GetCategories`) 24 | .pipe( 25 | map(response => response || {}), 26 | catchError(this.handleError)); 27 | } 28 | 29 | getProducts(categoryId: number): Observable { 30 | return this.http 31 | .get(`${this.baseUrl}/GetProducts/${categoryId}`) 32 | .pipe( 33 | map(response => response || {}), 34 | catchError(this.handleError)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/app/angular-security/show-html/show-html.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, SecurityContext, ViewChild } from "@angular/core"; 2 | import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; 3 | 4 | 5 | @Component({ 6 | selector: "app-show-html", 7 | templateUrl: "./show-html.component.html", 8 | styleUrls: ["./show-html.component.css"] 9 | }) 10 | export class ShowHtmlComponent implements OnInit { 11 | 12 | @ViewChild("dataContainer") dataContainer: ElementRef | null = null; 13 | htmlContent = "Template Syntax"; 14 | html: SafeHtml | null = null; 15 | sanitizedHtml: string | null = null; 16 | 17 | constructor(private sanitizer: DomSanitizer) { } 18 | 19 | ngOnInit() { 20 | if (!this.dataContainer) { 21 | throw new Error("this.dataContainer is null"); 22 | } 23 | this.dataContainer.nativeElement.innerHTML = "nativeElement Syntax"; 24 | this.html = this.sanitizer.bypassSecurityTrustHtml( 25 | "From DomSanitizer"); 26 | 27 | this.sanitizedHtml = this.sanitizer.sanitize(SecurityContext.HTML, "Sanitize"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/browser-storage-sample/browser-storage-sample-test/browser-storage-sample-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import { BrowserStorageService } from "@app/core"; 3 | 4 | @Component({ 5 | selector: "app-browser-storage-sample-test", 6 | templateUrl: "./browser-storage-sample-test.component.html", 7 | styleUrls: ["./browser-storage-sample-test.component.css"] 8 | }) 9 | export class BrowserStorageSampleTestComponent implements OnInit { 10 | 11 | fromSessionStorage = ""; 12 | fromLocalStorage = "" 13 | 14 | sessionStorageKey = "sessionStorageKey1"; 15 | localStorageKey = "localStorageKey1" 16 | 17 | constructor(private browserStorage: BrowserStorageService) { } 18 | 19 | ngOnInit() { 20 | } 21 | 22 | sessionStorageSetItem() { 23 | this.browserStorage.setSession(this.sessionStorageKey, "Val1"); 24 | } 25 | 26 | sessionStorageGetItem() { 27 | this.fromSessionStorage = this.browserStorage.getSession(this.sessionStorageKey); 28 | } 29 | 30 | localStorageSetItem() { 31 | this.browserStorage.setLocal(this.localStorageKey, { key1: "val1", key2: 2 }); 32 | } 33 | 34 | localStorageGetItem() { 35 | this.fromLocalStorage = JSON.stringify(this.browserStorage.getLocal(this.localStorageKey)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Models/ProductDataSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Models 4 | { 5 | /// 6 | /// منبع داده فرضی جهت سهولت دموی برنامه 7 | /// 8 | public static class ProductDataSource 9 | { 10 | private static readonly IList _cachedItems; 11 | static ProductDataSource() 12 | { 13 | _cachedItems = createProductsDataSource(); 14 | } 15 | 16 | public static IList LatestProducts 17 | { 18 | get { return _cachedItems; } 19 | } 20 | 21 | /// 22 | /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است 23 | /// 24 | private static IList createProductsDataSource() 25 | { 26 | var list = new List(); 27 | for (var i = 0; i < 1500; i++) 28 | { 29 | list.Add(new Product 30 | { 31 | ProductId = i + 1, 32 | ProductName = "نام " + (i + 1), 33 | IsAvailable = (i % 2 == 0), 34 | Price = 1000 + i 35 | }); 36 | } 37 | return list; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/app/welcome/welcome.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Welcome works! 4 |

5 | 6 |
7 | -------------------------------------------------------------------------------- /src/app/modal-bootstrap-dialogs/modal-dialog-test/modal-dialog-test.component.html: -------------------------------------------------------------------------------- 1 |

Displaying modal bootstrap dialogs

2 | 3 | 4 | 5 | 6 | 12 | 28 | 29 | 30 | 31 |
{{confirmResult}}
32 | -------------------------------------------------------------------------------- /src/app/custom-pipe/moment-jalaali-test/moment-jalaali-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import * as momentJalaali from "moment-jalaali"; 3 | 4 | import { JsonDatesService } from "./../json-dates.service"; 5 | 6 | @Component({ 7 | selector: "app-moment-jalaali-test", 8 | templateUrl: "./moment-jalaali-test.component.html", 9 | styleUrls: ["./moment-jalaali-test.component.css"] 10 | }) 11 | export class MomentJalaaliTestComponent implements OnInit { 12 | now: string | null = null; 13 | nowLongDateFormat: string | null = null; 14 | nowExtraLongDateFormat: string | null = null; 15 | dates: any[] = []; 16 | 17 | constructor(private jsonDatesService: JsonDatesService) { } 18 | 19 | ngOnInit() { 20 | this.persianDateTests(); 21 | 22 | this.jsonDatesService.getDates().subscribe(data => { 23 | this.dates = data; 24 | }); 25 | } 26 | 27 | persianDateTests() { 28 | // https://github.com/jalaali/moment-jalaali 29 | momentJalaali.loadPersian(/*{ usePersianDigits: true }*/); // نمايش فارسى نام ماه‌ها، روزها و امثال آن 30 | 31 | this.now = momentJalaali().format("jYYYY/jMM/jDD HH:mm"); 32 | this.nowLongDateFormat = momentJalaali().format("jD jMMMM jYYYY [ساعت] LT"); 33 | this.nowExtraLongDateFormat = momentJalaali().format( 34 | "dddd، jD jMMMM jYYYY [ساعت] LT" 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "strict": true, /* Enable all strict type-checking options. */ 11 | "noImplicitReturns": true, 12 | "allowUnreachableCode": false, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "suppressExcessPropertyErrors": false, 16 | "noImplicitThis": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "strictFunctionTypes": true, 19 | "suppressImplicitAnyIndexErrors": false, 20 | "alwaysStrict": true, 21 | "noImplicitAny": true, 22 | "strictNullChecks": true, 23 | "target": "es5", 24 | "typeRoots": [ 25 | "node_modules/@types" 26 | ], 27 | "lib": [ 28 | "es2016", 29 | "dom" 30 | ], 31 | "baseUrl": "src", 32 | "paths": { 33 | "@app/*": [ 34 | "app/*" 35 | ], 36 | "@app/core/*": [ 37 | "app/core/*" 38 | ], 39 | "@app/shared/*": [ 40 | "app/shared/*" 41 | ], 42 | "@env/*": [ 43 | "environments/*" 44 | ] 45 | } 46 | }, 47 | "angularCompilerOptions": { 48 | "fullTemplateTypeCheck": true, 49 | "preserveWhiteSpace": false 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Controllers/EmployeeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using AngularTemplateDrivenFormsLab.Models; 3 | using System; 4 | 5 | namespace AngularTemplateDrivenFormsLab.Controllers 6 | { 7 | [Route("api/[controller]")] 8 | public class EmployeeController : Controller 9 | { 10 | public IActionResult Post([FromBody] Employee model) 11 | { 12 | //todo: save model 13 | 14 | model.Id = 100; 15 | return Created("", new { fields = model }); 16 | } 17 | 18 | [HttpGet("/api/[controller]/[action]")] 19 | public IActionResult Languages() 20 | { 21 | string[] languages = { "Persian", "English", "Spanish", "Other" }; 22 | return Ok(languages); 23 | } 24 | 25 | [HttpPost("[action]")] 26 | [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] 27 | public IActionResult CheckUser([FromBody] Employee model) 28 | { 29 | var remoteValidationResult = new { result = true, message = $"{model.FirstName} is fine!" }; 30 | if (model.FirstName?.Equals("Vahid", StringComparison.OrdinalIgnoreCase) ?? false) 31 | { 32 | remoteValidationResult = new { result = false, message = "username:`Vahid` is already taken." }; 33 | } 34 | 35 | return Json(remoteValidationResult); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/product/product-group/product-group.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Cascading Drop-down Lists

3 |
4 |
5 | 6 | 7 | 14 |
15 | 16 |
17 | 18 | 19 | 25 |
26 | 27 | 28 |
29 |
30 | -------------------------------------------------------------------------------- /src/app/timers/using-timers/using-timers.component.html: -------------------------------------------------------------------------------- 1 |

2 | Using timers 3 |

4 | 5 |
6 |
7 |

Observable.interval(1000)

8 |
9 |
10 |
11 | {{intervalValue}} 12 |
13 |
14 | 15 | 16 |
17 |
18 |
19 | 20 |
21 |
22 |

Countdown timer

23 |
24 |
25 |
26 | {{countdown}} 27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 |
36 |

Ticker

37 |
38 |
39 |
40 | {{tick}} 41 |
42 |
43 | 44 | 45 | 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-simple.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | import { Ticket } from "./ticket"; 7 | 8 | 9 | @Injectable() 10 | export class UploadFileSimpleService { 11 | private baseUrl = "api/SimpleUpload"; 12 | 13 | constructor(private http: HttpClient) { } 14 | 15 | postTicket(ticket: Ticket, filesList: FileList): Observable { 16 | if (!filesList || filesList.length === 0) { 17 | return observableThrowError("Please select a file."); 18 | } 19 | 20 | const formData: FormData = new FormData(); 21 | 22 | for (const key in ticket) { 23 | if (ticket.hasOwnProperty(key)) { 24 | formData.append(key, (ticket)[key]); 25 | } 26 | } 27 | 28 | for (let i = 0; i < filesList.length; i++) { 29 | formData.append(filesList[i].name, filesList[i]); 30 | } 31 | 32 | const headers = new HttpHeaders().set("Accept", "application/json"); 33 | return this.http 34 | .post(`${this.baseUrl}/SaveTicket`, formData, { headers: headers }) 35 | .pipe( 36 | map(response => response || {}), 37 | catchError((error: HttpErrorResponse) => { 38 | console.error("observable error: ", error); 39 | return observableThrowError(error); 40 | })); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/autocomplete/autocomplete-sample/autocomplete-sample.component.html: -------------------------------------------------------------------------------- 1 |

2 | Autocomplete 3 |

4 |
5 |
6 |

Using flatMap

7 |
8 |
9 | 10 | 11 |
    12 |
  • 13 | {{country}} 14 |
  • 15 |
16 |
17 |
18 | 19 | 20 |
21 |
22 |

Using switchMap

23 |
24 |
25 | 26 | 27 |
    28 |
  • 29 | {{country}} 30 |
  • 31 |
32 |
33 |
34 | 35 |
36 |
37 |

Normal debounceTime

38 |
39 |
40 | 41 | 42 |
    43 |
  • 44 | {{country}} 45 |
  • 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /Utils/AngularPushStateRoutingMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace AngularTemplateDrivenFormsLab.Utils 8 | { 9 | public class AngularPushStateRoutingMiddleware 10 | { 11 | private readonly RequestDelegate _next; 12 | 13 | public AngularPushStateRoutingMiddleware(RequestDelegate next) 14 | { 15 | _next = next; 16 | } 17 | 18 | public async Task Invoke(HttpContext context) 19 | { 20 | await _next(context); 21 | var requestPath = context.Request.Path.Value; 22 | if (requestPath != null && 23 | context.Response.StatusCode == 404 && 24 | !Path.HasExtension(requestPath) && 25 | !requestPath.StartsWith("/api/", StringComparison.OrdinalIgnoreCase)) 26 | { 27 | //context.Request.Path = "/index.html"; 28 | context.Request.Path = "/"; // since we are using views/shared/_layout.cshtml now. 29 | context.Response.StatusCode = 200; 30 | await _next(context); 31 | } 32 | } 33 | } 34 | 35 | public static class AngularPushStateRoutingMiddlewareExtensions 36 | { 37 | public static IApplicationBuilder UseAngularPushStateRouting(this IApplicationBuilder builder) 38 | { 39 | return builder.UseMiddleware(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ng2-lab 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | @RenderBody() 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/app/core/browser-storage.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | 3 | @Injectable() 4 | export class BrowserStorageService { 5 | 6 | getSession(key: string): any { 7 | const data = window.sessionStorage.getItem(key); 8 | if (data) { 9 | return JSON.parse(data); 10 | } else { 11 | return null; 12 | } 13 | } 14 | 15 | setSession(key: string, value: any): void { 16 | const data = value === undefined ? "" : JSON.stringify(value); 17 | window.sessionStorage.setItem(key, data); 18 | } 19 | 20 | removeSession(key: string): void { 21 | window.sessionStorage.removeItem(key); 22 | } 23 | 24 | removeAllSessions(): void { 25 | for (const key in window.sessionStorage) { 26 | if (window.sessionStorage.hasOwnProperty(key)) { 27 | this.removeSession(key); 28 | } 29 | } 30 | } 31 | 32 | getLocal(key: string): any { 33 | const data = window.localStorage.getItem(key); 34 | if (data) { 35 | return JSON.parse(data); 36 | } else { 37 | return null; 38 | } 39 | } 40 | 41 | setLocal(key: string, value: any): void { 42 | const data = value === undefined ? "" : JSON.stringify(value); 43 | window.localStorage.setItem(key, data); 44 | } 45 | 46 | removeLocal(key: string): void { 47 | window.localStorage.removeItem(key); 48 | } 49 | 50 | removeAllLocals(): void { 51 | for (const key in window.localStorage) { 52 | if (window.localStorage.hasOwnProperty(key)) { 53 | this.removeLocal(key); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/app/core/interceptors/loader-interceptor.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { SlimLoadingBarService } from "ng2-slim-loading-bar"; 4 | import { Observable } from "rxjs"; 5 | import { tap } from "rxjs/operators"; 6 | 7 | @Injectable() 8 | export class LoaderInterceptorService implements HttpInterceptor { 9 | 10 | private pendingRequests = 0; 11 | private showLoading = false; 12 | 13 | constructor(private loadingBar: SlimLoadingBarService) { 14 | } 15 | 16 | intercept(req: HttpRequest, next: HttpHandler): Observable> { 17 | this.pendingRequests++; 18 | this.showLoadingBar(); 19 | 20 | return next.handle(req).pipe( 21 | tap( 22 | (event: HttpEvent) => { 23 | if (event instanceof HttpResponse) { 24 | this.hideLoadingBar(); 25 | } 26 | }, 27 | () => { 28 | this.pendingRequests = 0; 29 | this.hideLoadingBar(); 30 | }, 31 | () => { 32 | this.pendingRequests = 0; 33 | this.hideLoadingBar(); 34 | }) 35 | ); 36 | } 37 | 38 | private showLoadingBar() { 39 | if (!this.showLoading) { 40 | this.loadingBar.start(); 41 | this.showLoading = true; 42 | } 43 | } 44 | 45 | private hideLoadingBar() { 46 | this.pendingRequests--; 47 | if (this.pendingRequests <= 0) { 48 | this.loadingBar.complete(); 49 | this.showLoading = false; 50 | this.pendingRequests = 0; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-with-progress-bar.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse, HttpEvent, HttpHeaders } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | import { Ticket } from "./ticket"; 7 | 8 | 9 | @Injectable() 10 | export class UploadFileWithProgressBarService { 11 | private baseUrl = "api/SimpleUpload"; 12 | 13 | constructor(private http: HttpClient) { } 14 | 15 | postTicket(ticket: Ticket, filesList: FileList): Observable> { 16 | if (!filesList || filesList.length === 0) { 17 | return observableThrowError("Please select a file."); 18 | } 19 | 20 | const formData: FormData = new FormData(); 21 | 22 | for (const key in ticket) { 23 | if (ticket.hasOwnProperty(key)) { 24 | formData.append(key, (ticket)[key]); 25 | } 26 | } 27 | 28 | for (let i = 0; i < filesList.length; i++) { 29 | formData.append(filesList[i].name, filesList[i]); 30 | } 31 | 32 | const headers = new HttpHeaders().set("Accept", "application/json"); 33 | return this.http 34 | .post(`${this.baseUrl}/SaveTicket`, formData, { 35 | headers: headers, 36 | reportProgress: true, 37 | observe: "events" 38 | }).pipe( 39 | map(response => response || {}), 40 | catchError((error: HttpErrorResponse) => { 41 | console.error("observable error: ", error); 42 | return observableThrowError(error); 43 | })); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/angular-http-client-blob/download-blobs/download-blobs.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from "@angular/core"; 2 | import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; 3 | import { DownloadBinaryDataService } from "app/angular-http-client-blob/download-binary-data.service"; 4 | 5 | import { WindowRefService } from "./../../core/window.service"; 6 | 7 | @Component({ 8 | templateUrl: "./download-blobs.component.html", 9 | styleUrls: ["./download-blobs.component.css"] 10 | }) 11 | export class DownloadBlobsComponent implements OnInit { 12 | 13 | @ViewChild("sampleImage1") sampleImage1: ElementRef | null = null; 14 | private nativeWindow: Window | null = null; 15 | imageBlobUrl: string | null = null; 16 | sanitizedImageBlobUrl: SafeUrl | null = null; 17 | 18 | constructor(private downloadService: DownloadBinaryDataService, 19 | private windowRefService: WindowRefService, private sanitizer: DomSanitizer) { } 20 | 21 | ngOnInit() { 22 | this.nativeWindow = this.windowRefService.nativeWindow; 23 | this.downloadService.getImage().subscribe(imageDataBlob => { 24 | if (!this.nativeWindow || !this.sampleImage1) { 25 | return; 26 | } 27 | const urlCreator = this.nativeWindow.URL; 28 | this.imageBlobUrl = urlCreator.createObjectURL(imageDataBlob); // doesn't work -> 29 | this.sampleImage1.nativeElement.src = this.imageBlobUrl; // works -> 30 | this.sanitizedImageBlobUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageBlobUrl); // works -> 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Utils/AntiforgeryTokenMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.Antiforgery; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace AngularTemplateDrivenFormsLab.Utils 8 | { 9 | public class AngularAntiforgeryTokenMiddleware 10 | { 11 | private readonly RequestDelegate _next; 12 | private readonly IAntiforgery _antiforgery; 13 | 14 | public AngularAntiforgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery) 15 | { 16 | _next = next; 17 | _antiforgery = antiforgery; 18 | } 19 | 20 | public Task Invoke(HttpContext context) 21 | { 22 | var path = context.Request.Path.Value; 23 | if (path != null && !path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase)) 24 | { 25 | var tokens = _antiforgery.GetAndStoreTokens(context); 26 | context.Response.Cookies.Append( 27 | key: "XSRF-TOKEN", 28 | value: tokens.RequestToken, 29 | options: new CookieOptions 30 | { 31 | HttpOnly = false // Now JavaScript is able to read the cookie 32 | }); 33 | } 34 | return _next(context); 35 | } 36 | } 37 | 38 | public static class AntiforgeryTokenMiddlewareExtensions 39 | { 40 | public static IApplicationBuilder UseAngularAntiforgeryToken(this IApplicationBuilder builder) 41 | { 42 | return builder.UseMiddleware(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/core/interceptors/retry.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, of, throwError } from "rxjs"; 4 | import { catchError, delay, mergeMap, retryWhen, take } from "rxjs/operators"; 5 | 6 | @Injectable() 7 | export class RetryInterceptor implements HttpInterceptor { 8 | 9 | private delayBetweenRetriesMs = 1000; 10 | private numberOfRetries = 3; 11 | 12 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 13 | return next.handle(request).pipe( 14 | retryWhen(errors => errors.pipe( 15 | mergeMap((error: HttpErrorResponse, retryAttempt: number) => { 16 | if (retryAttempt === this.numberOfRetries - 1) { 17 | console.log(`HTTP call '${request.method} ${request.url}' failed after ${this.numberOfRetries} retries.`); 18 | return throwError(error); // no retry 19 | } 20 | 21 | switch (error.status) { 22 | case 400: 23 | case 404: 24 | return throwError(error); // no retry 25 | } 26 | 27 | return of(error); // retry 28 | }), 29 | delay(this.delayBetweenRetriesMs), 30 | take(this.numberOfRetries) 31 | )), 32 | catchError((error: any, caught: Observable>) => { 33 | console.error({ error, caught }); 34 | if (error.status === 401 || error.status === 403) { 35 | // this.router.navigate(["/accessDenied"]); 36 | } 37 | return throwError(error); 38 | }) 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Controllers/SimpleUploadController.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading.Tasks; 3 | using AngularTemplateDrivenFormsLab.Models; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace AngularTemplateDrivenFormsLab.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public class SimpleUploadController : Controller 11 | { 12 | private readonly IWebHostEnvironment _environment; 13 | public SimpleUploadController(IWebHostEnvironment environment) 14 | { 15 | _environment = environment; 16 | } 17 | 18 | [HttpPost("[action]")] 19 | public async Task SaveTicket(Ticket ticket) 20 | { 21 | //TODO: save the ticket ... get id 22 | ticket.Id = 1001; 23 | 24 | var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads"); 25 | if (!Directory.Exists(uploadsRootFolder)) 26 | { 27 | Directory.CreateDirectory(uploadsRootFolder); 28 | } 29 | 30 | var files = Request.Form.Files; 31 | foreach (var file in files) 32 | { 33 | //TODO: do security checks ...! 34 | 35 | if (file == null || file.Length == 0) 36 | { 37 | continue; 38 | } 39 | 40 | var filePath = Path.Combine(uploadsRootFolder, file.FileName); 41 | using (var fileStream = new FileStream(filePath, FileMode.Create)) 42 | { 43 | await file.CopyToAsync(fileStream).ConfigureAwait(false); 44 | } 45 | } 46 | 47 | return Created("", ticket); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/core/seo-service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { Meta, Title } from "@angular/platform-browser"; 3 | import { ActivatedRouteSnapshot, NavigationEnd, Router } from "@angular/router"; 4 | import { distinctUntilChanged, filter } from "rxjs/operators"; 5 | 6 | @Injectable() 7 | export class SeoService { 8 | 9 | constructor(private titleService: Title, private metaService: Meta, private router: Router) { } 10 | 11 | enableSeo() { 12 | this.router.events.pipe( 13 | filter(event => event instanceof NavigationEnd), 14 | distinctUntilChanged() 15 | ).subscribe(() => { 16 | this.addMetaData(this.router.routerState.snapshot.root); 17 | }); 18 | } 19 | 20 | private addMetaData(root: ActivatedRouteSnapshot): void { 21 | if (root.children && root.children.length) { 22 | this.addMetaData(root.children[0]); 23 | } else if (root.data) { 24 | this.setTitle(root.data); 25 | this.setMetaTags(root.data); 26 | } 27 | } 28 | 29 | private setMetaTags(routeData: { [name: string]: any; }) { 30 | const routeDataMetaTagsKey = "metaTags"; 31 | const metaTags = routeData[routeDataMetaTagsKey]; 32 | if (!metaTags) { return; } 33 | for (const tag in metaTags) { 34 | if (metaTags.hasOwnProperty(tag)) { 35 | const newTag = { name: tag, content: metaTags[tag] }; 36 | console.log("new tag", newTag); 37 | this.metaService.addTag(newTag); 38 | } 39 | } 40 | } 41 | 42 | private setTitle(routeData: { [name: string]: any; }) { 43 | const routeDataTitleKey = "title"; 44 | const title = routeData[routeDataTitleKey]; 45 | if (title) { 46 | console.log("new title", title); 47 | this.titleService.setTitle(title); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/employee/employee-register/employee-register.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import { NgForm } from "@angular/forms"; 3 | import { Employee } from "app/employee/employee"; 4 | 5 | import { FormPosterService } from "../form-poster.service"; 6 | 7 | @Component({ 8 | selector: "app-employee-register", 9 | templateUrl: "./employee-register.component.html", 10 | styleUrls: ["./employee-register.component.css"] 11 | }) 12 | export class EmployeeRegisterComponent implements OnInit { 13 | languages: string[] = []; 14 | model = new Employee("Vahid", "N", true, "FullTime", "default"); 15 | hasPrimaryLanguageError = false; 16 | 17 | constructor(private formPoster: FormPosterService) { } 18 | 19 | ngOnInit() { 20 | this.formPoster.getLanguages().subscribe( 21 | data => { 22 | this.languages = data; 23 | }, 24 | err => console.log("get error: ", err) 25 | ); 26 | } 27 | 28 | firstNameToUpperCase(value: string) { 29 | if (value.length > 0) { 30 | this.model.firstName = value.charAt(0).toUpperCase() + value.slice(1); 31 | } else { 32 | this.model.firstName = value; 33 | } 34 | } 35 | 36 | validatePrimaryLanguage(value: any) { 37 | if (value === "default") { 38 | this.hasPrimaryLanguageError = true; 39 | } else { 40 | this.hasPrimaryLanguageError = false; 41 | } 42 | } 43 | 44 | submitForm(form: NgForm) { 45 | console.log(this.model); 46 | console.log(form.value); 47 | 48 | // validate form 49 | this.validatePrimaryLanguage(this.model.primaryLanguage); 50 | if (this.hasPrimaryLanguageError) { 51 | return; 52 | } 53 | 54 | this.formPoster 55 | .postEmployeeForm(this.model) 56 | .subscribe( 57 | data => console.log("success: ", data), 58 | err => console.log("error: ", err) 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/app/employee/form-poster.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, of, throwError as observableThrowError, throwError } from "rxjs"; 4 | import { catchError, delay, map, mergeMap, retryWhen, take } from "rxjs/operators"; 5 | 6 | import { Employee } from "./employee"; 7 | 8 | 9 | @Injectable() 10 | export class FormPosterService { 11 | private baseUrl = "api/employee"; 12 | 13 | constructor(private http: HttpClient) { } 14 | 15 | private handleError(error: HttpErrorResponse): Observable { 16 | console.error("observable error: ", error); 17 | return observableThrowError(error); 18 | } 19 | 20 | postEmployeeForm(employee: Employee): Observable { 21 | const headers = new HttpHeaders({ "Content-Type": "application/json" }); 22 | return this.http 23 | .post(this.baseUrl, employee, { headers: headers }) 24 | .pipe( 25 | map((response: any) => response["fields"] || {}), 26 | retryWhen(errors => errors.pipe( 27 | mergeMap((error: HttpErrorResponse, retryAttempt: number) => { 28 | if (retryAttempt === 3 - 1) { 29 | console.log(`HTTP call failed after 3 retries.`); 30 | return throwError(error); // no retry 31 | } 32 | 33 | switch (error.status) { 34 | case 400: 35 | case 404: 36 | return throwError(error); // no retry 37 | } 38 | 39 | return of(error); // retry 40 | }), 41 | delay(1000), 42 | take(3) 43 | )), 44 | catchError(this.handleError) 45 | ); 46 | } 47 | 48 | getLanguages(): Observable { 49 | return this.http 50 | .get(`${this.baseUrl}/languages`) 51 | .pipe( 52 | map(response => response || {}), 53 | catchError(this.handleError)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { HttpClientModule } from "@angular/common/http"; 3 | import { ModuleWithProviders, NgModule } from "@angular/core"; 4 | import { FormsModule } from "@angular/forms"; 5 | import { FileUploadModule } from "ng2-file-upload"; 6 | import { SlimLoadingBarModule } from "ng2-slim-loading-bar"; 7 | import { ToastyModule } from "ng2-toasty"; 8 | 9 | import { ConfirmModalComponent } from "./confirm-modal/confirm-modal.component"; 10 | import { SharedBootstrapModule } from "./shared.bootstrap.module"; 11 | 12 | @NgModule({ 13 | imports: [ 14 | CommonModule, 15 | FormsModule, 16 | HttpClientModule, 17 | SharedBootstrapModule, 18 | ToastyModule.forRoot(), 19 | SlimLoadingBarModule.forRoot(), 20 | FileUploadModule 21 | ], 22 | entryComponents: [ 23 | // All components about to be loaded "dynamically" need to be declared in the entryComponents section. 24 | ConfirmModalComponent 25 | ], 26 | declarations: [ 27 | // common and shared components/directives/pipes between more than one module and components will be listed here. 28 | ConfirmModalComponent 29 | ], 30 | exports: [ 31 | // common and shared components/directives/pipes between more than one module and components will be listed here. 32 | CommonModule, 33 | FormsModule, 34 | HttpClientModule, 35 | SharedBootstrapModule, 36 | ToastyModule, 37 | SlimLoadingBarModule, 38 | FileUploadModule 39 | ] 40 | /* No providers here! Since they’ll be already provided in AppModule. */ 41 | }) 42 | export class SharedModule { 43 | static forRoot(): ModuleWithProviders { 44 | // Forcing the whole app to use the returned providers from the AppModule only. 45 | return { 46 | ngModule: SharedModule, 47 | providers: [ /* All of your services here. It will hold the services needed by `itself`. */] 48 | }; 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-simple/upload-file-simple.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ElementRef, OnInit, ViewChild } from "@angular/core"; 2 | import { NgForm } from "@angular/forms"; 3 | import { ToastOptions, ToastyService } from "ng2-toasty"; 4 | 5 | import { Ticket } from "./../ticket"; 6 | import { UploadFileSimpleService } from "./../upload-file-simple.service"; 7 | 8 | @Component({ 9 | selector: "app-upload-file-simple", 10 | templateUrl: "./upload-file-simple.component.html", 11 | styleUrls: ["./upload-file-simple.component.css"] 12 | }) 13 | export class UploadFileSimpleComponent implements OnInit { 14 | @ViewChild("screenshotInput") screenshotInput: ElementRef | null = null; 15 | model = new Ticket(); 16 | 17 | constructor( 18 | private uploadService: UploadFileSimpleService, 19 | private toastyService: ToastyService 20 | ) { } 21 | 22 | ngOnInit() { } 23 | 24 | fileChange(event: any) { 25 | const filesList: FileList = event.target.files; 26 | console.log("fileChange() -> filesList", filesList); 27 | } 28 | 29 | submitForm(form: NgForm) { 30 | console.log("this.model", this.model); 31 | console.log("form.value", form.value); 32 | 33 | if (!this.screenshotInput) { 34 | throw new Error("this.screenshotInput is null."); 35 | } 36 | 37 | const fileInput: HTMLInputElement = this.screenshotInput.nativeElement; 38 | console.log("fileInput.files", fileInput.files); 39 | 40 | if (!fileInput.files) { 41 | return; 42 | } 43 | 44 | this.uploadService 45 | .postTicket(this.model, fileInput.files) 46 | .subscribe((data: any) => { 47 | console.log("success: ", data); 48 | this.toastyService.success( 49 | { 50 | title: "Success!", 51 | msg: 52 | "Your ticket has been submitted successfully and will be resolved shortly!", 53 | theme: "bootstrap", 54 | showClose: true, 55 | timeout: 15000 56 | } 57 | ); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome for dotnet build & ng build", 11 | "url": "http://localhost:5000", 12 | "runtimeArgs": [ 13 | "--user-data-dir", 14 | "--remote-debugging-port=9222", 15 | "--disable-session-crashed-bubble" 16 | ], 17 | "sourceMaps": true, 18 | "trace": true, 19 | "webRoot": "${workspaceRoot}/wwwroot/", 20 | "userDataDir": "${workspaceRoot}/.vscode/chrome" 21 | }, 22 | { 23 | "name": ".NET Core Launch (web)", 24 | "type": "coreclr", 25 | "request": "launch", 26 | "preLaunchTask": "build", 27 | // If you have changed target frameworks, make sure to update the program path. 28 | "program": "${workspaceRoot}/bin/Debug/netcoreapp1.1/AngularTemplateDrivenFormsLab.dll", 29 | "args": [], 30 | "cwd": "${workspaceRoot}", 31 | "stopAtEntry": false, 32 | "internalConsoleOptions": "openOnSessionStart", 33 | "launchBrowser": { 34 | "enabled": true, 35 | "args": "${auto-detect-url}", 36 | "windows": { 37 | "command": "cmd.exe", 38 | "args": "/C start ${auto-detect-url}" 39 | }, 40 | "osx": { 41 | "command": "open" 42 | }, 43 | "linux": { 44 | "command": "xdg-open" 45 | } 46 | }, 47 | "env": { 48 | "ASPNETCORE_ENVIRONMENT": "Development" 49 | }, 50 | "sourceFileMap": { 51 | "/Views": "${workspaceRoot}/Views" 52 | } 53 | }, 54 | { 55 | "name": ".NET Core Attach", 56 | "type": "coreclr", 57 | "request": "attach", 58 | "processId": "${command:pickProcess}" 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/app/custom-validators/equal-validator.directive.ts: -------------------------------------------------------------------------------- 1 | import { Attribute, Directive } from "@angular/core"; 2 | import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; 3 | 4 | @Directive({ 5 | // https://angular.io/guide/styleguide#style-02-08 6 | // Do use a custom prefix for the selector of directives (e.g, the prefix toh from Tour of Heroes). 7 | // Do spell non-element selectors in lower camel case unless the selector is meant to match a native HTML attribute. 8 | 9 | // the directive matches elements that have the attribute appValidateEqual and one of the formControlName or formControl or ngModel 10 | selector: 11 | "[appValidateEqual][formControlName],[appValidateEqual][formControl],[appValidateEqual][ngModel]", 12 | providers: [ 13 | { 14 | provide: NG_VALIDATORS, 15 | useExisting: EqualValidatorDirective, 16 | multi: true // the new directives are added to the previously registered directives instead of overriding them. 17 | } 18 | ] 19 | }) 20 | export class EqualValidatorDirective implements Validator { 21 | constructor( @Attribute("compare-to") public compareToControl: string) { } 22 | 23 | validate(element: AbstractControl): ValidationErrors | null { 24 | const selfValue = element.value; 25 | const otherControl = element.root.get(this.compareToControl); 26 | 27 | // console.log("EqualValidatorDirective", { 28 | // thisControlValue: selfValue, 29 | // otherControlValue: otherControl ? otherControl.value : null 30 | // }); 31 | 32 | if (otherControl && selfValue !== otherControl.value) { 33 | return { 34 | appValidateEqual: true // Or a string such as 'Password mismatch.' or an abject. 35 | }; 36 | } 37 | 38 | if ( 39 | otherControl && 40 | otherControl.errors && 41 | selfValue === otherControl.value 42 | ) { 43 | delete otherControl.errors["appValidateEqual"]; 44 | if (!Object.keys(otherControl.errors).length) { 45 | otherControl.setErrors(null); 46 | } 47 | } 48 | 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Utils/ContentSecurityPolicyMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace AngularTemplateDrivenFormsLab.Utils 6 | { 7 | public class ContentSecurityPolicyMiddleware 8 | { 9 | private readonly RequestDelegate _next; 10 | 11 | public ContentSecurityPolicyMiddleware(RequestDelegate next) 12 | { 13 | _next = next; 14 | } 15 | 16 | public Task Invoke(HttpContext context) 17 | { 18 | context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); 19 | context.Response.Headers.Add("X-Xss-Protection", "1; mode=block"); 20 | context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); // Refused to execute script from '' because its MIME type ('') is not executable, and strict MIME type checking is enabled. 21 | 22 | string[] csp = 23 | { 24 | "default-src 'self' blob:", 25 | "style-src 'self' 'unsafe-inline'", 26 | "script-src 'self' https://freegeoip.net/ 'unsafe-inline' 'unsafe-eval' ", 27 | "font-src 'self'", 28 | "img-src 'self' data: blob:", 29 | "connect-src 'self'", 30 | "media-src 'self'", 31 | "object-src 'self' blob:", 32 | "report-uri /api/CspReport/Log" //TODO: Add api/CspReport/Log 33 | }; 34 | context.Response.Headers.Add("Content-Security-Policy", string.Join("; ", csp)); 35 | return _next(context); 36 | } 37 | } 38 | 39 | public static class ContentSecurityPolicyMiddlewareExtensions 40 | { 41 | /// 42 | /// Make sure you add this code BEFORE app.UseStaticFiles();, 43 | /// otherwise the headers will not be applied to your static files. 44 | /// 45 | public static IApplicationBuilder UseContentSecurityPolicy(this IApplicationBuilder builder) 46 | { 47 | return builder.UseMiddleware(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/product/product-group/product-group.component.ts: -------------------------------------------------------------------------------- 1 | import { NgForm } from "@angular/forms"; 2 | import { Component, OnInit } from "@angular/core"; 3 | 4 | import { ProductItemsService } from "./../product-items.service"; 5 | 6 | import { Category } from "./../category"; 7 | import { Product } from "./../product"; 8 | import { ProductGroupForm } from "./../product-group-form"; 9 | 10 | import { ToastyService, ToastOptions } from "ng2-toasty"; 11 | 12 | @Component({ 13 | selector: "app-product-group", 14 | templateUrl: "./product-group.component.html", 15 | styleUrls: ["./product-group.component.css"] 16 | }) 17 | export class ProductGroupComponent implements OnInit { 18 | 19 | categories: Category[] = []; 20 | products: Product[] = []; 21 | model = new ProductGroupForm(); 22 | isLoadingProducts = false; 23 | 24 | constructor( 25 | private productItemsService: ProductItemsService, 26 | private toastyService: ToastyService) { } 27 | 28 | ngOnInit() { 29 | this.productItemsService.getCategories().subscribe( 30 | data => { 31 | this.categories = data; 32 | }, 33 | err => console.log("get error: ", err) 34 | ); 35 | } 36 | 37 | fetchProducts(categoryId?: number) { 38 | console.log(categoryId); 39 | 40 | this.products = []; 41 | 42 | if (categoryId === undefined || categoryId.toString() === "undefined") { 43 | this.toastyService.error({ 44 | title: "Error!", 45 | msg: "Please select a category.", 46 | theme: "bootstrap", 47 | showClose: true, 48 | timeout: 5000 49 | }); 50 | return; 51 | } 52 | 53 | this.isLoadingProducts = true; 54 | this.productItemsService.getProducts(categoryId).subscribe( 55 | data => { 56 | this.products = data; 57 | this.isLoadingProducts = false; 58 | }// , 59 | // err => { 60 | // console.log("get error: ", err); 61 | // this.isLoadingProducts = false; 62 | // } 63 | ); 64 | } 65 | 66 | submitForm(form: NgForm) { 67 | console.log(this.model); 68 | console.log(form.value); 69 | 70 | // todo: ... post this.model 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/app/injection-beyond-classes/injection-beyond-classes.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from "@angular/core"; 2 | import { CommonModule } from "@angular/common"; 3 | 4 | import { CarService } from "./car.service"; 5 | import { ThisModuleConfig, APP_CONFIG } from "./thismodule.config"; 6 | 7 | import { InjectionBeyondClassesRoutingModule } from "./injection-beyond-classes-routing.module"; 8 | import { TestProvidersComponent } from "./test-providers/test-providers.component"; 9 | import { HttpClient } from "@angular/common/http"; 10 | 11 | @NgModule({ 12 | imports: [ 13 | CommonModule, 14 | InjectionBeyondClassesRoutingModule 15 | ], 16 | declarations: [TestProvidersComponent], 17 | providers: [ 18 | // ------ useValue 19 | { provide: "API_BASE_HREF", useValue: "http://localhost:5000" }, 20 | { provide: "APP_BASE_HREF", useValue: document.location.pathname }, 21 | { provide: "IS_PROD", useValue: true }, 22 | { provide: "APIKey", useValue: "XYZ1234ABC" }, 23 | { provide: "Random", useValue: Math.random() }, 24 | { 25 | provide: "emailApiConfig", useValue: Object.freeze({ 26 | apiKey: "email-key", 27 | context: "registration" 28 | }) 29 | }, 30 | { provide: "languages", useValue: "en", multi: true }, 31 | { provide: "languages", useValue: "fa", multi: true }, 32 | { provide: APP_CONFIG, useValue: ThisModuleConfig }, 33 | // ------ useFactory 34 | { provide: "BASE_URL", useFactory: getBaseUrl }, 35 | { provide: "RandomFactory", useFactory: randomFactory }, 36 | { provide: "Car_Service", useFactory: carServiceFactory, deps: [HttpClient] }, 37 | // ------ useClass 38 | { provide: "Car_Service_Name1", useClass: CarService }, 39 | // ------ useExisting 40 | { provide: "Car_Service_Token2", useExisting: "Car_Service_Name1" }, 41 | ] 42 | }) 43 | export class InjectionBeyondClassesModule { } 44 | 45 | export function getBaseUrl() { 46 | return document.getElementsByTagName("base")[0].href; 47 | } 48 | 49 | export function randomFactory() { 50 | return Math.random(); 51 | } 52 | 53 | export function carServiceFactory(http: HttpClient) { 54 | return new CarService(http); 55 | } 56 | -------------------------------------------------------------------------------- /src/app/bread-crumb-sample/bread-crumb-sample-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { BreadCrumb } from "./../core/bread-crumb/bread-crumb"; 2 | import { Parent1Child1Child1Component } from "./parent1-child1-child1/parent1-child1-child1.component"; 3 | import { Parent1Child1Component } from "./parent1-child1/parent1-child1.component"; 4 | import { Parent1Component } from "./parent1/parent1.component"; 5 | import { NgModule } from "@angular/core"; 6 | import { Routes, RouterModule } from "@angular/router"; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: "breadCrumbTest", 11 | data: { 12 | breadcrumb: { label: "Parent1", glyphIcon: "glyphicon glyphicon-link" } as BreadCrumb, 13 | title: "Parent1 Title", 14 | metaTags: { 15 | description: "Page Description or some content here", 16 | keywords: "some, keywords, here, separated, by, a comma" 17 | } 18 | }, 19 | children: [ 20 | { 21 | path: "", component: Parent1Component 22 | }, 23 | { 24 | path: "Parent1Child1", 25 | data: { 26 | breadcrumb: { label: "Parent1-Child1", glyphIcon: "glyphicon glyphicon-envelope" } as BreadCrumb, 27 | title: "Parent1-Child1 Title", 28 | metaTags: { 29 | description: "Page Description or some content here", 30 | keywords: "some, keywords, here, separated, by, a comma" 31 | } 32 | }, 33 | children: [ 34 | { 35 | path: "", component: Parent1Child1Component 36 | }, 37 | { 38 | path: "Parent1Child1Child1", component: Parent1Child1Child1Component, 39 | data: { 40 | breadcrumb: { label: "Parent1-Child1 Child1", glyphIcon: "glyphicon glyphicon-pencil" } as BreadCrumb, 41 | title: "Parent1-Child1 Child1", 42 | metaTags: { 43 | description: "Page Description or some content here", 44 | keywords: "some, keywords, here, separated, by, a comma" 45 | } 46 | } 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | ]; 53 | 54 | @NgModule({ 55 | imports: [RouterModule.forChild(routes)], 56 | exports: [RouterModule] 57 | }) 58 | export class BreadCrumbSampleRoutingModule { } 59 | -------------------------------------------------------------------------------- /src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { HTTP_INTERCEPTORS } from "@angular/common/http"; 3 | import { APP_INITIALIZER, ErrorHandler, NgModule, Optional, SkipSelf } from "@angular/core"; 4 | import { RouterModule } from "@angular/router"; 5 | 6 | import { AppConfigService } from "./app-config.service"; 7 | import { AppErrorHandler } from "./app.error-handler"; 8 | import { BreadCrumbComponent } from "./bread-crumb/bread-crumb.component"; 9 | import { BrowserStorageService } from "./browser-storage.service"; 10 | import { LoaderInterceptorService } from "./interceptors/loader-interceptor.service"; 11 | import { RetryInterceptor } from "./interceptors/retry.interceptor"; 12 | import { ModalService } from "./modal.service"; 13 | import { SeoService } from "./seo-service"; 14 | import { WindowRefService } from "./window.service"; 15 | 16 | // import RxJs needed operators only once 17 | @NgModule({ 18 | imports: [CommonModule, RouterModule], 19 | exports: [ 20 | // components that are used in app.component.ts will be listed here. 21 | BreadCrumbComponent 22 | ], 23 | declarations: [ 24 | // components that are used in app.component.ts will be listed here. 25 | BreadCrumbComponent 26 | ], 27 | providers: [ 28 | // global singleton services of the whole app will be listed here. 29 | BrowserStorageService, 30 | AppConfigService, 31 | { 32 | provide: HTTP_INTERCEPTORS, 33 | useClass: LoaderInterceptorService, 34 | multi: true 35 | }, 36 | { 37 | provide: HTTP_INTERCEPTORS, 38 | useClass: RetryInterceptor, 39 | multi: true 40 | }, 41 | { 42 | provide: ErrorHandler, 43 | useClass: AppErrorHandler 44 | }, 45 | { 46 | provide: APP_INITIALIZER, 47 | useFactory: (config: AppConfigService) => () => config.loadClientConfig(), 48 | deps: [AppConfigService], 49 | multi: true 50 | }, 51 | ModalService, 52 | WindowRefService, 53 | SeoService 54 | ] 55 | }) 56 | export class CoreModule { 57 | constructor(@Optional() @SkipSelf() core: CoreModule) { 58 | if (core) { 59 | throw new Error("CoreModule should be imported ONLY in AppModule."); 60 | } 61 | } 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /Models/CategoriesDataSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AngularTemplateDrivenFormsLab.Models 4 | { 5 | /// 6 | /// منبع داده فرضی جهت سهولت دموی برنامه 7 | /// 8 | public static class CategoriesDataSource 9 | { 10 | private static readonly IList _cachedItems; 11 | static CategoriesDataSource() 12 | { 13 | _cachedItems = createCategoriesDataSource(); 14 | } 15 | 16 | public static IList Items 17 | { 18 | get { return _cachedItems; } 19 | } 20 | 21 | /// 22 | /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است 23 | /// 24 | private static IList createCategoriesDataSource() 25 | { 26 | return new List 27 | { 28 | new Category 29 | { 30 | CategoryId = 1, 31 | CategoryName = "گروه 1", 32 | Products = new List 33 | { 34 | new Product 35 | { 36 | ProductId = 1, 37 | ProductName = "محصول 1" 38 | }, 39 | new Product 40 | { 41 | ProductId = 2, 42 | ProductName = "محصول 2" 43 | } 44 | } 45 | }, 46 | new Category 47 | { 48 | CategoryId = 2, 49 | CategoryName = "گروه 2", 50 | Products = new List 51 | { 52 | new Product 53 | { 54 | ProductId = 3, 55 | ProductName = "محصول 3" 56 | }, 57 | new Product 58 | { 59 | ProductId = 4, 60 | ProductName = "محصول 4" 61 | } 62 | } 63 | } 64 | }; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Utils/IQueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace AngularTemplateDrivenFormsLab.Utils 7 | { 8 | public interface IPagedQueryModel 9 | { 10 | string SortBy { get; set; } 11 | bool IsAscending { get; set; } 12 | int Page { get; set; } 13 | int PageSize { get; set; } 14 | string FilterByColumn { get; set; } 15 | string FilterByValue { get; set; } 16 | } 17 | 18 | public static class IQueryableExtensions 19 | { 20 | public static IQueryable ApplyFiltering( 21 | this IQueryable query, 22 | IPagedQueryModel model, 23 | IDictionary>> columnsMap) 24 | { 25 | if (string.IsNullOrWhiteSpace(model.FilterByValue) || !columnsMap.ContainsKey(model.FilterByColumn)) 26 | { 27 | return query; 28 | } 29 | 30 | var func = columnsMap[model.FilterByColumn].Compile(); 31 | return query.Where(x => func(x).ToString() == model.FilterByValue); 32 | } 33 | 34 | public static IQueryable ApplyOrdering( 35 | this IQueryable query, 36 | IPagedQueryModel model, 37 | IDictionary>> columnsMap) 38 | { 39 | if (string.IsNullOrWhiteSpace(model.SortBy) || !columnsMap.ContainsKey(model.SortBy)) 40 | { 41 | return query; 42 | } 43 | 44 | if (model.IsAscending) 45 | { 46 | return query.OrderBy(columnsMap[model.SortBy]); 47 | } 48 | else 49 | { 50 | return query.OrderByDescending(columnsMap[model.SortBy]); 51 | } 52 | } 53 | 54 | public static IQueryable ApplyPaging( 55 | this IQueryable query, IPagedQueryModel model) 56 | { 57 | if (model.Page <= 0) 58 | { 59 | model.Page = 1; 60 | } 61 | 62 | if (model.PageSize <= 0) 63 | { 64 | model.PageSize = 10; 65 | } 66 | 67 | return query.Skip((model.Page - 1) * model.PageSize).Take(model.PageSize); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app/modal-bootstrap-dialogs/modal-dialog-test/modal-dialog-test.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, TemplateRef } from "@angular/core"; 2 | import { BsModalRef, BsModalService } from "ngx-bootstrap"; 3 | 4 | import { ModalService } from "./../../core/modal.service"; 5 | import { ConfirmModalComponent } from "./../../shared/confirm-modal/confirm-modal.component"; 6 | 7 | @Component({ 8 | selector: "app-modal-dialog-test", 9 | templateUrl: "./modal-dialog-test.component.html", 10 | styleUrls: ["./modal-dialog-test.component.css"] 11 | }) 12 | export class ModalDialogTestComponent implements OnInit { 13 | 14 | modalRef: BsModalRef | null = null; 15 | confirmResult: string | null = null; 16 | 17 | private filesList: FileList | null = null; 18 | private fileInput2: HTMLInputElement | null = null; 19 | 20 | constructor( 21 | private bsModalService: BsModalService, 22 | private modalService: ModalService) { } 23 | 24 | openModal(template: TemplateRef) { 25 | this.modalRef = this.bsModalService.show( 26 | template, 27 | { 28 | animated: true, keyboard: true, backdrop: true, ignoreBackdropClick: false 29 | }); 30 | } 31 | 32 | closeModal() { 33 | if (this.modalRef) { 34 | this.modalRef.hide(); 35 | } 36 | } 37 | 38 | ngOnInit() { 39 | } 40 | 41 | deleteRecord() { 42 | this.confirmResult = ""; 43 | this.modalService.show( 44 | ConfirmModalComponent, 45 | { 46 | title: "Confirm", message: "Do you want to delete this record?" 47 | }, 48 | { 49 | animated: true, keyboard: true, backdrop: true, ignoreBackdropClick: false 50 | }).then(confirmed => { 51 | if (confirmed) { 52 | this.confirmResult = "Deleted!"; 53 | } else { 54 | this.confirmResult = "Canceled!"; 55 | } 56 | }); 57 | } 58 | 59 | fileChange(event: any) { 60 | this.filesList = event.target.files; 61 | this.fileInput2 = event.target as HTMLInputElement; 62 | console.log("fileChange() -> filesList", this.filesList); 63 | console.log("fileChange() -> this.fileInput2.files", this.fileInput2.files); 64 | } 65 | 66 | submitUpload() { 67 | console.log("filesList", this.filesList); 68 | if (this.fileInput2) { 69 | console.log("fileInput2", this.fileInput2.files); 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-template-driven-forms-lab", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e", 12 | "_0-npm-restore": "npm install", 13 | "_0-dotnet-restore": "dotnet restore", 14 | "_1-ng-build-dev": "ng build --watch", 15 | "_1-ng-build-prod": "ng build --prod --watch", 16 | "_2-dotnet-run": "dotnet watch run", 17 | "_2-ng-serve-proxy": "ng serve --proxy-config proxy.config.json -o" 18 | }, 19 | "private": true, 20 | "dependencies": { 21 | "@angular/animations": "^6.0.0", 22 | "@angular/common": "^6.0.0", 23 | "@angular/compiler": "^6.0.0", 24 | "@angular/core": "^6.0.0", 25 | "@angular/forms": "^6.0.0", 26 | "@angular/http": "^6.0.0", 27 | "@angular/platform-browser": "^6.0.0", 28 | "@angular/platform-browser-dynamic": "^6.0.0", 29 | "@angular/router": "^6.0.0", 30 | "bootstrap": "3.3.7", 31 | "core-js": "^2.5.5", 32 | "jquery": "^3.3.1", 33 | "jssha": "^2.3.1", 34 | "moment-jalaali": "^0.7.2", 35 | "ng2-file-upload": "^1.3.0", 36 | "ng2-slim-loading-bar": "^4.0.0", 37 | "ng2-toasty": "^4.0.3", 38 | "ngx-bootstrap": "^2.0.3", 39 | "rxjs": "^6.1.0", 40 | "rxjs-compat": "^6.1.0", 41 | "stacktrace-js": "^2.0.0", 42 | "zone.js": "^0.8.26" 43 | }, 44 | "devDependencies": { 45 | "@angular-devkit/build-angular": "~0.6.0", 46 | "@angular/cli": "^6.0.0", 47 | "@angular/compiler-cli": "^6.0.0", 48 | "@angular/language-service": "^6.0.0", 49 | "@types/jasmine": "2.8.7", 50 | "@types/jquery": "^3.3.1", 51 | "@types/jssha": "0.0.29", 52 | "@types/moment-jalaali": "^0.7.4", 53 | "@types/node": "~10.0.4", 54 | "@types/stacktrace-js": "0.0.32", 55 | "codelyzer": "~4.3.0", 56 | "jasmine-core": "~3.1.0", 57 | "jasmine-spec-reporter": "~4.2.1", 58 | "karma": "~2.0.0", 59 | "karma-chrome-launcher": "~2.2.0", 60 | "karma-cli": "~1.0.1", 61 | "karma-coverage-istanbul-reporter": "^1.4.2", 62 | "karma-jasmine": "~1.1.1", 63 | "karma-jasmine-html-reporter": "^1.0.0", 64 | "protractor": "~5.3.1", 65 | "rxjs-tslint": "^0.1.3", 66 | "ts-node": "~6.0.2", 67 | "tslint": "~5.10.0", 68 | "typescript": "~2.7.2" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/app/upload-file/upload-file-with-progress-bar/upload-file-with-progress-bar.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Upload File With ProgressBar Using HttpClient

3 |
4 |
5 | 6 | 8 |
9 |
10 | description is required. 11 |
12 |
13 |
14 | 15 |
16 | 17 | 19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 51 | 52 | 53 |
EventStatus
Elapsed time{{uploadTimeElapsed | number:'.1'}} second(s)
Remaining time{{uploadTimeRemaining | number:'.1'}} second(s)
Upload speed{{uploadSpeed | number:'.3'}} MB/s
Queue progress 45 |
48 | {{queueProgress}}% 49 |
50 |
54 |
55 | 56 | 57 |
58 |
59 | -------------------------------------------------------------------------------- /Startup.cs: -------------------------------------------------------------------------------- 1 | using AngularTemplateDrivenFormsLab.Utils; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.AspNetCore.StaticFiles; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | 10 | namespace AngularTemplateDrivenFormsLab 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.AddAntiforgery(x => x.HeaderName = "X-XSRF-TOKEN"); 24 | services.AddMvc(options => 25 | { 26 | options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); 27 | }); 28 | } 29 | 30 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 31 | { 32 | if (env.IsDevelopment()) 33 | { 34 | app.UseDeveloperExceptionPage(); 35 | } 36 | else 37 | { 38 | app.UseExceptionHandler("/Home/Error"); 39 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 40 | app.UseHsts(); 41 | } 42 | app.UseHttpsRedirection(); 43 | 44 | app.UseContentSecurityPolicy(); 45 | app.UseAngularAntiforgeryToken(); 46 | app.UseAngularPushStateRouting(); // or just use `routes.MapSpaFallbackRoute` after the `default` route. 47 | 48 | app.UseRouting(); 49 | 50 | app.UseAuthorization(); 51 | 52 | app.UseEndpoints(endpoints => 53 | { 54 | endpoints.MapControllerRoute( 55 | name: "default", 56 | pattern: "{controller=Home}/{action=Index}/{id?}"); 57 | }); 58 | 59 | var provider = new FileExtensionContentTypeProvider(); 60 | app.UseStaticFiles(new StaticFileOptions 61 | { 62 | ContentTypeProvider = provider 63 | }); 64 | app.UseDefaultFiles(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/app/autocomplete/autocomplete-sample/autocomplete-sample.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import { debounceTime, distinctUntilChanged, flatMap, switchMap } from "rxjs/operators"; 3 | import { Subject } from "rxjs"; 4 | 5 | import { SearchService } from "./../search.service"; 6 | 7 | @Component({ 8 | selector: "app-autocomplete-sample", 9 | templateUrl: "./autocomplete-sample.component.html", 10 | styleUrls: ["./autocomplete-sample.component.css"] 11 | }) 12 | export class AutocompleteSampleComponent implements OnInit { 13 | 14 | countries1: string[] = []; 15 | countries2: string[] = []; 16 | countries3: string[] = []; 17 | 18 | private model1Changed: Subject = new Subject(); 19 | private model2Changed: Subject = new Subject(); 20 | private model3Changed: Subject = new Subject(); 21 | private dueTime = 300; 22 | 23 | constructor(private searchService: SearchService) { } 24 | 25 | ngOnInit() { 26 | this.model1Changed 27 | .pipe( 28 | debounceTime(this.dueTime), 29 | distinctUntilChanged(), 30 | flatMap(inputValue => { 31 | console.log("debounced input value1", inputValue); 32 | return this.searchService.searchCountries(inputValue); 33 | }) 34 | ) 35 | .subscribe(countries => { 36 | this.countries1 = countries; 37 | }); 38 | 39 | this.model2Changed 40 | .pipe( 41 | debounceTime(this.dueTime), 42 | distinctUntilChanged(), 43 | switchMap(inputValue => { 44 | console.log("debounced input value2", inputValue); 45 | return this.searchService.searchCountries(inputValue); 46 | }) 47 | ) 48 | .subscribe(countries => { 49 | this.countries2 = countries; 50 | }); 51 | 52 | this.model3Changed 53 | .pipe( 54 | debounceTime(this.dueTime), 55 | distinctUntilChanged() 56 | ) 57 | .subscribe(inputValue => { 58 | console.log("debounced input value3", inputValue); 59 | this.searchService.searchCountries(inputValue).subscribe(countries => { 60 | this.countries3 = countries; 61 | }); 62 | }); 63 | } 64 | 65 | onSearch1Change(value: string) { 66 | this.model1Changed.next(value); 67 | } 68 | 69 | onSearch2Change(value: string) { 70 | this.model2Changed.next(value); 71 | } 72 | 73 | onSearch3Change(value: string) { 74 | this.model3Changed.next(value); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/app/model-state-validation/model-state-validation-test/model-state-validation-test.component.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorResponse } from "@angular/common/http"; 2 | import { Component, OnInit } from "@angular/core"; 3 | import { NgForm } from "@angular/forms"; 4 | 5 | import { Movie } from "./../movie"; 6 | import { MovieService } from "./../movie.service"; 7 | 8 | @Component({ 9 | selector: "app-model-state-validation-test", 10 | templateUrl: "./model-state-validation-test.component.html", 11 | styleUrls: ["./model-state-validation-test.component.css"] 12 | }) 13 | export class ModelStateValidationTestComponent implements OnInit { 14 | 15 | model = new Movie("", "", 0, ""); 16 | successfulSave = false; 17 | errors: string[] = []; 18 | 19 | constructor(private movieService: MovieService) { } 20 | 21 | ngOnInit() { 22 | } 23 | 24 | submitForm(form: NgForm) { 25 | console.log(form); 26 | 27 | this.errors = []; 28 | this.movieService.postMovieForm(this.model).subscribe( 29 | (data: Movie) => { 30 | console.log("Saved data", data); 31 | this.successfulSave = true; 32 | }, 33 | (responseError: HttpErrorResponse) => { 34 | this.successfulSave = false; 35 | console.log("Response Error", responseError); 36 | this.processModelStateErrors(form, responseError); 37 | }); 38 | } 39 | 40 | processModelStateErrors(form: NgForm, responseError: HttpErrorResponse) { 41 | if (responseError.status === 400) { 42 | const modelStateErrors = responseError.error; 43 | for (const fieldName in modelStateErrors) { 44 | if (modelStateErrors.hasOwnProperty(fieldName)) { 45 | const modelStateError = modelStateErrors[fieldName]; 46 | const control = form.controls[fieldName] || form.controls[this.lowerCaseFirstLetter(fieldName)]; 47 | console.log({ fieldName: fieldName, modelStateError: modelStateError, control: control }); 48 | if (control) { 49 | // integrate into Angular's validation 50 | control.setErrors({ 51 | modelStateError: { error: modelStateError } 52 | }); 53 | } else { 54 | // for cross field validations -> show the validation error at the top of the screen 55 | this.errors.push(modelStateError); 56 | } 57 | } 58 | } 59 | } else { 60 | this.errors.push("something went wrong!"); 61 | } 62 | } 63 | 64 | lowerCaseFirstLetter(data: string): string { 65 | return data.charAt(0).toLowerCase() + data.slice(1); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/app/core/bread-crumb/bread-crumb.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit, ViewEncapsulation } from "@angular/core"; 2 | import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; 3 | import { Observable } from "rxjs"; 4 | import { distinctUntilChanged, filter, map } from "rxjs/operators"; 5 | 6 | import { BreadCrumb } from "./bread-crumb"; 7 | 8 | @Component({ 9 | selector: "app-bread-crumb", 10 | templateUrl: "./bread-crumb.component.html", 11 | styleUrls: ["./bread-crumb.component.css"], 12 | encapsulation: ViewEncapsulation.None 13 | }) 14 | export class BreadCrumbComponent implements OnInit { 15 | 16 | breadcrumbs$: Observable | null = null; 17 | 18 | @Input() homeLabel: string | null = null; 19 | @Input() homeGlyphIcon: string | null = null; 20 | 21 | constructor(private activatedRoute: ActivatedRoute, private router: Router) { } 22 | 23 | ngOnInit() { 24 | this.breadcrumbs$ = this.router.events 25 | .pipe( 26 | filter(event => event instanceof NavigationEnd), 27 | distinctUntilChanged(), 28 | map(event => event ? this.buildBreadCrumbs(this.activatedRoute.root) : [])); 29 | } 30 | 31 | buildBreadCrumbs(route: ActivatedRoute, url: string = "", breadcrumbs: Array = []): Array { 32 | const routeDataBreadCrumbKey = "breadcrumb"; 33 | const routeConfig = route.routeConfig; 34 | 35 | const path = routeConfig && routeConfig.path !== undefined ? routeConfig.path : ""; 36 | const nextUrl = `${url}${path}/`; 37 | 38 | let breadcrumb: BreadCrumb = { 39 | label: path, 40 | url: nextUrl, 41 | glyphIcon: "" 42 | }; 43 | 44 | if (url === "") { 45 | breadcrumb = { 46 | label: this.homeLabel ? this.homeLabel : "", 47 | url: nextUrl, 48 | glyphIcon: this.homeGlyphIcon ? this.homeGlyphIcon : "" 49 | } 50 | } else if (routeConfig && routeConfig.data !== undefined) { 51 | const definedBreadcrumb = routeConfig.data[routeDataBreadCrumbKey] as BreadCrumb; 52 | if (definedBreadcrumb !== undefined) { 53 | if (definedBreadcrumb.url === undefined) { 54 | definedBreadcrumb.url = nextUrl; 55 | } 56 | breadcrumb = definedBreadcrumb; 57 | } 58 | } 59 | 60 | console.log("breadcrumb", { path: path, breadcrumb: breadcrumb, route: route }); 61 | const newBreadcrumbs = [...breadcrumbs, breadcrumb]; 62 | if (route.firstChild) { 63 | return this.buildBreadCrumbs(route.firstChild, nextUrl, newBreadcrumbs); 64 | } 65 | return newBreadcrumbs; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/app/simple-grid/products-list.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http"; 2 | import { Injectable } from "@angular/core"; 3 | import { Observable, throwError as observableThrowError } from "rxjs"; 4 | import { catchError, map } from "rxjs/operators"; 5 | 6 | import { AppProduct } from "./app-product"; 7 | import { PagedQueryModel } from "./paged-query-model"; 8 | import { PagedQueryResult } from "./paged-query-result"; 9 | 10 | 11 | @Injectable() 12 | export class ProductsListService { 13 | private baseUrl = "api/Product"; 14 | 15 | constructor(private http: HttpClient) { } 16 | 17 | getPagedProductsList( 18 | queryModel: PagedQueryModel 19 | ): Observable> { 20 | return this.http 21 | .get>( 22 | `${this.baseUrl}/GetPagedProducts?${this.toQueryString(queryModel)}` 23 | ).pipe( 24 | map(result => { 25 | return >{ 26 | totalItems: result.totalItems, 27 | items: result.items 28 | }; 29 | })); 30 | } 31 | 32 | toQueryString(obj: any): string { 33 | const parts = []; 34 | for (const key in obj) { 35 | if (obj.hasOwnProperty(key)) { 36 | const value = obj[key]; 37 | if (value !== null && value !== undefined) { 38 | parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); 39 | } 40 | } 41 | } 42 | return parts.join("&"); 43 | } 44 | 45 | private handleError(error: HttpErrorResponse): Observable { 46 | console.error("observable error: ", error); 47 | return observableThrowError(error); 48 | } 49 | 50 | addAppProduct(item: AppProduct): Observable { 51 | const header = new HttpHeaders({ "Content-Type": "application/json" }); 52 | return this.http 53 | .post(`${this.baseUrl}/AddProduct`, JSON.stringify(item), { 54 | headers: header 55 | }).pipe( 56 | map(response => response || {}), 57 | catchError(this.handleError)); 58 | } 59 | 60 | updateAppProduct(id: number, item: AppProduct): Observable { 61 | const header = new HttpHeaders({ "Content-Type": "application/json" }); 62 | return this.http 63 | .put( 64 | `${this.baseUrl}/UpdateProduct/${id}`, 65 | JSON.stringify(item), 66 | { headers: header } 67 | ).pipe( 68 | map(response => response || {}), 69 | catchError(this.handleError)); 70 | } 71 | 72 | deleteAppProduct(id: number): Observable { 73 | return this.http.delete(`${this.baseUrl}/DeleteProduct/${id}`); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | import "core-js/es6/reflect"; 17 | import "core-js/es7/reflect"; 18 | import "zone.js/dist/zone"; 19 | 20 | 21 | /*************************************************************************************************** 22 | * BROWSER POLYFILLS 23 | */ 24 | 25 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 26 | // import 'core-js/es6/symbol'; 27 | // import 'core-js/es6/object'; 28 | // import 'core-js/es6/function'; 29 | // import 'core-js/es6/parse-int'; 30 | // import 'core-js/es6/parse-float'; 31 | // import 'core-js/es6/number'; 32 | // import 'core-js/es6/math'; 33 | // import 'core-js/es6/string'; 34 | // import 'core-js/es6/date'; 35 | // import 'core-js/es6/array'; 36 | // import 'core-js/es6/regexp'; 37 | // import 'core-js/es6/map'; 38 | // import 'core-js/es6/weak-map'; 39 | // import 'core-js/es6/set'; 40 | 41 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 42 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 43 | 44 | /** IE10 and IE11 requires the following to support `@angular/animation`. */ 45 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 46 | 47 | 48 | /** Evergreen browsers require these. **/ 49 | 50 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/ 51 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 52 | 53 | 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by Angular itself. 57 | */ 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /src/app/core/app.error-handler.ts: -------------------------------------------------------------------------------- 1 | import { LocationStrategy, PathLocationStrategy } from "@angular/common"; 2 | import { HttpErrorResponse } from "@angular/common/http"; 3 | import { ErrorHandler, Inject, NgZone } from "@angular/core"; 4 | import { ToastOptions, ToastyService } from "ng2-toasty"; 5 | import * as StackTrace from "stacktrace-js"; 6 | 7 | export class AppErrorHandler extends ErrorHandler { 8 | constructor( 9 | @Inject(NgZone) private ngZone: NgZone, 10 | @Inject(ToastyService) private toastyService: ToastyService, 11 | @Inject(LocationStrategy) private locationProvider: LocationStrategy 12 | ) { 13 | super(); 14 | } 15 | 16 | handleError(error: any): void { 17 | console.log("Error:", error); 18 | 19 | const url = this.locationProvider instanceof PathLocationStrategy ? this.locationProvider.path() : ""; 20 | const message = this.getError(error); 21 | this.ngZone.run(() => { 22 | this.toastyService.error( 23 | { 24 | title: "Error!", 25 | msg: `URL:${url} \n ERROR:${message}`, 26 | theme: "bootstrap", 27 | showClose: true, 28 | timeout: 15000 29 | } 30 | ); 31 | }); 32 | 33 | super.handleError(error); 34 | } 35 | 36 | getError(error: any): string { 37 | const date = new Date().toISOString(); 38 | 39 | this.getStackTrace(error).then(stackTrace => { 40 | // TODO: log it on the server --> { message, url, stackTrace } 41 | console.log("StackTrace", stackTrace); 42 | }).catch(err => { 43 | console.log("StackTrace Error", err); 44 | }); 45 | 46 | if (error instanceof HttpErrorResponse) { 47 | return `HTTP error occurred at ${date}, ${error.message}, ${(error).status}, ${error.statusText}, 48 | ${this.gerErrorDetails(error.error)}`; 49 | } 50 | 51 | if (error instanceof TypeError) { 52 | return `Type error occurred at ${date}, message - ${error.message}`; 53 | } 54 | 55 | if (error instanceof Error) { 56 | return `General error occurred at ${date}, message - ${error.message}`; 57 | } 58 | 59 | return `Some magical error occurred at ${date}, error - ${error}`; 60 | } 61 | 62 | gerErrorDetails(error: any): string { 63 | const errors: string[] = []; 64 | if (typeof error === "object" && error.constructor === Object) { 65 | for (const fieldName in error) { 66 | if (error.hasOwnProperty(fieldName)) { 67 | const modelStateError = error[fieldName]; 68 | errors.push(`${fieldName}: ${modelStateError}`); 69 | } 70 | } 71 | } else { 72 | errors.push(error.toString()); 73 | } 74 | return errors.join(", "); 75 | } 76 | 77 | getStackTrace(error: any): Promise { 78 | return StackTrace.fromError(error) 79 | .then(stackFrames => stackFrames.splice(0, 20).map(stackFrame => stackFrame.toString()).join("\n")); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/app/timers/using-timers/using-timers.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import { interval, NEVER, Subject, Subscription, timer } from "rxjs"; 3 | import { finalize, map, pluck, switchMap, take, takeUntil, timeInterval } from "rxjs/operators"; 4 | 5 | @Component({ 6 | selector: "app-using-timers", 7 | templateUrl: "./using-timers.component.html", 8 | styleUrls: ["./using-timers.component.css"] 9 | }) 10 | export class UsingTimersComponent implements OnInit { 11 | 12 | private intervalSubscription: Subscription | null = null; 13 | intervalValue = 0; 14 | countdown = 0; 15 | 16 | tick = 0; 17 | pauser = new Subject(); 18 | tickerSource = new Subject(); 19 | 20 | constructor() { } 21 | 22 | ngOnInit() { 23 | this.testTimeInterval(); 24 | this.testTimeIntervalWithPluck(); 25 | } 26 | 27 | startInterval() { 28 | const intervalTimer = interval(1000); 29 | this.intervalSubscription = intervalTimer 30 | .pipe( 31 | finalize(() => console.log("All done!")) 32 | ) 33 | .subscribe(i => this.intervalValue += i); 34 | } 35 | 36 | stopInterval() { 37 | if (this.intervalSubscription) { 38 | this.intervalSubscription.unsubscribe(); 39 | } 40 | } 41 | 42 | startCountdownTimer() { 43 | const intervalTimeValue = 1000; 44 | const duration = 10 * 1000; 45 | const stream$ = timer(0, intervalTimeValue) 46 | .pipe( 47 | finalize(() => console.log("All done!")), 48 | takeUntil(timer(duration + intervalTimeValue)), 49 | map(value => duration - value * intervalTimeValue)); 50 | stream$.subscribe(value => this.countdown = value); 51 | } 52 | 53 | testTimeInterval() { 54 | const source = timer(0, 1000) 55 | .pipe( 56 | timeInterval(), 57 | map(x => x.value + ":" + x.interval), 58 | take(5)); 59 | 60 | source.subscribe( 61 | x => console.log("Next timeInterval: " + x), 62 | err => console.log("Error: " + err), 63 | () => console.log("Completed") 64 | ); 65 | } 66 | 67 | testTimeIntervalWithPluck() { 68 | const source = timer(0, 1000). 69 | pipe( 70 | timeInterval(), 71 | pluck("interval"), 72 | take(5)); 73 | 74 | source.subscribe( 75 | x => console.log("Next interval: " + x), 76 | err => console.log("Error: " + err), 77 | () => console.log("Completed") 78 | ); 79 | } 80 | 81 | startTicker() { 82 | timer(0, 1000) 83 | .subscribe(this.tickerSource); 84 | 85 | this.pauser 86 | .pipe( 87 | switchMap(paused => paused ? NEVER : this.tickerSource) 88 | ). 89 | subscribe((t: any) => this.tickerFunc(t)); 90 | 91 | this.pauser.next(false); // resume 92 | } 93 | 94 | tickerFunc(tick: number) { 95 | this.tick = tick; 96 | } 97 | 98 | pauseTicker() { 99 | this.pauser.next(true); 100 | } 101 | 102 | resumeTicker() { 103 | this.pauser.next(false); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/app/display-pdf/view-pdf/view-pdf.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from "@angular/core"; 2 | import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; 3 | 4 | import { WindowRefService } from "./../../core/window.service"; 5 | import { DownloadPdfDataService } from "./../download-pdf-data.service"; 6 | 7 | @Component({ 8 | selector: "app-view-pdf", 9 | templateUrl: "./view-pdf.component.html", 10 | styleUrls: ["./view-pdf.component.css"] 11 | }) 12 | export class ViewPdfComponent implements OnInit { 13 | 14 | private nativeWindow: Window | null = null; 15 | private pdfBlobUrl: string | null = null; 16 | sanitizedPdfBlobResourceUrl: SafeResourceUrl | null = null; 17 | 18 | constructor(private downloadService: DownloadPdfDataService, 19 | private windowRefService: WindowRefService, private sanitizer: DomSanitizer) { } 20 | 21 | ngOnInit() { 22 | this.nativeWindow = this.windowRefService.nativeWindow; 23 | this.downloadService.getReport().subscribe(pdfDataBlob => { 24 | if (!this.nativeWindow) { 25 | throw new Error("this.nativeWindow is null"); 26 | } 27 | console.log("pdfDataBlob", pdfDataBlob); 28 | const urlCreator = this.nativeWindow.URL; 29 | this.pdfBlobUrl = urlCreator.createObjectURL(pdfDataBlob); 30 | console.log("pdfBlobUrl", this.pdfBlobUrl); 31 | this.sanitizedPdfBlobResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.pdfBlobUrl); 32 | }); 33 | } 34 | 35 | printPdf() { 36 | if (!this.pdfBlobUrl) { 37 | throw new Error("this.pdfBlobUrl is null"); 38 | } 39 | const iframe = document.createElement("iframe"); 40 | iframe.style.display = "none"; 41 | iframe.src = this.pdfBlobUrl; 42 | document.body.appendChild(iframe); 43 | if (iframe.contentWindow) { 44 | iframe.contentWindow.print(); 45 | } 46 | } 47 | 48 | showPdf() { 49 | if (!this.pdfBlobUrl) { 50 | throw new Error("this.pdfBlobUrl is null"); 51 | } 52 | if (!this.nativeWindow) { 53 | throw new Error("this.nativeWindow is null"); 54 | } 55 | this.nativeWindow.open(this.pdfBlobUrl); 56 | } 57 | 58 | downloadPdf() { 59 | if (!this.pdfBlobUrl) { 60 | throw new Error("this.pdfBlobUrl is null"); 61 | } 62 | const fileName = "test.pdf"; 63 | const anchor = document.createElement("a"); 64 | anchor.style.display = "none"; 65 | anchor.href = this.pdfBlobUrl; 66 | anchor.download = fileName; 67 | document.body.appendChild(anchor); 68 | anchor.click(); 69 | } 70 | 71 | // Tips: 72 | // 1- How do I enable/disable the built-in pdf viewer of FireFox 73 | // https://support.mozilla.org/en-US/kb/disable-built-pdf-viewer-and-use-another-viewer 74 | // 2- How to configure browsers to use the Adobe PDF plug-in to open PDF files 75 | // https://helpx.adobe.com/acrobat/kb/pdf-browser-plugin-configuration.html 76 | // https://helpx.adobe.com/acrobat/using/display-pdf-in-browser.html 77 | // 3- Microsoft Edge is gaining new PDF reader features within the Windows 10 Fall Creator’s Update (version 1709). 78 | } 79 | -------------------------------------------------------------------------------- /src/app/upload-file/ng2-file-upload-test/ng2-file-upload-test.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Support Form(ng2-file-upload)

3 |
4 |
5 | 6 | 8 |
9 |
10 | description is required. 11 |
12 |
13 |
14 | 15 |
16 | 17 | 19 |
20 | 21 |
22 |

Upload queue

23 |

Queue length: {{ fileUploader?.queue?.length }}

24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 45 | 50 | 55 | 56 | 57 |
NameSizeProgressStatusActions
37 | {{ item?.file?.name }} 38 | {{ item?.file?.size/1024/1024 | number:'.2' }} MB 41 |
42 |
43 |
44 |
46 | 47 | 48 | 49 | 51 | 54 |
58 | 59 |
60 |
61 | Queue progress: 62 |
63 |
64 |
65 |
66 | 70 |
71 |
72 | 74 |
75 |
76 |
77 | -------------------------------------------------------------------------------- /src/app/model-state-validation/model-state-validation-test/model-state-validation-test.component.html: -------------------------------------------------------------------------------- 1 |

2 | Displaying ASP.NET Core ModelState validation errors 3 |

4 |
5 | 12 | 15 | 16 |
17 | 18 | 20 | 21 |
22 |
23 | 24 | 26 | 27 |
28 |
29 | 30 | 32 | 33 |
34 |
35 | 36 | 38 | 39 |
40 | 41 | 42 |
43 | 44 | 45 |
46 |
47 | This field is required. 48 |
49 |
50 | This field should be minimum {{ctrl.errors.minlength.requiredLength}} characters. 51 |
52 |
53 | This field should be max {{ctrl.errors.maxlength.requiredLength}} characters. 54 |
55 |
56 | This field's pattern: {{ctrl.errors.pattern.requiredPattern}} 57 |
58 |
59 | {{ctrl.errors.modelStateError.error}} 60 |
61 |
62 |
63 | --------------------------------------------------------------------------------