In my client Angular project I have a component called report-viewer. Inside it I have a form, EmailSettings, which is called when a button from component is pressed.
The form code is the following:
EmailSettings.html
<h1 mat-dialog-title>Email Settings</h1>
<div mat-dialog-content>
<form id="formID" class="example-form">
<mat-form-field *ngIf="data.smtpHost === ''" class="example-full-width" appearance="fill">
<mat-label>SMTP Host</mat-label>
<textarea matInput placeholder="Ex: smtp.gmail.com"></textarea>
</mat-form-field> <br>
<mat-form-field *ngIf="data.smtpPort === ''" class="example-full-width" appearance="fill">
<mat-label>SMTP Port</mat-label>
<textarea matInput placeholder="Ex: 587"></textarea>
</mat-form-field> <br>
<mat-form-field *ngIf="data.smtpUserName === ''" class="example-full-width" appearance="fill">
<mat-label>SMTP User Name</mat-label>
<textarea matInput placeholder="Ex: example@gmail.com"></textarea>
</mat-form-field> <br>
<mat-form-field *ngIf="data.smtpUserPassword === ''" class="example-full-width" appearance="fill">
<mat-label>SMTP User Password</mat-label>
<textarea matInput placeholder="Ex: password"></textarea>
</mat-form-field> <br>
<mat-form-field *ngIf="data.smtpFrom === ''" class="example-full-width" appearance="fill">
<mat-label>SMTP From</mat-label>
<textarea matInput placeholder="Ex: example@gmail.com"></textarea>
</mat-form-field> <br>
<mat-form-field *ngIf="data.smtpDisplayName === ''" class="example-full-width" appearance="fill">
<mat-label>SMTP Display Name</mat-label>
<textarea matInput placeholder="Ex: Jhon"></textarea>
</mat-form-field> <br>
<button mat-button (click)="saveXML()">Save</button>
<!-- <mat-form-field class="example-full-width" appearance="fill">
<button mat-button (click)="saveXML()">Save</button>
</mat-form-field> -->
</form>
</div>
EmailSettings.ts
import { Component, OnInit, ViewChild, ViewEncapsulation, ChangeDetectorRef, Inject } from '@angular/core';
import * as FileSaver from 'file-saver';
import { MatInputModule } from '@angular/material/input';
@Component({
selector: 'app-EmailSettings',
encapsulation: ViewEncapsulation.None,
templateUrl: './EmailSettings.html',
})
export class EmailSettings{
appIdOpts: any = [];
saveXML(){
console.log("TestXML");
let blob = new Blob([document.getElementById('formID').innerHTML], {type: "text/xml"});
FileSaver.saveAs(blob, "blobExport.xml");
}
}
When I press on form's Save button I get the error ERROR TypeError: _co.saveXML is not a function
.
I know that there is already a post about it, but didn't help me solve the error. What could I do?
EDIT:
As @SirOneOfMany suggested, I have created a new component, EmailSettings. My new structure is at follows:
As you have requested, my report-viewer.component.ts has the following content. Note that it contains some other functions which have no relevance to this topic. The function is relevant for us is called openDialog()
.
import { Component, OnInit, ViewChild, ViewEncapsulation, ChangeDetectorRef, Inject } from '@angular/core';
import 'devexpress-reporting/dx-richedit';
import { DxReportViewerComponent } from 'devexpress-reporting-angular';
import { HttpClient } from '@angular/common/http';
import DevExpress from '@devexpress/analytics-core';
import List from "devextreme/ui/list";
import { DxSelectBoxModule, DxCheckBoxModule, DxListModule } from 'devextreme-angular';
import { ParagraphPropertiesKeepLinesTogetherDescriptor } from 'devexpress-richedit/lib/core/model/paragraph/paragraph-property-descriptors';
import { DxDropDownBoxModule, DxTreeViewModule, DxDataGridModule, DxTreeViewComponent, } from 'devextreme-angular';
import DXAnalytics from '@devexpress/analytics-core/dx-analytics-core'
import * as ko from 'knockout';
import { MatDialog, MAT_DIALOG_DATA } from '@angular/material';
import { MatInputModule } from '@angular/material/input';
import { EmailSettingsComponent } from '../email-settings/email-settings.component';
@Component({
selector: 'app-report-viewer',
encapsulation: ViewEncapsulation.None,
templateUrl: './report-viewer.component.html',
styleUrls: [
"./report-viewer.component.css",
"../../../node_modules/jquery-ui/themes/base/all.css",
"../../../node_modules/devextreme/dist/css/dx.common.css",
"../../../node_modules/devextreme/dist/css/dx.light.css",
"../../../node_modules/@devexpress/analytics-core/dist/css/dx-analytics.common.css",
"../../../node_modules/@devexpress/analytics-core/dist/css/dx-analytics.light.css",
"../../../node_modules/devexpress-reporting/dist/css/dx-webdocumentviewer.css"
]
})
export class ReportViewerComponent implements OnInit {
@ViewChild(DxReportViewerComponent, { static: true }) viewer: DxReportViewerComponent;
@ViewChild('selectedReport', { static: false }) public selectedReport: "C:\\ReportDesigner\\Reports";
@ViewChild("emailSettings", { static: true }) emailSettings: EmailSettingsComponent;
// @ViewChild('selectedReport', { static: false }) public selectedReport: "@/Reports";
treeBoxValue: 'selectedReport';
isTreeBoxOpened: boolean;
title = 'DXReportViewerSample';
hostUrl = 'http://localhost:54111/';
invokeAction: string = "/WebDocumentViewer/Invoke";
reportUrl: string = 'Employee';
//some other functions here...
ngOnInit() {
var ajaxSetup = DXAnalytics.Analytics.Utils.ajaxSetup
ajaxSetup.ajaxSettings = {
xhrFields: {
withCredentials: true
}
};
}
openDialog(){ //this functions is used to open the form which was on EmailSettings.html)
this.dialog.open(EmailSettingsComponent, {
data: {
// animal: 'panda',
smtpHost: '',
smtpPort: '',
smtpUserName: '',
smtpUserPassword: '',
smtpFrom: '',
smtpDisplayName: '',
},
});
}
}
export interface DialogData {
smtpHost: any;
smtpPort: any;
smtpUserName: any;
smtpUserPassword: any;
smtpFrom: any;
smtpDisplayName: any;
}
@Component({
selector: 'app-email-settings',
templateUrl: 'email-settings',
})
export class EmailSettings {
constructor(@Inject(MAT_DIALOG_DATA) public data: DialogData) {}
}
At this point I get the error ERROR in ./src/app/report-viewer/report-viewer.component.ts Module not found: Error: Can't resolve './email-settings' in 'C:\ProiectVisualStudio\ProjectName\JS\angular-report-designer\src\app\report-viewer'
As I already stated in the comments section you will have to declare a component. Just adding a decorator is not enough. Every class you add @Component(...) to needs to be added to the declarations array of a module.
I assume you just have one single AppModule class. So you will need to add your component to this module.
You used
@Component({
selector: 'app-EmailSettings',
encapsulation: ViewEncapsulation.None,
templateUrl: './EmailSettings.html',
})
So you will need to add your EmailSettings class to your modules declarations array:
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent, ReportViewerComponent, EmailSettings ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
That is why I said you should create your components with ng schematics, because angular already does this for you.
So just use:
ng generate component relative/path/in/src/folder/email-settings
This command will create the files for your component (.ts, .html, .(s)css, .spec.ts) and add the component to the closest module.
Have a look at this article for a more precise explanation about modules
EDIT
Thanks for editing your question. At the very end of your report-viewer component you have the following that might throw an error
@Component({
selector: 'app-email-settings',
templateUrl: 'email-settings', // this should throw the error
})
export class EmailSettings {
constructor(@Inject(MAT_DIALOG_DATA) public data: DialogData) {}
}
Remove that class and use your newly generated component. And as a small best practice hint:
Just implement only one class decorated via @Component per file
Use ng schematics to generate code, that way you can ensure your stuff is consistent through your whole app.
Give each interface a single file and move it to a models folder