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...
}
Related
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>
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>
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"
This might be answered, but damn if I can find it . I am creating a module, and its working, the issue is I want to assign a property to another property on my module.
so in angular 2 (with ng-module) I have created a simple panel
<simple-panel name="MyPanel"></simple-panel>
I have it working great, the issue is I want to assign a property to the name Property now and I have no idea what is the best way to do this.
so I would like to return {{MyPanel.thisProperty}} for use on the page where I am calling the tag.
here is a sample of what I am doing, stripped down for this question
here is my simple-panel.ts
import {Component,NgModule,ModuleWithProviders, Directive, Input} from '#angular/core';
/**
* Content of the edit panel.
*/
#Directive({
selector: 'simple-panel-edit-panel-content'
})
export class SimplePanelEditPanelContent {}
#Component({
selector: 'simple-panel',
templateUrl: 'simple-panel.html',
styleUrls: ['simple-panel.css'],
encapsulation: ViewEncapsulation.None
})
export class SimplePanel{
private _name: string;
private _announceedit: boolean = false;
private _buttonname: string = 'edit';
/** This sets the Name as a variable that can be used. */
#Input()
get name(): string { return this._name; }
set name(value) { this._name = value; }
/**Sets the Edit Announcement */
#Input()
get editannounce(): boolean { return this._announceedit; }
set editannounce(value: boolean) {
if (!value) {
this._announceedit = true;
this._buttonname = 'search';
}else{
this._announceedit = false;
this._buttonname = 'edit';
}
}
}
#NgModule({
exports: [SimplePanel,SimplePanelEditPanelContent],
declarations: [SimplePanel,SimplePanelEditPanelContent],
})
export class SimplePanelComponent {
static forRoot(): ModuleWithProviders {
return {
ngModule: SimplePanelComponent,
providers: []
};
}
}
here is the simple-panel.html
<md-card>
<md-card-title-group>
<button md-raised-button (click)="editannounce=editannounce;"><md-icon>{{ _buttonname }}</md-icon></button>
</md-card-title-group>
<md-card-content>
<ng-content select="simple-panel-edit-panel-content"></ng-content>
</md-card-content>
<md-card-actions>
<button md-raised-button (click)="editannounce = editannounce"><md-icon>save</md-icon> SAVE</button>
</md-card-actions>
</md-card>
when someone uses the module, a panel is created with a button
when someone clicks the button I can access the variable within the template above, but what I want to do is actually access a variable that is used on the page itself where they call the module to use. it would be nice to have it named MyPanel.announceedit or MyPanel.editable as an example, but the main thing is that a variable is created, and watched, when it changes it passes it back up to where the module is bieng used and allows user the ability to access it within the content area, so if they added an input and wanted to see if the button was clicked to set the readOnly attribute they could. Hopefully this makes more sense.
If you write it like
<simple-panel [name]="MyPanel"></simple-panel>
in the component that includes this html, you can access/set MyPanel with a simple this.MyPanel.
And in your SimplePanel component
#Input() name;
...
this.name = "something";
is again all you need to set and get that field.
I have created two components named as Alert ticker and dropdown.
These two are separate component.
So lets come to the point.
I am using this alert ticker in some other page named as Dashboard.
so I am passing the callback function from dashboard page to ticker component and ticker component pass this callback function to dropdown component.
Dashboard.html
<ticker [data]="data"
poll-time="10"
[filter-data]="filterData"
(on-polling)="onTickerPoll($event);"
(on-scroll)="onTickerScroll($event);"
(on-selection)="onTickerFilterChange($event);"></ticker>
Ticker.html
<dropdown [list]="ctrl.filterData" (on-selection)="ctrl.onSelection($event)" ddl-class="form-control"></dropdown>
Ticker.ts
#Component({
selector: 'od-ticker',
directives: [DropdownComponent],
templateUrl: '/app/components/ticker/ticker.html'
})
export class TickerComponent {
#Input() data;
#Output() onSelection: EventEmitter < string > = new EventEmitter();
}
But I am unable to do this .
Please advice.
There is a nice discussion on the Angular2 issue tracker about: "Why EventEmitter doesn't bubble up ?". This discussion is here.
So I think that you have to expose the event in the whole hierarchy of components...
I propose the following solution :
Dashboard.ts :
import {Component} from 'angular2/core';
import {MyTicker} from './my-ticker'
#Component({
selector: 'dashboard-app',
template: `<my-ticker [datas]="dashBoardDatas" (onSelection)="selectValueChanged($event)"></my-ticker>`,
directives: [MyTicker],
})
export class DashboardApp {
private dashBoardDatas: Array<string>;
constructor(){
this.dashBoardDatas = ["toto", "tata"];
}
selectValueChanged(value: string){
console.log('Selected value changed to :' + value);
}
}
And MyTicker.ts :
import {Component, Input, Output, EventEmitter, AfterViewInit} from 'angular2/core';
import {MySelect} from './my-select'
#Component({
selector: 'my-ticker',
template: `
<p>
<my-select [datas]="datas" (onSelection)="onSelection.emit($event)"></my-select>
</p>
`,
directives: [MySelect]
})
export class MyTicker {
#Input() datas: Array<string>;
#Output() onSelection: EventEmitter < string > = new EventEmitter();
constructor() {}
ngAfterViewInit(){
console.log("Datas in ticker : " + this.datas);
}
}
And MySelect.ts :
import {Component, Input, Output, EventEmitter} from 'angular2/core';
#Component({
selector: 'my-select',
template: `
<p>
<select name="filter" (change)="onSelection.emit($event.target.value)">
<option *ngFor="#item of datas" value="{{item}}">{{item}}</option>
</select>
</p>
`
})
export class MySelect {
#Input() datas: Array<string>;
#Output() onSelection: EventEmitter < string > = new EventEmitter();
constructor() {}
}
I did the following Plunkr to illustrate this.
As you can see, in the Dashboard, I have a method that I want to trigger when the content of the ticker's select change (selectValueChanged) (that is triggered when my-select selection change)... So we have to re-define the event emitter in "MyTicker".
The ticker and select contains the same members as in you question. An event emitter, and the filter's datas. When the select value change, I emit the event (thanks to (change)="onSelection.emit($event.target.value)" ).
This event is "catched" by the ticker and re-emited, and then catched by dashboard (thanks to "(onSelection)="selectValueChanged($event)". So the method "selectValueChanged" of Dashboard is called when the event "OnSelection" of MySelect is fired.
Thanks guys for your help.
I have got the way to accomplish this.
#Output() onScroll: EventEmitter<string> = new EventEmitter();
#Output() onSelection: EventEmitter<string> = new EventEmitter();
onFilterChange(data){
this.onSelection.next(data);
}
onScrollEnd(){
this.onScroll.next({});
}
Ticker.html
<dropdown [list]="ctrl.filterData" (on-selection)="ctrl.onFilterChange($event)" ddl-class="form-control"></dropdown>