├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── gulpfile.js
├── license
├── package.json
├── src
├── app
│ ├── about
│ │ ├── about.component.html
│ │ ├── about.component.scss
│ │ ├── about.component.spec.ts
│ │ └── about.component.ts
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ ├── app.module.ts
│ ├── bar-chart
│ │ ├── bar-chart.component.html
│ │ └── bar-chart.component.ts
│ ├── data-service.ts
│ ├── grid-app
│ │ ├── grid-app.component.html
│ │ └── grid-app.component.ts
│ ├── home
│ │ ├── dashboard
│ │ │ ├── dashboard.component.html
│ │ │ └── dashboard.component.ts
│ │ ├── home.component.html
│ │ ├── home.component.scss
│ │ ├── home.component.spec.ts
│ │ ├── home.component.ts
│ │ └── input
│ │ │ ├── input.component.html
│ │ │ └── input.component.ts
│ ├── main.ts
│ └── statement
│ │ ├── statement.component.html
│ │ └── statement.component.ts
├── assets
│ ├── .gitkeep
│ ├── index.scss
│ └── styles.scss
├── favicon.ico
├── index.html
├── main.ts
└── styles.scss
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://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 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Loan Calculator Showcase Sample in JavaScript
2 |
3 | The Loan Calculator Showcase web application sample is a practical demonstration of a loan calculator built using JavaScript and Syncfusion's Essential JS 2 library. This showcase provides users with a user-friendly interface to calculate and visualize various aspects of loan repayment, making it a valuable tool for both individuals and financial institution.
4 |
5 | ## Installation
6 |
7 | To install the application dependencies, use the following command:
8 |
9 | ```sh
10 | npm install
11 | ```
12 |
13 | ## Build the application
14 |
15 | To Build the application, use the below command,
16 |
17 | ```sh
18 | ng build
19 | ```
20 |
21 | ## Code scaffolding
22 |
23 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
24 |
25 |
26 | ## Run the application
27 |
28 | To run the sample in the browser with live reload, use the following command:
29 |
30 | ```sh
31 | ng serve
32 | ```
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ej2-angular-loan-calculator": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:component": {
10 | "style": "scss"
11 | }
12 | },
13 | "root": "",
14 | "sourceRoot": "src",
15 | "prefix": "app",
16 | "architect": {
17 | "build": {
18 | "builder": "@angular-devkit/build-angular:browser",
19 | "options": {
20 | "outputPath": "dist/",
21 | "index": "src/index.html",
22 | "main": "src/main.ts",
23 | "polyfills": [
24 | "zone.js"
25 | ],
26 | "tsConfig": "tsconfig.app.json",
27 | "inlineStyleLanguage": "scss",
28 | "assets": [
29 | "src/favicon.ico",
30 | "src/assets"
31 | ],
32 | "styles": [
33 | "src/styles.scss"
34 | ],
35 | "scripts": [],
36 | "stylePreprocessorOptions": {
37 | "includePaths": [
38 | "node_modules/@syncfusion"
39 | ]
40 | }
41 | },
42 | "configurations": {
43 | "production": {
44 | "budgets": [
45 | {
46 | "type": "initial",
47 | "maximumWarning": "500kb",
48 | "maximumError": "5mb"
49 | },
50 | {
51 | "type": "anyComponentStyle",
52 | "maximumWarning": "2kb",
53 | "maximumError": "4kb"
54 | }
55 | ],
56 | "outputHashing": "all"
57 | },
58 | "development": {
59 | "buildOptimizer": false,
60 | "optimization": false,
61 | "vendorChunk": true,
62 | "extractLicenses": false,
63 | "sourceMap": true,
64 | "namedChunks": true
65 | }
66 | },
67 | "defaultConfiguration": "production"
68 | },
69 | "serve": {
70 | "builder": "@angular-devkit/build-angular:dev-server",
71 | "configurations": {
72 | "production": {
73 | "browserTarget": "ej2-angular-loan-calculator:build:production"
74 | },
75 | "development": {
76 | "browserTarget": "ej2-angular-loan-calculator:build:development"
77 | }
78 | },
79 | "defaultConfiguration": "development"
80 | },
81 | "extract-i18n": {
82 | "builder": "@angular-devkit/build-angular:extract-i18n",
83 | "options": {
84 | "browserTarget": "ej2-angular-loan-calculator:build"
85 | }
86 | },
87 | "test": {
88 | "builder": "@angular-devkit/build-angular:karma",
89 | "options": {
90 | "polyfills": [
91 | "zone.js",
92 | "zone.js/testing"
93 | ],
94 | "tsConfig": "tsconfig.spec.json",
95 | "inlineStyleLanguage": "scss",
96 | "assets": [
97 | "src/favicon.ico",
98 | "src/assets"
99 | ],
100 | "styles": [
101 | "src/styles.scss"
102 | ],
103 | "scripts": []
104 | }
105 | }
106 | }
107 | }
108 | },
109 | "cli": {
110 | "analytics": "b84261b9-4f3d-4ec6-b1c1-9d047d7b2c3e"
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | Essential JS 2 library is available under the Syncfusion Essential Studio program, and can be licensed either under the Syncfusion Community License Program or the Syncfusion commercial license.
2 |
3 | To be qualified for the Syncfusion Community License Program you must have a gross revenue of less than one (1) million U.S. dollars ($1,000,000.00 USD) per year and have less than five (5) developers in your organization, and agree to be bound by Syncfusion’s terms and conditions.
4 |
5 | Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options.
6 |
7 | Under no circumstances can you use this product without (1) either a Community License or a commercial license and (2) without agreeing and abiding by Syncfusion’s license containing all terms and conditions.
8 |
9 | The Syncfusion license that contains the terms and conditions can be found at
10 | https://www.syncfusion.com/content/downloads/syncfusion_license.pdf
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@syncfusion/ej2-angular-loan-calculator",
3 | "version": "0.0.1",
4 | "description": "Essential JS 2 - Loan Calculator Application",
5 | "author": "Syncfusion Inc.",
6 | "license": "SEE LICENSE IN license",
7 | "scripts": {
8 | "ng": "ng",
9 | "start": "ng serve",
10 | "build": "ng build",
11 | "watch": "ng build --watch --configuration development",
12 | "test": "ng test",
13 | "ci-publish": "gulp publish-sample"
14 | },
15 | "private": true,
16 | "dependencies": {
17 | "@angular/animations": "^15.2.0",
18 | "@angular/common": "^15.2.0",
19 | "@angular/compiler": "^15.2.0",
20 | "@angular/core": "^15.2.0",
21 | "@angular/forms": "^15.2.0",
22 | "@angular/platform-browser": "^15.2.0",
23 | "@angular/platform-browser-dynamic": "^15.2.0",
24 | "@angular/router": "^15.2.0",
25 | "@syncfusion/ej2-angular-base": "*",
26 | "@syncfusion/ej2-angular-buttons": "*",
27 | "@syncfusion/ej2-angular-calendars": "*",
28 | "@syncfusion/ej2-angular-charts": "*",
29 | "@syncfusion/ej2-angular-grids": "*",
30 | "@syncfusion/ej2-angular-inputs": "*",
31 | "@syncfusion/ej2-angular-treegrid": "*",
32 | "rxjs": "~7.8.0",
33 | "tslib": "^2.6.2",
34 | "zone.js": "~0.12.0"
35 | },
36 | "devDependencies": {
37 | "@angular-devkit/build-angular": "^15.2.6",
38 | "@angular/cli": "~15.2.6",
39 | "@angular/compiler-cli": "^15.2.0",
40 | "@syncfusion/ej2-showcase-helper": "*",
41 | "@types/jasmine": "~4.3.0",
42 | "jasmine-core": "~4.5.0",
43 | "karma": "~6.4.0",
44 | "karma-chrome-launcher": "~3.1.0",
45 | "karma-coverage": "~2.2.0",
46 | "karma-jasmine": "~5.1.0",
47 | "karma-jasmine-html-reporter": "~2.0.0",
48 | "typescript": "~4.9.4"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/app/about/about.component.html:
--------------------------------------------------------------------------------
1 |
about page
2 |
3 |
--------------------------------------------------------------------------------
/src/app/about/about.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-loan-calculator/db26a1309fb45e3f722327db0a9339cca9f27afa/src/app/about/about.component.scss
--------------------------------------------------------------------------------
/src/app/about/about.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { AboutComponent } from './about.component';
4 |
5 | describe('AboutComponent', () => {
6 | let component: AboutComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ AboutComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(AboutComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/about/about.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | @Component({
5 | selector: 'app-about',
6 | templateUrl: './about.component.html',
7 | styleUrls: ['./about.component.scss']
8 | })
9 | export class AboutComponent {
10 | constructor(public router: Router) { }
11 |
12 | public goBack(): void {
13 | let link: string[] = ['#/home'];
14 | this.router.navigate(link);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/app/app-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.forRoot(routes)],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule { }
11 |
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-loan-calculator/db26a1309fb45e3f722327db0a9339cca9f27afa/src/app/app.component.scss
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed } from '@angular/core/testing';
2 | import { RouterTestingModule } from '@angular/router/testing';
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async () => {
7 | await TestBed.configureTestingModule({
8 | imports: [
9 | RouterTestingModule
10 | ],
11 | declarations: [
12 | AppComponent
13 | ],
14 | }).compileComponents();
15 | });
16 |
17 | it('should create the app', () => {
18 | const fixture = TestBed.createComponent(AppComponent);
19 | const app = fixture.componentInstance;
20 | expect(app).toBeTruthy();
21 | });
22 |
23 | it(`should have as title 'ej2-angular-showcase-template'`, () => {
24 | const fixture = TestBed.createComponent(AppComponent);
25 | const app = fixture.componentInstance;
26 | expect(app.title).toEqual('ej2-angular-showcase-template');
27 | });
28 |
29 | it('should render title', () => {
30 | const fixture = TestBed.createComponent(AppComponent);
31 | fixture.detectChanges();
32 | const compiled = fixture.nativeElement as HTMLElement;
33 | expect(compiled.querySelector('.content span')?.textContent).toContain('ej2-angular-showcase-template app is running!');
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.scss']
7 | })
8 | export class AppComponent {
9 | title = 'ej2-angular-showcase-template';
10 | }
11 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { AppRoutingModule } from './app-routing.module';
4 |
5 | import { NumericTextBoxModule, SliderModule } from '@syncfusion/ej2-angular-inputs';
6 | import { RadioButtonModule } from '@syncfusion/ej2-angular-buttons';
7 | import { AccumulationChartModule, ChartModule } from '@syncfusion/ej2-angular-charts';
8 | import { DatePickerModule } from '@syncfusion/ej2-angular-calendars';
9 | import { TreeGridModule } from '@syncfusion/ej2-angular-treegrid';
10 |
11 | import { AppComponent } from './app.component';
12 | import { HomeComponent } from './home/home.component';
13 | import { InputComponent } from './home/input/input.component';
14 | import { DashboardComponent } from './home/dashboard/dashboard.component';
15 | import { StatementComponent } from './statement/statement.component';
16 | import { BarChartComponent } from './bar-chart/bar-chart.component';
17 | import { TreeGridAppComponent} from './grid-app/grid-app.component';
18 |
19 | import { DataService } from './data-service';
20 |
21 |
22 | @NgModule({
23 | declarations: [
24 | AppComponent,
25 | HomeComponent,
26 | InputComponent,
27 | DashboardComponent,
28 | StatementComponent,
29 | BarChartComponent,
30 | TreeGridAppComponent
31 | ],
32 | imports: [
33 | BrowserModule,
34 | NumericTextBoxModule,
35 | SliderModule,
36 | RadioButtonModule,
37 | AccumulationChartModule,
38 | DatePickerModule,
39 | ChartModule,
40 | TreeGridModule,
41 | AppRoutingModule
42 | ],
43 | providers: [DataService],
44 | bootstrap: [AppComponent]
45 | })
46 | export class AppModule { }
47 |
--------------------------------------------------------------------------------
/src/app/bar-chart/bar-chart.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Amortization Chart
3 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/app/bar-chart/bar-chart.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
2 | import { AccumulationChart, AccumulationLegend, PieSeries, AccumulationTooltip,
3 | AccumulationDataLabel, IAxisLabelRenderEventArgs,
4 | Chart, LineSeries, DateTime, Legend, Tooltip, IAccLoadedEventArgs, AccumulationTheme, IAccPointRenderEventArgs,
5 | StackingColumnSeries, DataLabel, ColumnSeries, IMouseEventArgs, Series
6 | } from '@syncfusion/ej2-charts';
7 | import { DataService } from '../data-service';
8 |
9 | Chart.Inject(LineSeries, StackingColumnSeries, DataLabel, ColumnSeries, DateTime, Legend, Tooltip);
10 |
11 |
12 | @Component({
13 | selector: 'app-chart',
14 | templateUrl: './bar-chart.component.html',
15 | encapsulation: ViewEncapsulation.None
16 | })
17 |
18 | export class BarChartComponent implements OnInit {
19 |
20 | /** Configurations for the Chart page */
21 | constructor(private data: DataService) {
22 | }
23 |
24 | @ViewChild('paymentGraph')
25 | public chartObj!: Chart;
26 |
27 | // Chart component binding properties
28 | public primaryXAxis: Object = {
29 | title: 'Years',
30 | valueType: 'DateTime',
31 | labelFormat: 'y',
32 | intervalType: 'Years',
33 | majorGridLines: { width: 0 },
34 | minorGridLines: { width: 0 },
35 | majorTickLines: { width: 0 },
36 | minorTickLines: { width: 0 },
37 | lineStyle: { width: 1, dashArray: '2', color: 'rgba(255,255,255,0.2)' },
38 | labelStyle: {
39 | color: '#989CA9',
40 | fontFamily: 'Roboto',
41 | fontWeight: '400',
42 | size: '12px',
43 | },
44 | titleStyle: {
45 | color: '#FFFFFF',
46 | fontFamily: 'Raleway, sans-serif',
47 | fontWeight: '600',
48 | opacity: 0.62,
49 | size: '16px',
50 | }
51 | };
52 | public primaryYAxis: Object = {
53 | title: 'Balance',
54 | labelFormat: 'c0',
55 | rangePadding: 'None',
56 | lineStyle: { width: 0 },
57 | majorTickLines: { width: 0 },
58 | majorGridLines: { width: 1, dashArray: '2', color: 'rgba(255,255,255,0.2)' },
59 | minorGridLines: { width: 0 },
60 | minorTickLines: { width: 0 },
61 | labelStyle: {
62 | color: '#989CA9',
63 | fontFamily: 'Roboto',
64 | fontWeight: '400',
65 | size: '16px',
66 | },
67 | titleStyle: {
68 | color: '#FFFFFF',
69 | fontFamily: 'Raleway, sans-serif',
70 | fontWeight: '600',
71 | opacity: 0.62,
72 | size: '16px',
73 | }
74 | };
75 | public axes: Object[] = [
76 | {
77 | majorGridLines: { width: 0 },
78 | minorGridLines: { width: 0 },
79 | majorTickLines: { width: 0 },
80 | minorTickLines: { width: 0 },
81 | rowIndex: 0, opposedPosition: true,
82 | lineStyle: { width: 0 },
83 | name: 'yAxis',
84 | title: 'Payment',
85 | labelFormat: 'c0',
86 | labelStyle: {
87 | color: '#989CA9',
88 | fontFamily: 'Roboto',
89 | fontWeight: '400',
90 | size: '16px',
91 | },
92 | titleStyle: {
93 | color: '#FFFFFF',
94 | fontFamily: 'Raleway, sans-serif',
95 | fontWeight: '600',
96 | opacity: 0.62,
97 | size: '16px',
98 | }
99 | }
100 | ];
101 | public tooltip: Object = {
102 | enable: true, shared: true,
103 | format: '${series.name} : ${point.y}',
104 | header: '${point.x}',
105 | fill: '#FFFFFF',
106 | opacity: 1,
107 | textStyle: {
108 | color: '#555555',
109 | fontFamily: 'Roboto',
110 | size: '12px',
111 | fontWeight: '400',
112 | },
113 | };
114 | public palettes: string[] = ['#FB6589', '#3AC8DC', '#FFFFFF'];
115 | public legendSettings: Object = {
116 | textStyle: {
117 | color: '#FFFFFF',
118 | fontFamily: 'Raleway, sans-serif',
119 | fontWeight: '600',
120 | opacity: 0.62,
121 | size: '16px',
122 | }
123 | };
124 | public chartArea: Object = {
125 | border: {
126 | width: 0
127 | }
128 | };
129 | public height: string = '500px';
130 | public yearWiseData: Object[] = this.data.yearWiseData;
131 | public barSeries: Object[] = [
132 | // {
133 | // type: 'Column',
134 | // columnWidth: 0.7,
135 | // dataSource: this.yearWiseData,
136 | // xName: 'yearN', width: 2, marker: {
137 | // visible: true,
138 | // width: 10,
139 | // height: 10,
140 | // },
141 | // yName: 'yearTotal', name: 'Total Amount Paid', yAxisName: 'yAxis', visible: false
142 | // },
143 | {
144 | type: 'StackingColumn',
145 | columnWidth: 0.425,
146 | dataSource: this.yearWiseData,
147 | xName: 'yearN', width: 2, marker: {
148 | visible: true,
149 | width: 10,
150 | height: 10
151 | },
152 | yName: 'yearPrincipal', name: 'Principal Paid', yAxisName: 'yAxis'
153 | },
154 | {
155 | type: 'StackingColumn',
156 | columnWidth: 0.425,
157 | dataSource: this.yearWiseData,
158 | xName: 'yearN', width: 2, marker: {
159 | visible: true,
160 | width: 10,
161 | height: 10
162 | },
163 | yName: 'yearInterest', name: 'Interest Paid', yAxisName: 'yAxis'
164 | },
165 | {
166 | type: 'Line',
167 | dataSource: this.yearWiseData,
168 | xName: 'yearN', width: 2, marker: {
169 | visible: true,
170 | width: 10,
171 | height: 10,
172 | fill: '#60448D',
173 | },
174 | yName: 'endingBalance', name: 'Balance',
175 | }
176 | ];
177 |
178 | public onChartMouseUp(args: IMouseEventArgs): void {
179 | if (args.target.indexOf('_chart_legend_') > -1 && (args.target.indexOf('shape') > -1 || args.target.indexOf('text') > -1)) {
180 | let id: string[] = [args.target];
181 | id = (args.target.indexOf('shape') > -1) ? id[0].split('chart_legend_shape_') : id[0].split('chart_legend_text_');
182 | let index: number = parseInt(id[1], 10);
183 | let series: Series = this.chartObj.visibleSeries[index];
184 | let yName: string = series.yAxisName;
185 | let ySName: any;
186 | let visibility: boolean = false;
187 | if (series.visible) {
188 | for (let i: number = 0, len: number = this.chartObj.series.length; i < len; i++) {
189 | ySName = this.chartObj.series[i].yAxisName;
190 | if (len === 1 || (this.chartObj.series[i].visible &&
191 | (this.chartObj.series[i]).index !== series.index && yName === ySName)) {
192 | visibility = true;
193 | }
194 | }
195 | series.yAxis.visible = visibility;
196 | } else {
197 | series.yAxis.visible = true;
198 | }
199 | }
200 | }
201 |
202 | public axisLabelRender(args: IAxisLabelRenderEventArgs): void {
203 | if (window.innerWidth < 576) {
204 | if (args.axis.name === 'primaryYAxis' || args.axis.name === 'yAxis') {
205 | let value: number = Number(args.value) / 1000;
206 | args.text = value === 0 ? String(value) : (String(value) + 'K');
207 | }
208 | }
209 | }
210 |
211 | public ngOnInit(): void {
212 | }
213 |
214 | public ngAfterViewInit(): void {
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/src/app/data-service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { Internationalization } from '@syncfusion/ej2-base';
4 | import {
5 | AccumulationChart, AccumulationLegend, PieSeries, AccumulationTooltip,
6 | AccumulationDataLabel, SeriesModel, Chart, LineSeries, DateTime, Legend, Tooltip, IAccLoadedEventArgs, AccumulationTheme, IAccPointRenderEventArgs,
7 | StackingColumnSeries, Crosshair, DataLabel, ColumnSeries, IMouseEventArgs, Series
8 | } from '@syncfusion/ej2-charts';
9 | import { TreeGrid } from '@syncfusion/ej2-treegrid';
10 |
11 | import { TreeGridAppComponent } from './grid-app/grid-app.component';
12 | import { BarChartComponent } from './bar-chart/bar-chart.component';
13 | import { DashboardComponent } from './home/dashboard/dashboard.component';
14 |
15 | @Injectable()
16 |
17 | export class DataService {
18 | constructor() {
19 | this.initialize();
20 | }
21 |
22 | public emi: number = 0;
23 | public princ: number = 0;
24 | public tent: number = 0;
25 | public principalValue: number = 300000;
26 | public interestValue: number = 5.5;
27 | public loanValue: number = 15;
28 | public dateValue: Date = new Date();
29 | public yearWiseData: Object[] = [];
30 | public emiAmt: string = '';
31 | public principalAmt: string = '';
32 | public interestAmt: string = '';
33 | public totalAmt: string = '';
34 | public dashboard!: DashboardComponent;
35 |
36 | public yearTenure: boolean = true;
37 | public chart!: BarChartComponent;
38 | public treegrid!: TreeGridAppComponent;
39 | public totalPrincipalYear: number = 0;
40 | public totalInterestYear: number = 0;
41 | public inter!: number;
42 | public dataUnits: Object[] = [];
43 | public dateObj: Date = new Date();
44 | public totalInterest: number = 0;
45 | public totalAmount: number = 0;
46 | public totalPrincipal: number = 0;
47 | public endBalance!: number;
48 | public beginBalance!: number;
49 | public yearTotal: number = 0;
50 | public monthNames: string[] = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
51 | public intl: Internationalization = new Internationalization();
52 |
53 | public getCurrencyVal(value: number): string {
54 | return this.intl.formatNumber(value, { format: 'C0' });
55 | }
56 |
57 | public refreshUI1(): void {
58 | this.setInitValues();
59 | let interestPercent: number = parseFloat((Math.round((this.emi * this.tent) - this.princ) / Math.round((this.emi * this.tent)) * 100).toFixed(2));
60 | this.dashboard.pieChart.series = [
61 | {
62 | dataSource: [
63 | {
64 | 'x': 'Principal Amount',
65 | y: this.princ,
66 | text: parseFloat(((this.princ) / Math.round((this.emi * this.tent)) * 100).toFixed(2)) + '%'
67 | },
68 | {
69 | 'x': 'Interest Amount',
70 | y: (this.tent ? Math.round((this.emi * this.tent) - this.princ) : 0),
71 | text: interestPercent ? interestPercent + '%' : ' '
72 | }
73 | ],
74 | radius: '80%', xName: 'x',
75 | animation: { enable: true },
76 | yName: 'y',
77 | startAngle: 290,
78 | endAngle: 290, innerRadius: '60%',
79 | explode: true, explodeOffset: '10%', explodeIndex: 3
80 | }
81 | ];
82 | this.dashboard.pieChart.refresh();
83 | this.updateTotalAmt();
84 | }
85 |
86 | public refreshUI(): void {
87 | this.refreshUI1();
88 | this.calRangeValues();
89 | this.renderControls();
90 | this.chart.chartObj.refresh();
91 | }
92 |
93 | public updateTotalAmt(): void {
94 | this.dashboard.emiAmt = this.emiAmt;
95 | this.dashboard.interestAmt = this.interestAmt;
96 | this.dashboard.totalAmt = this.totalAmt;
97 | this.dashboard.principalAmt = this.principalAmt;
98 | }
99 |
100 | public renderControls(): void {
101 | this.treegrid.yearWiseData = this.yearWiseData;
102 | this.chart.chartObj.series = [
103 | // {
104 | // type: 'Column',
105 | // columnWidth: 0.7,
106 | // dataSource: this.yearWiseData,
107 | // xName: 'yearN', width: 2, marker: {
108 | // visible: true,
109 | // width: 10,
110 | // height: 10,
111 | // },
112 | // yName: 'yearTotal', name: 'Total Amount Paid', yAxisName: 'yAxis',
113 | // },
114 | {
115 | type: 'StackingColumn',
116 | columnWidth: 0.425,
117 | dataSource: this.yearWiseData,
118 | xName: 'yearN', width: 2, marker: {
119 | visible: true,
120 | width: 10,
121 | height: 10
122 | },
123 | yName: 'yearPrincipal', name: 'Principal Paid', yAxisName: 'yAxis'
124 | },
125 | {
126 | type: 'StackingColumn',
127 | columnWidth: 0.425,
128 | dataSource: this.yearWiseData,
129 | xName: 'yearN', width: 2, marker: {
130 | visible: true,
131 | width: 10,
132 | height: 10
133 | },
134 | yName: 'yearInterest', name: 'Interest Paid', yAxisName: 'yAxis'
135 | },
136 | {
137 | type: 'Line',
138 | dataSource: this.yearWiseData,
139 | xName: 'yearN', width: 2, marker: {
140 | visible: true,
141 | width: 10,
142 | height: 10,
143 | fill: '#60448D',
144 | },
145 | yName: 'endingBalance', name: 'Balance',
146 | },
147 | ];
148 | }
149 |
150 | public getInterest(): number {
151 | return this.interestValue ? parseFloat('' + this.interestValue / 12 / 100) : 0;
152 | }
153 |
154 | public calculateEMI(): number {
155 | let interestValue: number = this.getInterest();
156 | let tent: number = this.yearTenure ? (this.loanValue * 12) : this.loanValue;
157 | if (interestValue) {
158 | return this.principalValue * interestValue *
159 | (Math.pow((1 + interestValue), tent)) / ((Math.pow((1 + interestValue), tent)) - 1);
160 |
161 | }
162 | return this.principalValue / tent;
163 | }
164 |
165 | public setInitValues(): void {
166 | this.emi = this.calculateEMI();
167 | this.princ = this.principalValue;
168 | this.tent = this.yearTenure ? (this.loanValue * 12) : this.loanValue;
169 | this.dataUnits = [];
170 | this.yearWiseData = [];
171 | this.dateObj = new Date(this.dateValue.getTime());
172 | this.totalInterest = 0;
173 | this.totalAmount = 0;
174 | this.totalPrincipal = 0;
175 | this.totalPrincipalYear = 0;
176 | this.totalInterestYear = 0;
177 | this.emiAmt = this.getCurrencyVal(this.tent ? Math.round(this.emi) : 0);
178 | this.interestAmt = this.getCurrencyVal(this.tent ? Math.round((this.emi * this.tent) - this.princ) : 0);
179 | this.totalAmt = this.getCurrencyVal(this.tent ? Math.round((this.emi * this.tent)) : 0);
180 | this.principalAmt = this.getCurrencyVal(this.princ);
181 | }
182 |
183 | public calRangeValues(): void {
184 | for (let i: number = 0; i < this.tent; i++) {
185 | this.inter = this.getInterest ? (this.princ * this.getInterest()) : this.princ;
186 | this.totalInterest += this.inter;
187 | this.totalAmount += this.emi;
188 | this.totalPrincipal += parseFloat((this.emi - this.inter).toFixed(2));
189 | this.endBalance = this.princ - (this.emi - this.inter);
190 | this.yearTotal += this.emi;
191 | this.totalPrincipalYear += parseFloat((this.emi - this.inter).toFixed(2));
192 | this.totalInterestYear += this.inter;
193 | this.dataUnits.push({
194 | id: i + 1,
195 | month: this.monthNames[this.dateObj.getMonth()],
196 | index: i + 1,
197 | totalInterest: Math.round(this.totalInterest),
198 | totalAmount: this.totalAmount,
199 | emi: Math.round(this.emi),
200 | year: this.monthNames[this.dateObj.getMonth()],
201 | beginningBalance: Math.round(this.princ),
202 | interest: Math.round(this.inter),
203 | pricipalPaid: Math.round(this.emi - this.inter),
204 | endingBalance: Math.round(this.endBalance),
205 | parentId: this.dateObj.getFullYear(),
206 | yearTotal: Math.round(this.yearTotal),
207 | yearPrincipal: this.totalPrincipalYear,
208 | yearInterest: this.totalInterestYear,
209 | });
210 | if (i === 0 || this.dateObj.getMonth() === 0) {
211 | this.beginBalance = this.princ;
212 | }
213 | if (this.dateObj.getMonth() === 11 || (i === this.tent - 1)) {
214 | this.yearWiseData.push({
215 | id: this.dateObj.getFullYear(),
216 | beginningBalance: Math.round(this.beginBalance),
217 | totalInterest: Math.round(this.totalInterest),
218 | totalPrincipal: Math.round(this.totalPrincipal),
219 | totalAmount: Math.round(this.totalAmount),
220 | yearTotal: Math.round(this.yearTotal),
221 | endingBalance: Math.round(this.endBalance),
222 | yearN: new Date(this.dateObj.getFullYear(), 0, 1),
223 | year: this.dateObj.getFullYear(),
224 | yearPrincipal: this.totalPrincipalYear,
225 | yearInterest: this.totalInterestYear,
226 | childRecord: this.dataUnits,
227 | parentId: null,
228 | });
229 | this.yearTotal = 0;
230 | this.totalPrincipalYear = 0;
231 | this.totalInterestYear = 0;
232 | this.dataUnits = [];
233 | }
234 | this.princ = this.endBalance;
235 | if (i < this.tent - 1) {
236 | this.dateObj.setMonth(this.dateObj.getMonth() + 1);
237 | }
238 | }
239 | }
240 |
241 | public initialize(): void {
242 | this.setInitValues();
243 | this.calRangeValues();
244 | }
245 |
246 | }
247 |
--------------------------------------------------------------------------------
/src/app/grid-app/grid-app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Amortization Schedule
3 |
4 |
5 |
6 |
7 |
8 | {{ data.emi | currency:'':'symbol':'1.0-2' }}
9 | {{ data.yearTotal | currency:'':'symbol':'1.0-2' }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/app/grid-app/grid-app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
2 | import { closest } from '@syncfusion/ej2-base';
3 | import { TreeGridComponent } from '@syncfusion/ej2-angular-treegrid';
4 | import { TreeGrid } from '@syncfusion/ej2-treegrid';
5 | import { DataService } from '../data-service';
6 |
7 |
8 | @Component({
9 | selector: 'app-grid',
10 | templateUrl: './grid-app.component.html',
11 | encapsulation: ViewEncapsulation.None
12 | })
13 |
14 | export class TreeGridAppComponent implements OnInit {
15 |
16 | /** Configurations for the Grid page */
17 | constructor(private data: DataService) {
18 | }
19 |
20 | @ViewChild('scheduleGrid')
21 | public treegrid!: TreeGrid;
22 |
23 | public yearWiseData: Object[] = this.data.yearWiseData;
24 | public format: string = 'c0';
25 | public balanceHideAtMedia: string = '(min-width: 750px)';
26 | public paymentHideAtMedia: string = '(min-width: 480px)';
27 |
28 | public ngOnInit(): void {
29 | }
30 |
31 | public ngAfterViewInit(): void {
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/app/home/dashboard/dashboard.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Principal Amount
12 |
{{principalAmt}}
13 |
14 |
15 |
Total Interest
16 |
{{interestAmt}}
17 |
18 |
19 |
20 | Total Payment
21 | (Principal + Interest)
22 |
23 |
{{totalAmt}}
24 |
25 |
26 |
27 |
28 |
29 |
{{emiAmt}}
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/app/home/dashboard/dashboard.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
2 | import { AccumulationChart, AccumulationLegend, PieSeries, AccumulationTooltip,
3 | AccumulationDataLabel,
4 | Chart, LineSeries, DateTime, Legend, Tooltip, IAccLoadedEventArgs, AccumulationTheme, IAccPointRenderEventArgs,
5 | StackingColumnSeries, Crosshair, DataLabel, ColumnSeries, IMouseEventArgs, Series
6 | } from '@syncfusion/ej2-charts';
7 | import { AccumulationChartComponent } from '@syncfusion/ej2-angular-charts';
8 | import { DataService } from '../../data-service';
9 | AccumulationChart.Inject(AccumulationLegend, PieSeries, AccumulationTooltip, AccumulationDataLabel);
10 |
11 | @Component({
12 | selector: 'dashboard-section',
13 | templateUrl: './dashboard.component.html',
14 | encapsulation: ViewEncapsulation.None
15 | })
16 |
17 | export class DashboardComponent implements OnInit {
18 |
19 | /** Configurations for the Dashboard page */
20 | constructor(private data: DataService) {
21 | }
22 |
23 | @ViewChild('pieChart')
24 | public pieChart!: AccumulationChartComponent;
25 |
26 | // chart binding properties
27 | public legendSettings: Object = { visible: false };
28 | public width: string = '100%';
29 | public tooltip: Object = { enable: false };
30 | public pieSeries: Object[] = [
31 | {
32 | dataSource: [
33 | { 'x': 'Principal Amount', y: this.data.principalValue },
34 | { 'x': 'Interest Amount', y: ((this.data.emi * this.data.tent) - this.data.principalValue) }
35 | ],
36 | radius: '80%', xName: 'x',
37 | animation: { enable: true },
38 | yName: 'y',
39 | startAngle: 290,
40 | endAngle: 290, innerRadius: '60%',
41 | explode: true, explodeOffset: '10%', explodeIndex: 3
42 | }
43 | ];
44 | public emiAmt: string = this.data.emiAmt;
45 | public principalAmt: string = this.data.principalAmt;
46 | public interestAmt: string = this.data.interestAmt;
47 | public totalAmt: string = this.data.totalAmt;
48 |
49 | public onLoad(args: IAccLoadedEventArgs): void {
50 | let selectedTheme: string = location.hash.split('/')[1];
51 | selectedTheme = selectedTheme ? selectedTheme : 'Material';
52 | args.accumulation.theme = (selectedTheme.charAt(0).toUpperCase() + selectedTheme.slice(1));
53 | }
54 |
55 | public pointRender(args: IAccPointRenderEventArgs): void {
56 | if (args.point.index) {
57 | args.border.width = 7;
58 | args.fill = 'url(#interest_svg)';
59 | } else {
60 | args.border.width = 7;
61 | args.border.color = '#162036';
62 | args.fill = 'url(#principal_svg)';
63 | }
64 | }
65 |
66 | public ngOnInit(): void {
67 | }
68 |
69 | public ngAfterViewInit(): void {
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/app/home/home.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-loan-calculator/db26a1309fb45e3f722327db0a9339cca9f27afa/src/app/home/home.component.scss
--------------------------------------------------------------------------------
/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 |
15 | fixture = TestBed.createComponent(HomeComponent);
16 | component = fixture.componentInstance;
17 | fixture.detectChanges();
18 | });
19 |
20 | it('should create', () => {
21 | expect(component).toBeTruthy();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
2 | import { DashboardComponent } from './dashboard/dashboard.component';
3 | import { DataService } from '../data-service';
4 |
5 | @Component({
6 | selector: 'app-home',
7 | templateUrl: './home.component.html',
8 | encapsulation: ViewEncapsulation.None
9 | })
10 |
11 | export class HomeComponent implements OnInit {
12 |
13 | /** Configurations for the Home page */
14 | constructor(private data: DataService) {
15 | }
16 |
17 | @ViewChild('dashboardSection')
18 | public dashboard!: DashboardComponent;
19 |
20 | public ngOnInit(): void {
21 | }
22 |
23 | public ngAfterViewInit(): void {
24 | this.data.dashboard = this.dashboard;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/home/input/input.component.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/home/input/input.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, OnInit, ViewChild } from '@angular/core';
2 | import { NumericTextBoxComponent, SliderTickRenderedEventArgs } from '@syncfusion/ej2-angular-inputs';
3 | import { ChangeEventArgs } from '@syncfusion/ej2-inputs';
4 | import { DataService } from '../../data-service';
5 |
6 | @Component({
7 | selector: 'input-section',
8 | templateUrl: './input.component.html',
9 | encapsulation: ViewEncapsulation.None
10 | })
11 |
12 | export class InputComponent implements OnInit {
13 |
14 | /** Configurations for the Input page */
15 | constructor(private data: DataService) {
16 | this.principalValue = this.data.principalValue;
17 | this.interestValue = this.data.interestValue;
18 | this.loanValue = this.data.loanValue;
19 | }
20 |
21 | // The principal NumericTextBox binding properties
22 | public principalNumMinValue: number = 1000;
23 | public principalNumMaxValue: number = 5000000;
24 | public principalNumStep: number = 10000;
25 | public principalNumFormat: string = 'c0';
26 | public principalNumWidth: string = '200px';
27 |
28 | public principalNumChange(args: ChangeEventArgs): void {
29 | this.data.principalValue = this.principalValue;
30 | if (args.isInteraction) {
31 | this.data.refreshUI();
32 | }
33 | }
34 |
35 | // The interest NumericTextBox binding properties
36 | public interestNumMinValue: number = 0;
37 | public interestNumMaxValue: number = 20;
38 | public interestNumStep: number = .25;
39 | public interestNumFormat: string = '#.##\' %\'';
40 | public interestNumWidth: string = '165px';
41 |
42 | public interestNumChange(args: ChangeEventArgs): void {
43 | this.data.interestValue = this.interestValue;
44 | if (args.isInteraction) {
45 | this.data.refreshUI();
46 | }
47 | }
48 |
49 | // The loan NumericTextBox binding properties
50 | public loanNumMinValue: number = 1;
51 | public loanNumMaxValue: number = 40;
52 | public loanNumStep: number = 1;
53 | public loanNumFormat: string = '#.##';
54 | public loanNumWidth: string = '150px';
55 |
56 | public loanNumChange(args: ChangeEventArgs): void {
57 | this.data.loanValue = this.loanValue;
58 | if (args.isInteraction) {
59 | this.data.refreshUI();
60 | }
61 | }
62 |
63 | // The principal Slider binding properties
64 | public principalValue: number = 0;
65 | public principalMinValue: number = 0;
66 | public principalMaxValue: number = 500000;
67 | public principalStep: number = 10000;
68 | public principalType: string = 'MinRange';
69 | public principalTicks: Object = { placement: 'After', largeStep: 100000, smallStep: 10000, showSmallTicks: false, format: 'c0' };
70 |
71 | public principalChange(args: ChangeEventArgs): void {
72 | this.data.principalValue = this.principalValue;
73 | this.data.setInitValues();
74 | this.data.updateTotalAmt();
75 | }
76 |
77 | public principalChanged(args: ChangeEventArgs): void {
78 | this.data.refreshUI();
79 | }
80 |
81 | public principalRenderedTicks(args: SliderTickRenderedEventArgs): void {
82 | let li: HTMLCollectionBase = args.ticksWrapper.getElementsByClassName('e-large');
83 | for (let i: number = 0; i < li.length; ++i) {
84 | let ele: HTMLElement = (li[i].querySelectorAll('.e-tick-value')[0] as HTMLElement);
85 | let num: number = parseInt(ele.innerText.substring(1).replace(/,/g , ''), 10) / 1000;
86 | ele.innerText = num === 0 ? ('' + num) : (num + 'K');
87 | }
88 | }
89 |
90 | // The interest Slider binding properties
91 | public interestValue: number = 0;
92 | public interestMinValue: number = 0;
93 | public interestMaxValue: number = 20;
94 | public interestStep: number = .25;
95 | public interestType: string = 'MinRange';
96 | public interestTicks: Object = { placement: 'After', largeStep: 5, smallStep: 1, showSmallTicks: false };
97 |
98 | public interestChange(args: ChangeEventArgs): void {
99 | this.data.interestValue = this.interestValue;
100 | this.data.setInitValues();
101 | this.data.updateTotalAmt();
102 | }
103 |
104 | public interestChanged(args: ChangeEventArgs): void {
105 | this.data.refreshUI();
106 | }
107 |
108 | // The loan Slider binding properties
109 | public loanValue: number = 0;
110 | public loanMinValue: number = 0;
111 | public loanMaxValue: number = 40;
112 | public loanStep: number = 1;
113 | public loanType: string = 'MinRange';
114 | public loanTicks: Object = { placement: 'After', largeStep: 10, smallStep: 1, showSmallTicks: false };
115 |
116 | public loanChange(args: ChangeEventArgs): void {
117 | this.data.loanValue = this.loanValue;
118 | this.data.setInitValues();
119 | this.data.updateTotalAmt();
120 | }
121 |
122 | public loanChanged(args: ChangeEventArgs): void {
123 | this.data.refreshUI();
124 | }
125 |
126 | // Radio button binding properties
127 | public yearTenure: boolean = this.data.yearTenure;
128 |
129 | public monthChanged(): void {
130 | this.data.yearTenure = this.yearTenure = false;
131 | this.loanNumMinValue = 12;
132 | this.loanNumMaxValue = 480;
133 | this.loanNumStep = 12;
134 | this.loanMaxValue = 480;
135 | this.loanValue = (this.loanValue * 12);
136 | this.loanStep = 12;
137 | this.loanTicks = { placement: 'After', largeStep: 120, smallStep: 12, showSmallTicks: false };
138 | }
139 |
140 | public yearChanged(): void {
141 | this.data.yearTenure = this.yearTenure = true;
142 | this.loanNumMinValue = 1;
143 | this.loanNumMaxValue = 40;
144 | this.loanNumStep = 1;
145 | this.loanMaxValue = 40;
146 | this.loanValue = (this.loanValue / 12);
147 | this.loanStep = 1;
148 | this.loanTicks = { largeStep: 10, smallStep: 1, showSmallTicks: false };
149 | }
150 |
151 | public ngOnInit(): void {
152 | }
153 |
154 | public ngAfterViewInit(): void {
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/app/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowser } from '@angular/platform-browser';
2 | import { AppModuleNgFactory } from './app.module.ngfactory';
3 | platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
--------------------------------------------------------------------------------
/src/app/statement/statement.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Monthly payments starting from
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/app/statement/statement.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, ViewEncapsulation, OnInit } from '@angular/core';
2 | import { ChangedEventArgs } from '@syncfusion/ej2-angular-calendars';
3 | import { isNullOrUndefined as isNOU } from '@syncfusion/ej2-base';
4 | import { DataService } from '../data-service';
5 | import { Data } from '@syncfusion/ej2-angular-grids';
6 |
7 | @Component({
8 | selector: 'app-statement',
9 | templateUrl: './statement.component.html',
10 | encapsulation: ViewEncapsulation.None
11 | })
12 |
13 | export class StatementComponent implements OnInit {
14 |
15 | /** Configurations for the Statement page */
16 | constructor(private data: DataService) {
17 | }
18 |
19 | public format: string = 'MMM yyy';
20 | public dateValue?: Date = new Date();
21 | public width: string = '250px';
22 |
23 | public onChange(args: ChangedEventArgs): void {
24 | if (isNOU(args.value as Date)) {
25 | this.dateValue = new Date();
26 | } else {
27 | this.data.dateValue = this.dateValue = args.value as Date;
28 | this.data.refreshUI();
29 | }
30 | }
31 |
32 | public ngOnInit(): void {
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-loan-calculator/db26a1309fb45e3f722327db0a9339cca9f27afa/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/assets/index.scss:
--------------------------------------------------------------------------------
1 | //sass-lint:disable-all
2 | $border-color: #e4e2e2;
3 | $bg-color: #fbfbfb;
4 | $body-bg-color: rgb(22, 32, 54);
5 | $header-bg-color: #27304c;
6 | $header-font-color: rgb(255, 255, 255);
7 | $input-bg-color: #3A4360;
8 | $input-font-color: #fff;
9 | $input-icon-color: #95979c;
10 | $input-border-color: #475274;
11 | $main-bg-color: #27304c;
12 | $text-color: #E7E7E7;
13 | $text-color1: #989CA9;
14 | $text-color2: #F5F5F5;
15 | $header-color: #4a4a4a 100%;
16 | $range-color: #3B9ED4;
17 | $slider-color: #20273E;
18 | $row-bg-color: #27304c;
19 | $altrow-bg-color: #313B5A;
20 | $row-font-color: #fff;
21 | $hover-color: #404D75;
22 | $active-color: #5B6BC0;
23 | $principal-color: linear-gradient(#3AC8DC, #5B6BC0);
24 | $interest-color: linear-gradient(#F3A55B, #FB6589);
25 | $tick-color: #ABABAB;
26 |
27 | @mixin Raleway-600 {
28 | font-family: 'Raleway', sans-serif;
29 | font-weight: 600;
30 | }
31 |
32 | @mixin Raleway-500 {
33 | font-family: 'Raleway', sans-serif;
34 | font-weight: 500;
35 | }
36 |
37 | @mixin Raleway-400 {
38 | font-family: 'Raleway', sans-serif;
39 | font-weight: 500;
40 | }
41 |
42 | @mixin Roboto-400 {
43 | font-family: "Roboto";
44 | font-weight: 400;
45 | }
46 |
47 | @mixin Roboto-500 {
48 | font-family: "Roboto";
49 | font-weight: 500;
50 | }
51 |
52 | .content-space {
53 | display: block;
54 | margin-top: 25px;
55 | margin-bottom: 25px;
56 | width: 100%;
57 |
58 | table {
59 | width: 100%;
60 | }
61 |
62 | label {
63 | float: left;
64 | margin-top: 6px;
65 | color: $text-color;
66 | font-size: 16px;
67 | @include Raleway-400();
68 | }
69 | }
70 |
71 | .editor-space {
72 | display: flex;
73 | justify-content: left;
74 | float: right;
75 | }
76 |
77 | .form-space {
78 | margin-bottom: 25px;
79 |
80 | &:not(:first-child) {
81 | margin-top: 25px;
82 | }
83 | }
84 |
85 | .navbar {
86 | border-radius: 0;
87 | }
88 |
89 | .component-content {
90 | border-radius: 3px;
91 | height: 100%;
92 | }
93 |
94 | .parent {
95 | min-height: 85px;
96 | position: relative;
97 | text-align: center;
98 | }
99 |
100 | .child {
101 | left: 50%;
102 | position: absolute;
103 | top: 50%;
104 | transform: translate(-50%, -50%);
105 | }
106 |
107 | .top-space {
108 | margin-top: 30px;
109 | margin-bottom: 15px;
110 |
111 | h6 {
112 | position: relative;
113 | }
114 | }
115 |
116 | // .emi-content > div {
117 | // background-color: $bg-color;
118 | // }
119 |
120 | .emi-content {
121 | max-width: 634px;
122 | max-height: 660px;
123 | margin: 30px 0 25px 0;
124 | padding: 0 44px;
125 |
126 | h6 {
127 | @include Raleway-500();
128 | text-align: center;
129 | }
130 |
131 | .emi-header {
132 | font-size: 20px;
133 | color: $text-color;
134 | letter-spacing: 0.7px;
135 | line-height: 29px;
136 | }
137 |
138 | .emi-footer {
139 | font-size: 18px;
140 | color: $text-color1;
141 | margin-top: 5px;
142 | text-align: center;
143 | }
144 |
145 | h1 {
146 | color: $text-color2;
147 | font-size: 32px;
148 | @include Roboto-500();
149 | text-align: center;
150 | }
151 |
152 | > div {
153 |
154 | > h6 {
155 | text-align: left;
156 | }
157 | }
158 | }
159 |
160 | .top-margin {
161 | margin-top: 10px;
162 | }
163 |
164 | .bottom-margin {
165 | margin-bottom: 35px;
166 | }
167 |
168 | .pie-content {
169 | height: 100%;
170 | padding: 0;
171 |
172 | p {
173 | text-align: center;
174 | font-size: 15px;
175 | @include Raleway-400();
176 | color: $text-color1;
177 | }
178 |
179 | .pie-icon {
180 | height: 21px;
181 | width: 21px;
182 | display: inline-block;
183 | border-radius: 10px;
184 | margin: 0 14px 0 0;
185 | vertical-align: text-top;
186 |
187 | &.pie-principal {
188 | background: $principal-color;
189 | }
190 |
191 | &.pie-interest {
192 | background: $interest-color;
193 | }
194 | }
195 |
196 | h5 {
197 | font-size: 18px;
198 | @include Roboto-500();
199 | color: $text-color2;
200 | padding: 5px 0 10px;
201 | text-align: center;
202 | }
203 |
204 | > div {
205 | margin: 35px 0;
206 |
207 | &:first-child {
208 | margin-top: 43px;
209 | }
210 |
211 | &.pie-total {
212 |
213 | p:first-child {
214 | margin-bottom: 0;
215 | }
216 |
217 | p + p {
218 | font-size: 14px;
219 | letter-spacing: 1px;
220 | }
221 | }
222 | }
223 | }
224 |
225 | .center-heading {
226 | padding: 10px;
227 | font-size: 20px;
228 | text-align: center;
229 | font-weight: 500;
230 | }
231 |
232 | .border-set {
233 | border: 1px solid $border-color;
234 | }
235 |
236 | .tenure-value {
237 | display: flex;
238 | padding: 4px 5px 4px 10px;
239 | position: relative;
240 |
241 | li {
242 | float: left;
243 | }
244 |
245 | .e-radio {
246 |
247 | & + label {
248 |
249 | &::before {
250 | height: 20px;
251 | width: 20px;
252 | border-width: 2px;
253 | }
254 |
255 | &::before, &:hover::before {
256 | border-color: $range-color;
257 | background-color: $input-bg-color;
258 | }
259 |
260 | &::after, &:hover::after {
261 | background-color: $range-color;
262 | }
263 |
264 | .e-label {
265 | color: $text-color;
266 | font-size: 16px;
267 | @include Raleway-500()
268 | }
269 | }
270 |
271 | &:focus + label {
272 |
273 | &::before {
274 | border-color: $range-color;
275 | background-color: $input-bg-color;
276 | }
277 |
278 | &::after{
279 | background-color: $range-color;
280 | }
281 | }
282 | }
283 |
284 | >li {
285 | padding-left: 10px;
286 | }
287 | }
288 |
289 | .graph-container {
290 | height: 520px;
291 | padding: 0 33px;
292 | }
293 |
294 | li {
295 | list-style: none;
296 | }
297 |
298 | .border-unset-left {
299 | border-left: 0;
300 | }
301 |
302 | .border-unset-right {
303 | border-right: 0;
304 | }
305 |
306 | .main-content {
307 | margin-top: 40px;
308 | max-width: 1300px;
309 |
310 | .loan-content {
311 | background-color: $main-bg-color;
312 | color: $text-color;
313 | }
314 |
315 | .e-slider-container {
316 |
317 | .e-scale {
318 | color: $tick-color;
319 |
320 | .e-tick {
321 |
322 | &.e-large {
323 | top: 14px;
324 | }
325 |
326 | .e-tick-value {
327 | @include Roboto-400();
328 | font-size: 13px;
329 | color: $tick-color;
330 | }
331 | }
332 | }
333 |
334 | .e-slider {
335 |
336 | .e-slider-track {
337 | background: $slider-color;
338 | border-color: $slider-color;
339 | }
340 |
341 | .e-handle {
342 | height: 26px;
343 | width: 26px;
344 | top: calc(50% - 13px);
345 | margin-left: -13px;
346 |
347 | &::before {
348 | content: '';
349 | background: $range-color;
350 | width: 14px;
351 | height: 14px;
352 | position: absolute;
353 | border-radius: 16px;
354 | top: 5px;
355 | left: 5px;
356 | }
357 | }
358 |
359 | .e-range {
360 | background-color: $range-color;
361 | top: calc(50% - 5px);
362 | }
363 |
364 | & .e-slider-track, & .e-range {
365 | height: 12px;
366 | }
367 |
368 | & .e-handle, & .e-slider-track, & .e-range {
369 | border-radius: 15px;
370 | }
371 | }
372 | }
373 |
374 | .e-input-group.e-control-wrapper {
375 |
376 | &:not(.e-success):not(.e-warning):not(.e-error) {
377 | border-color: $input-border-color;
378 | background: $input-bg-color;
379 | }
380 |
381 | &:not(.e-disabled):active:not(.e-success):not(.e-warning):not(.e-error):not(.e-input-focus) {
382 | border-color: $hover-color;
383 | }
384 |
385 | .e-input-group-icon {
386 | background: $input-bg-color;
387 | color: $input-icon-color;
388 | border-color: $input-border-color;
389 |
390 | &:hover {
391 | background: $hover-color;
392 | color: $input-icon-color;
393 | }
394 | }
395 |
396 | .e-spin-down {
397 | border-right-width: 0;
398 | }
399 |
400 | input.e-input {
401 | @include Roboto-400();
402 | font-size: 16px;
403 | height: 38px;
404 | background-color: $input-bg-color;
405 | color: $input-font-color;
406 | }
407 | }
408 |
409 | .graph-text {
410 | color: $text-color;
411 | font-size: 20px;
412 | @include Raleway-500();
413 | letter-spacing: 0.7px;
414 | margin: 0 22px;
415 | display: inline-block;
416 | vertical-align: middle;
417 | }
418 |
419 | .graph-input {
420 | display: inline-block;
421 | margin: 34px 22px;
422 | }
423 |
424 | .e-grid {
425 | border-color: $main-bg-color;
426 | border-radius: 0;
427 |
428 | .e-row {
429 | cursor: pointer;
430 | }
431 | .e-gridheader {
432 | border-width: 0;
433 | }
434 |
435 | .e-icon-grightarrow, .e-icon-gdownarrow {
436 | width: 11px;
437 | height: 11px;
438 | border: 1px solid #fff;
439 | margin: auto;
440 | text-indent: 0;
441 |
442 | &::before {
443 | position: absolute;
444 | font-size: 13px;
445 | }
446 | }
447 |
448 | .e-icon-grightarrow::before {
449 | content: '+';
450 | margin: -4px 0 0 -4px;
451 | }
452 |
453 | .e-icon-gdownarrow::before {
454 | content: '-';
455 | margin: -6px 0 0 -2px;
456 | }
457 |
458 | .e-headercelldiv {
459 | font-size: 14px;
460 | height: 55px;
461 | line-height: 55px;
462 | @include Raleway-400();
463 | white-space: normal;
464 | }
465 |
466 | & .e-columnheader {
467 | background: linear-gradient(-90deg, #5B6BC0, #3AC8DC);
468 | }
469 |
470 | & .e-headercell, & .e-detailheadercell {
471 | background: transparent;
472 | color: #fff;
473 | }
474 |
475 | & .e-rowcell, & .e-detailrowcollapse, & .e-detailrowexpand, & .e-detailindentcell, & .e-detailrow .e-altrow .e-rowcell {
476 | background: $row-bg-color;
477 | border-width: 0;
478 | font-size: 13px;
479 | @include Roboto-400();
480 | }
481 |
482 | .e-altrow {
483 |
484 | & .e-rowcell, & .e-detailrowcollapse, & .e-detailrowexpand {
485 | background: $altrow-bg-color;
486 | }
487 | }
488 |
489 | & .e-icons, & .e-row .e-rowcell, & .e-detailrowcollapse, & .e-detailrowexpand {
490 | color: $row-font-color;
491 | }
492 |
493 | .e-detailcell {
494 | padding: 0;
495 | border-width: 0;
496 | }
497 |
498 | &.e-gridhover tr[role='row'] {
499 |
500 | &:not(.e-editedrow):hover .e-rowcell:not(.e-cellselectionbackground), &:hover .e-detailrowcollapse:not(.e-cellselectionbackground), &:hover .e-detailrowexpand:not(.e-cellselectionbackground) {
501 |
502 | &:not(.e-active):not(.e-updatedtd):not(.e-indentcell) {
503 | background: $hover-color;
504 | color: #fff;
505 | }
506 | }
507 | }
508 |
509 | & [aria-selected] + tr .e-detailindentcell {
510 | border-width: 0;
511 | }
512 | }
513 | }
514 |
515 |
516 | #control-container {
517 | padding: 0px !important;
518 | }
519 |
520 | #principal_svg stop {
521 | stop-color: #3AC8DC;
522 | }
523 |
524 | #principal_svg stop[offset="0"] {
525 | stop-color: #3AC8DC;
526 | }
527 |
528 | #principal_svg stop[offset="1"] {
529 | stop-color: #5B6BC0;
530 | }
531 |
532 | #interest_svg stop {
533 | stop-color: #F3A55B;
534 | }
535 |
536 | #interest_svg stop[offset="0"] {
537 | stop-color: #e97c11;
538 | }
539 |
540 | #interest_svg stop[offset="1"] {
541 | stop-color: #FB6589;
542 | }
543 |
544 | .navbar-right {
545 | float: right;
546 | }
547 |
548 | .navbar-header {
549 | float: left;
550 | }
551 |
552 | .flex-container {
553 | display: flex;
554 | flex-wrap: wrap;
555 |
556 | .col-lg-4 {
557 | width: 33.33333333%;
558 | }
559 |
560 | @media (max-width: 500px) {
561 | .col-lg-4 {
562 | width: 100%;
563 | }
564 | }
565 | }
566 |
567 |
568 | h1,
569 | h2,
570 | h3,
571 | h4,
572 | h5,
573 | h6 {
574 | font-weight: 600;
575 | }
576 |
577 | body {
578 | background-color: $body-bg-color;
579 | color: $text-color;
580 | }
581 |
582 | .left-content {
583 | background-color: $main-bg-color;
584 | max-height: 660px;
585 | margin: 25px 0;
586 | padding: 0 44px;
587 | border-right: 1px solid $input-border-color;
588 | }
589 |
590 | .header-style {
591 | background: $header-bg-color;
592 | color: $header-font-color;
593 | margin: 0;
594 | padding: 15px;
595 | text-align: center;
596 | text-transform: uppercase;
597 | font-size: 26px;
598 | @include Raleway-500();
599 | }
600 |
601 | .margin-setter {
602 | margin: 35px;
603 | }
604 |
605 | .start-setter {
606 | margin: 17px 0;
607 | }
608 |
609 | .e-datepicker.e-popup-wrapper {
610 | border-color: $input-border-color;
611 | }
612 |
613 | .e-datepicker .e-calendar {
614 | background-color: $input-bg-color;
615 | }
616 |
617 | .e-calendar .e-header .e-title, .e-calendar .e-content span, .e-calendar .e-header.e-decade .e-title,
618 | .e-calendar .e-header .e-title:hover {
619 | color: $input-font-color;
620 | }
621 |
622 | .e-calendar .e-header .e-icon-container span, .e-calendar .e-header .e-prev:hover > span, .e-calendar .e-header .e-next:hover > span,
623 | .e-calendar .e-header button.e-prev:active span, .e-calendar .e-header button.e-next:active span {
624 | color: $input-icon-color;
625 | }
626 |
627 | .e-calendar .e-content td.e-selected span.e-day, .e-calendar .e-header .e-prev:active, .e-calendar .e-header .e-next:active {
628 | background: $active-color;
629 | }
630 |
631 | .e-calendar .e-content.e-decade tr:first-child .e-cell:first-child span.e-day, .e-calendar .e-content.e-decade tr:last-child .e-cell:last-child span.e-day {
632 | color: $tick-color;
633 | }
634 |
635 | .e-calendar .e-header .e-prev:hover, .e-calendar .e-header .e-next:hover, .e-calendar .e-content td.e-focused-date span.e-day,
636 | .e-calendar .e-content.e-year td:hover span.e-day, .e-calendar .e-content.e-decade td:hover span.e-day,
637 | .e-calendar .e-content.e-year td.e-selected:hover span.e-day, .e-calendar .e-content.e-decade td.e-selected:hover span.e-day {
638 | background: $hover-color;
639 | color: $input-font-color;
640 | }
641 |
642 | @media (max-width: 319px) {
643 | .container {
644 | min-width: 300px;
645 | }
646 | }
647 | @media (max-width: 576px) {
648 | .header-style {
649 | font-size: 20px;
650 | padding: 13px 0;
651 | }
652 | .container {
653 | padding: 10px;
654 | margin: 0;
655 | .e-slider-container .e-scale .e-tick .e-tick-value {
656 | font-size: 12px;
657 | }
658 | }
659 | .top-space {
660 | margin: 10px;
661 | }
662 | .row {
663 | margin-left: 0;
664 | margin-right: 0;
665 | }
666 | .left-content-wrap .loan-content:first-child {
667 | margin-bottom: 10px;
668 | }
669 | .left-content {
670 | padding: 0;
671 | margin: 0;
672 | border-right-width: 0;
673 | .col-lg-12 {
674 | padding: 12px 12px 18px 12px;
675 | }
676 |
677 | .content-space {
678 | margin: 0 0 12px 0;
679 | td {
680 | display: block;
681 | }
682 | label {
683 | font-size: 15px;
684 | margin: 0 0 12px;
685 | }
686 | .tenure-value .e-radio + label {
687 | margin: 0;
688 | .e-label {
689 | font-size: 13px;
690 | }
691 | }
692 | }
693 |
694 | .editor-space {
695 | float: left;
696 | width: 100%;
697 | }
698 |
699 | .form-space {
700 | margin-bottom: 23px;
701 | &:last-child {
702 | margin-bottom: 0;
703 | }
704 | }
705 |
706 | .e-input-group {
707 | width: 100% !important;
708 | }
709 | }
710 | .emi-content {
711 | padding: 0;
712 | margin-top: 0;
713 | .emi-header {
714 | text-align: center;
715 | font-size: 18px;
716 | }
717 | }
718 | .tenure-value {
719 | padding: 0;
720 | float: none;
721 | }
722 | .emi-content {
723 | h6 {
724 | font-size: 23px;
725 | }
726 | h1 {
727 | font-size: 30px;
728 | }
729 |
730 | .col-lg-7 {
731 | width: 100%;
732 | float: left;
733 | }
734 |
735 | .col-lg-5 {
736 | width: 100%;
737 | float: left;
738 | }
739 | }
740 | .pie-content {
741 | div {
742 | margin: 10px 0;
743 | text-align: center;
744 | &:first-child {
745 | margin-top: 0;
746 | }
747 | }
748 | p {
749 | display: inline-block;
750 | margin: 0;
751 | }
752 | h5 {
753 | display: inline-block;
754 | margin: 0;
755 | padding: 5px;
756 | }
757 | .pie-total {
758 | span {
759 | display: inline-block;
760 | }
761 | p {
762 | display: block;
763 | }
764 | }
765 | }
766 | .main-content .loan-content {
767 | .graph-text {
768 | margin: 12px;
769 | font-size: 18px;
770 | }
771 | .graph-input {
772 | padding: 0 12px 12px;
773 | width: 100%;
774 | margin: 0;
775 | }
776 | }
777 | .center-heading {
778 | font-size: 18px;
779 | }
780 | .graph-container {
781 | padding: 0 2px;
782 | }
783 | }
784 |
785 | @media (min-width: 576px) and (max-width: 991px) {
786 | .container {
787 | padding: 0 30px;
788 | width: 100%;
789 | }
790 | .left-content-wrap {
791 | .loan-content {
792 | width: 100%;
793 | float: left;
794 | margin: 0;
795 | padding-bottom: 25px;
796 | & + .loan-content {
797 | margin-top: 30px;
798 | padding-top: 25px;
799 | padding-bottom: 0px;
800 | }
801 | }
802 | }
803 | .left-content {
804 | border-right-width: 0;
805 | }
806 |
807 | .emi-content {
808 | max-width: 100%;
809 |
810 | .col-lg-7 {
811 | width: 60%;
812 | float: left;
813 | }
814 |
815 | .col-lg-5 {
816 | width: 40%;
817 | float: left;
818 | }
819 | }
820 | }
821 | @media (max-width: 700px) {
822 | .main-content .graph-text {
823 | margin-top: 15px;
824 | }
825 | .main-content .graph-input {
826 | margin: 21px;
827 | }
828 | }
829 | @media (min-width: 992px) {
830 | .container {
831 | padding: 0 35px;
832 | width: 100%;
833 | }
834 | .left-content-wrap {
835 | .loan-content {
836 | width: 50%;
837 | float: left;
838 | margin: 0;
839 | & + .loan-content {
840 | margin: 0;
841 | }
842 | }
843 | }
844 | .left-content {
845 | border-right-width: 1px;
846 | }
847 | }
848 | @media (min-width: 992px) and (max-width: 1200px) {
849 | .left-content {
850 | min-width: 450px;
851 | padding: 0 20px;
852 | margin-bottom: 40px;
853 | }
854 | .emi-content {
855 | max-width: 100%;
856 | padding: 0 20px;
857 |
858 | .col-lg-7 {
859 | width: 60%;
860 | float: left;
861 | }
862 |
863 | .col-lg-5 {
864 | width: 40%;
865 | float: left;
866 | }
867 |
868 | .emi-footer {
869 | margin-top: 15px;
870 | }
871 | }
872 | .graph-container {
873 | padding: 0 10px;
874 | }
875 | }
876 | @media (min-width: 1200px) and (max-width: 1329px) {
877 | .container {
878 | padding: 0 35px;
879 | }
880 | }
881 | @media (min-width: 1330px) {
882 | .container {
883 | width: 1300px;
884 | }
885 | }
886 | .e-grid#scheduleGrid > .e-gridheader .e-headercontent .e-table colgroup col:first-child,
887 | .e-grid .e-content .e-table#scheduleGrid_content_table > colgroup col:first-child {
888 | width: 0px !important;
889 | }
890 | .e-grid .e-detailrowexpand > div,
891 | .e-grid .e-detailrowcollapse > div {
892 | display: none;
893 | }
894 | .e-grid .e-row .e-row-toggle {
895 | display: inline-block;
896 | }
897 | #payment_pieChartPointHover_Border {
898 | opacity: 0;
899 | }
900 | .e-grid .e-detailrowcollapse, .e-grid .e-detailrowexpand{
901 | display: table-column;
902 | }
903 | .e-grid .e-icon-grightarrow,.e-grid .e-icon-gdownarrow {
904 | padding: 0px;
905 | }
906 | .e-treegrid .e-treegridexpand:hover::before, .e-treegrid .e-treegridcollapse:hover::before {
907 | color: #fff;
908 | }
909 | .e-grid.e-default .e-rowcell.e-lastrowcell:not(.e-xlsel-bottom-border) {
910 | border-bottom-width: 0px;
911 | }
--------------------------------------------------------------------------------
/src/assets/styles.scss:
--------------------------------------------------------------------------------
1 | @import 'ej2-base/styles/bootstrap.scss';
2 | @import 'ej2-buttons/styles/bootstrap.scss';
3 | @import 'ej2-popups/styles/bootstrap.scss';
4 | @import 'ej2-inputs/styles/bootstrap.scss';
5 | @import 'ej2-calendars/styles/bootstrap.scss';
6 | @import 'ej2-grids/styles/bootstrap.scss';
7 | @import 'ej2-treegrid/styles/bootstrap.scss';
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/syncfusion/ej2-showcase-angular-loan-calculator/db26a1309fb45e3f722327db0a9339cca9f27afa/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Essential JS 2 for Angular - Loan Calculator
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { AppModule } from './app/app.module';
4 |
5 |
6 | platformBrowserDynamic().bootstrapModule(AppModule)
7 | .catch(err => console.error(err));
8 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | @import './assets/styles.scss';
2 | @import './assets/index.scss';
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts"
10 | ],
11 | "include": [
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "compileOnSave": false,
4 | "compilerOptions": {
5 | "baseUrl": "./",
6 | "outDir": "./dist/out-tsc",
7 | "forceConsistentCasingInFileNames": true,
8 | "strict": true,
9 | "noImplicitOverride": true,
10 | "noPropertyAccessFromIndexSignature": true,
11 | "noImplicitReturns": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "sourceMap": true,
14 | "declaration": false,
15 | "downlevelIteration": true,
16 | "experimentalDecorators": true,
17 | "emitDecoratorMetadata": true,
18 | "moduleResolution": "node",
19 | "importHelpers": true,
20 | "target": "ES2022",
21 | "module": "ES2022",
22 | "useDefineForClassFields": false,
23 | "lib": [
24 | "ES2022",
25 | "dom"
26 | ]
27 | },
28 | "angularCompilerOptions": {
29 | "enableI18nLegacyMessageIdFormat": false,
30 | "strictInjectionParameters": true,
31 | "strictInputAccessModifiers": true,
32 | "strictTemplates": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "src/**/*.spec.ts",
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------