Angular Generate a PDF document from a DIV class in HTML - angularjs

I am working on an Invoicing app with angular.
Inside a DIV class, I have generated a preview of my Invoice.
I want to be able to generate a PDF file from this Div Class. Is it possible? Do you have any hint? What would another way?
Warms Regards
Mathieu

jsPDF is able to use plugins. In order to enable it to print HTML, you have to include certain plugins and therefore have to do the following:
Go to https://github.com/MrRio/jsPDF and download the latest Version.
Include the following Scripts in your project:
jspdf.js
jspdf.plugin.from_html.js
jspdf.plugin.split_text_to_size.js
jspdf.plugin.standard_fonts_metrics.js
If you want to ignore certain elements, you have to mark them with an ID, which you can then ignore in a special element handler of jsPDF. Therefore your HTML should look like this:
<!DOCTYPE html>
<html>
<body>
<p id="ignorePDF">don't print this to pdf</p>
<div>
<p><font size="3" color="red">print this to pdf</font></p>
</div>
</body>
</html>
Then you use the following JavaScript code to open the created PDF in a PopUp:
var doc = new jsPDF();
var elementHandler = {
'#ignorePDF': function (element, renderer) {
return true;
}
};
var source = window.document.getElementsByTagName("body")[0];
doc.fromHTML(
source,
15,
15,
{
'width': 180,'elementHandlers': elementHandler
}
);
doc.output("dataurlnewwindow");

When I open my PDF, I have a white page, and the PDF keeps on loading:
Here is my code:
component.show.ts
import { Component, OnInit } from '#angular/core';
import { BasicRequestsService } from '../../../../../core/services/basic-requests.service';
import { ActivatedRoute } from '#angular/router';
import { Observable, forkJoin } from 'rxjs';
import { FormsModule, FormBuilder } from '#angular/forms';
import { DatePipe } from '#angular/common';
import * as jsPDF from 'jspdf';
import { ElementRef, ViewChild} from "#angular/core";
import { ToastsService } from '../../../../../core/services/toasts.service';
import { Pipe, PipeTransform } from '#angular/core';
#Component({
selector: 'app-tools-billing-invoices-show',
templateUrl: './show.component.html',
styleUrls: ['./show.component.scss', '../invoices.component.scss']
})
export class InvoicesShowComponent implements OnInit {
endpoint = '/tools/billing/billers/';
billerId;
billerData;
invoiceId;
invoice;
invoiceUpdated;
invoiceLines;
invoiceForm;
clientData;
symbol;
invoiceTotal = {taxExcluded: 0, tax: 0, taxIncluded: 0};
date: Date;
constructor(
private basicRequestsService: BasicRequestsService,
private route: ActivatedRoute,
private formBuilder: FormBuilder,
private toasts: ToastsService,
) { }
createPdf(): void {
var doc = new jsPDF();
var elementHandler = {
'#ignorePDF': function (element, renderer) {
return true;
}
};
var source = window.document.getElementsByTagName("invoice-preview-box")[0];
doc.fromHTML(
source,
15,
15,
{
'width': 180,'elementHandlers': elementHandler
});
doc.output("dataurlnewwindow");
}
}
Here is my HTML
<button class="dropdown-item" (click)="createPdf()">Generate PDF</button>
<div class="col-xl-9 invoice-preview-box" tag="invoice-preview-box" id="invoice-preview-box"> Hello World, here is MY PDF </div>

Related

Migrate AngularJS to Angular 8

i need to put this code line (angularJS) in angular 2+ , how can i do this?
I already do some searchs but this line is confusing me.
scope.variable.value = event.color.toHex()
Old Code:
function callColorpicker() {
var colorpicker = $('.colorpicker')
colorpicker.colorpicker().on('changeColor', function(event) {
var scope = angular.element(this).scope()
scope.variable.value = event.color.toHex()
if ($scope.autoapplysass) {
$scope.autoApplySass()
}
})
}
New Code:
callColorpicker() {
var colorpicker = $('.colorpicker')
colorpicker.colorpicker();
var _this = this;
colorpicker.colorpicker().on('changeColor', function(event) {
--> scope.variable.value = event.color.toHex()
if (_this.applySass) {
_this.applySass(false)
}
})
}
In general, if you can avoid jQuery with Angular it's a good thing because Angular handles things completely differently than JQ and in many ways the two step on each other's toes. This can be difficult to troubleshoot and down the line could cause issues that are increasingly harder to diagnose.
In your case, as luck would have it, there is an angular color picker component you could substitute with.
NPMJS page: https://www.npmjs.com/package/ngx-color-picker
Demo: https://zefoy.github.io/ngx-color-picker/
StackBlitz: https://stackblitz.com/github/zefoy/ngx-color-picker/tree/master
install with npm npm install ngx-color-picker --save
Then in app.module.ts
import { ColorPickerModule } from 'ngx-color-picker';
#NgModule({
...
imports: [
...
ColorPickerModule
]
})
In your HTML
<input [(colorPicker)]="color"
[style.background]="color"
(colorPickerChange)="onColorChange($event)" />
and in your component.ts
import { Component, ViewContainerRef } from '#angular/core';
import { ColorPickerService, Cmyk } from 'ngx-color-picker';
constructor(public vcRef: ViewContainerRef, private cpService: ColorPickerService) {}
export class AppComponent {
color: string;
onColorChange(newColor) {
this.color = newColor;
if (_this.applySass) {
_this.applySass(false)
}
}

Display values from API In Angular 6 page

I am completely new to frontend dev and trying to display API data in an Angular 6 application and can't figure out how to do it.
I can display values in the top level of the returned details but it's the sub level details I am struggling with.
I am using an Angular 6 app using Routing.
Below is all my code
Homepage.component.html
<h2>Book ID List</h2>
<button (click)="getBooks()">Get</button>
<div *ngFor="let book of books.items">
<p>ID: {{book.id}}</p>
</div>
I can get the 'ID'
I am using a service to get the data from the test API
Service.component.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class ApiServiceService {
url = 'https://www.googleapis.com/books/v1/volumes?q=HTML5 Wire-frames';
constructor(private http: HttpClient) { }
private extractData(res: Response) {
const body = res;
return body || {};
}
getBooks(): Observable<any> {
return this.http.get(this.url).pipe(
map(this.extractData));
}
}
Homepage.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiServiceService } from '../../services/api-service.service';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
books: any = [];
constructor(private apiService: ApiServiceService) { }
ngOnInit() { }
getBooks() {
this.books = [];
this.apiService.getBooks().subscribe((data: {}) => {
console.log(data);
this.books = data;
});
}
}
At present this return the following:
What I want to do is display the value from the 'eBook' which is under the 'saleInfo' level. I know I need to the change the loop for each array returned in the HTML but this is where I'm stuck.
Also I'm not sure the code I have is the best, but it's working. As I said I'm new to all this, and can pull values from top level but not sublevels in the API.
I would recommend better naming for your service, Service.compoent.ts isn't ideal, api.service.ts is much more understandable.
Also you can see that when you subscribe, you are using data: {}, this means that the function should expect a value of type Object, but I would use any, since you use Observable<any>
Now for the problem.
I have created stackblitz which does just what you wanted. I think you have got confused with the comments. You don't want to change let book of books.items to let book of books because you would be iterating over object, which you cannot do in *ngFor.
Change the line this.books = data; to this.books.push(data);
Since, if it is this.books = data; and because the books is of type any. It will accept anything. So, now after this line, this.books = data; it becomes object which contains value of data variable. So, you should use,
this.books.push(data);
To make it behave like an array too. Then, you can access books with *ngFor.
So, now in the HTML you can access via *ngFor as:
<div *ngFor="let book of books">
<div *ngFor="let items of book.items">
<p>ID: {{items.id}}</p>
<p>ebook: {{items.saleInfo.isEbook}}</p>
</div>
</div>

Angular 2: How retrieve the changes that are done to an injected html?

I've inject HTML from an object that I created and than using a pipe I by pass the angular security to display input fields, text area etc..
I am inject an input field, some text and a <p contenteditable="true"> change me </p> that you can edit.
3) How to than track changes when user is updating the inner of and push it back to the object ?
If the user changes the HTML inside [innerHTML]="item.htmldata" is there a way to track it?
obj: Array<any> = [
{ htmldata: '<div> <strong> There should be a input field below </strong> <input type="text" value="search" /></div>' },
{ htmldata: '<div> <strong> me to, how are you </strong></div>'}
]
I am running through an ngFor loop and binding the data through angular [innerHTML]="item.htmldata"
<div *ngFor="let item of obj" >
{{item.htmldata | json }}
<div [innerHTML]="item.htmldata" | safeHtml></div>
<br />
<hr>
</div>
**SafeHTML pipe**
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer, SafeResourceUrl, SafeUrl} from '#angular/platform-browser';
DomSanitizer
#Pipe({name: 'safeHtml'})
export class Safe {
constructor(private sanitizer: DomSanitizer){}
transform(style) {
return this.sanitizer.bypassSecurityTrustHtml(style);
//return this.sanitizer.bypassSecurityTrustStyle(style);
// return this.sanitizer.bypassSecurityTrustXxx(style); - see docs
}
}
Plunker: http://plnkr.co/edit/ADeAEz81a07Cl2yrDGqQ?p=preview
Replace your sanitizedHtmlProperty by this code.
Sample Code
public get sanitizedHtmlProperty() : SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(this._originalHtmlProperty);
}
Plunker
Edit
You can use Blur and keyup
import {Directive, ElementRef, Input, Output, EventEmitter, OnChanges} from "#angular/core";
#Directive({
selector: '[contenteditableModel]',
host: {
'(blur)': 'onEdit()',
'(keyup)': 'onEdit()'
}
})
export class ContentEditableDirective implements OnChanges {
#Input('contenteditableModel') model: any;
#Output('contenteditableModelChange') update = new EventEmitter();
constructor(
private elementRef: ElementRef
) {
console.log('ContentEditableDirective.constructor');
}
ngOnChanges(changes) {
console.log('ContentEditableDirective.ngOnChanges');
console.log(changes);
if (changes.model.isFirstChange())
this.refreshView();
}
onEdit() {
console.log('ContentEditableDirective.onEdit');
var value = this.elementRef.nativeElement.innerText
this.update.emit(value)
}
private refreshView() {
console.log('ContentEditableDirective.refreshView');
this.elementRef.nativeElement.textContent = this.model
}
}
Reference
Edit 2
Here is code for tracking changes Plunker

Implementing ng2-completer

I am currently using ng2-completer (https://github.com/oferh/ng2-completer) for angular 2 and struggling to add in the suggestions to have the full response instead of just having one value.
Also, when the suggestion is selected, how is the method assigned to handle this?
The code I've got so far is:
import { Component } from '#angular/core';
import { AutoComplete } from './autocomplete';
import { CompleterService, CompleterData, RemoteData } from 'ng2-completer';
import { SearchComponent } from './search.component';
import { QueryService } from './query.service';
#Component({
selector: 'app-home',
template: `<ng2-completer [(ngModel)]="searchStr" [dataService]="dataService" [minSearchLength]="0" [inputClass]="['form-control input-list']" [autofocus]="['true']" [selected]="selected()"></ng2-completer>`,
//directives: [ AutoComplete ]
})
export class HomeComponent {
public searchStr: string;
private dataService: CompleterData;
constructor(private completerService: CompleterService, private queryService: QueryService) {
//this.dataService = completerService.local(this.searchData, 'color', 'color');
this.dataService = completerService.remote('http://localhost:61227/machine/?query=','ComputerHostname, AssetID', 'ComputerHostname').descriptionField('ComputerType');
//this.dataService = this.queryService.search(searchStr).then(items => this.items = items);
}
selected () {
console.log("test");
}
}
However it shows the following error:
Can't bind to 'selected' since it isn't a known property of 'ng2-completer'.
selected is an event and not a property and therefor the syntax for it (as described in Angular template syntax) should be (selected)="selected($event)"
autofocus expects a boolean value (see in ng2-completer doc) not an array so you should use [autofocus]="true"

Access local variable in Component fails in Angular 2

I want to get the DOM element and initialize a JSON Editor without using ElementRef.
Code:
import {Component, ViewChild} from 'angular2/core';
#Component({
selector: 'json-editor',
template: `
<div #container class="json-editor-container"></div>
`
})
export class JSONEditorComponent implements OnChanges {
#ViewChild('container') private container = null;
constructor() {
}
}
No matter how, this.container is still null. Which part of code I wrote is wrong?
Solution:
Before you access the ViewChild attribute, you must confirm the view has initialized. Also #VarChild returns ElementRef, if you want to process it further requires DOMElement, please use nativeElement attribute which is a Element
import {Component, ViewChild} from 'angular2/core';
#Component({
selector: 'json-editor',
template: `
<div #container class="json-editor-container"></div>
`
})
export class JSONEditorComponent implements OnChanges {
#ViewChild('container') private container = null;
private isViewInitialized: boolean = false;
constructor() {
}
ngAfterViewInit() {
this.isViewInitialized = true;
}
triggeredFromParentComponentOrWhatever() {
if (this.isViewInitialized) {
// Should work
console.log(this.container.nativeElement);
}
// Might not work as view might not initialized
console.log(this.container.nativeElement);
}
}
You can't access container in the constructor. It is only set just before ngAfterViewInit()
ngViewInit() {
container.nativeElement...
}

Resources