How to include angular components in doc.fromhtml() jspdf? - html

I am trying to generate a pdf which includes angular components.
typescript:
let doc = new jsPDF();
let source = document.getElementById("content");
doc.fromHTML(
source,
15,
15,
{
'width': 180
});
doc.save("sample.pdf");
}
}
html:
<div id="content">
<sample-card *ngFor="let x of list; let i = index"
[selection] = list
<sample-card>
</div>
I am using Angular 4
doc.fromHTML() is working for simple 'div' like To be downloaded. But its not working for angular components. How to achieve this?

I did in this way ,
Passsing values in app html to the details, where it display those values in details html
In app.component.html
<div id="content" details [app1]="app" *ngFor="let app of applications">
In details.component.html
{{app1.labels}}
Here i'm waiting to get the data to be loaded[Tried with AfterViewChecked -- keeps on saving files and AfterViewInit is giving null (as it is executing only once)]
So waited for Random time say 5 sec[not good but its downloading the file]
setTimeout(()=>{
setTimeout(this.saveFile(), 1000*5);
},3000);
Here is the output i got[on left pdf and on right webpage with some extra content]
For the id on top div
<div id="content" >
Things to Learn
<div details [app1]="app" *ngFor="let app of applications">
</div>
Hari
</div>
Output is:
Method-2 :
Here i am using event emitter to tell parent when to save the file
when the child component receives the final item from the array of items [passed by parent to child] ,telling parent to save the file now [as i have received all of your items in the array]
Implemented with AfterViewInit where it emits true when length and count are same
On left html and on right ts [of parent]
on left ts and on right html [of child]
Output

Related

Bind external HTML component file to json data from calling page in Angular

with Angular 14, I'm using an ag grid to display rows of data in my main component page. I select a row, click a button and I want to call an auxiliary component HTML file in a separate directory that contains its own typescript file. I am able to collect the row data as JSON data but I want to insert the auxiliary component HTML into my main page.
Q: How can I bind the auxiliary html component to appear in my main html?
Directory Structure
ClientApp
src
app
MyObject
MyObject-grid-main
MyObject-grid-main.css
MyObject-grid-main.html
MyObject-grid-main.ts
MyObject-grid-auxiliary
MyObject-grid-auxiliary.css
MyObject-grid-auxiliary.html
MyObject-grid-auxiliary.ts
MyObject-grid-main.html
<button (click)="getSelectedRowData()">Get More Info</button>
<ag-grid-angular #agGrid id="ag-grid"></ag-grid/angular>
...
<div>
<!-- Here I want to insert MyObject-grid-auxiliary.html after I bind the JSON data -->
</div>
MyObject-grid-main.ts
#ViewChild('agGrid' grid!: GridApi;
getSelectedRowData()
{
const selectedData = this.grid.getSelectedRows();
const jsonDataString = JSON.stringify(selectedData);
alert(`Selected Data:\n${jsonDataString}`); // display in popup
// Here is where I want to bind the JSON data to the MyObject-grid-auxiliary.html
// and have it appear in the place holder above
}
MyObject-grid-auxiliary.html
<h2 class="title">{{data.title}}</h2>
...
<span>{{data.remarks}}</span>
...
<tr *ngFor="let pos of data.locations">
<td>
<span>{{pos.position.lat}}</span>
</td>
<td>
<span>{{pos.position.long}}</span>
</td>
<td>
<span>{{pos.lastReportedOnDateTime}}</span>
</td>
</tr>
you can simply pass a MyObject-grid-main.ts class property set at first to null as MyObject-grid-auxiliary input and then check in the auxiliary component with an ngIf when the data are set, then show your stuff.
in getSelectedRowData set the class property and content should will be display in the auxiliar component
Update with example:
in your MyObject-grid-main.ts add a new property called
auxiliarData = null;
in MyObject-grid-main.html add the auxiliary component where you need.
<myobject-grid-auxiliary [data]="auxiliarData"></myobject-grid-auxiliary>
in the function getSelectedRowData add the update logic, example
const selectedData = this.grid.getSelectedRows();
const jsonDataString = JSON.stringify(selectedData);
alert(`Selected Data:\n${jsonDataString}`);
...
this.auxiliarData = selectedData;
in MyObjectGridAuxiliary.ts
export class MyObjectGridAuxiliary {
#Input() data = '';
}
in MyObject-grid-auxiliary.html
<div *ngIf="data"> put your html here ... </div>
so, whenever the MyObjectGridAuxiliary.data is defined the component will show you html with your data.

Angular. Getting wrong data from ngb-panel

I use ngb-accordion in my app. I am trying to get data from every panel but when the first panel is opened click from the second panel returns me wrong data.
Result
I think the problem is the event which raises when input file changes.
Stackblitz Link
I will be glad if someone give me a hint for solving this problem.
There are few things to note in your code.
Your *ngFor is at ngb-accordion which is creating a new accordion for every loop, instead of creating multiple panel within one accordion.
Fix: <ngb-panel *ngFor="let data of datalist; let i = index">
You are using the same label for all three panels, because of which your first panel is opening every time, regardless of which panel you are clicking.
Fix: <label [for]="'image-input-' + i"> and <input ... [id]="'image-input-' + i"
The modal that opens after image selection has no knowledge of which panel it's getting triggered from. So, you have to use your (change)="onFileChange($event, data)" event/function to keep track of selected panel/corresponding data.
Then you can pass that selection from your modal to your processFile(...)
Fix:
export class AppComponent {
...
selectedData: Data;
...
...
onFileChange(event: any, data): void {
...
this.selectedData = data;
}
}
html:
...
<input ... (change)="onFileChange($event, data)>
...
...
<button
...
(click)="processFile(imageInput, selectedData)"
> Done
</button>
Stackblitz Demo

IONIC : Loading spinner while UI completes its rendering

I have about 1000 Ui components to render, Which includes ion-range,ion-select with JSON data.
I am assigning my JSON Object array to the variable and rendering the components in HTML with *ngFor.
For e.g. :
In .ts :
let jsonArray=this.jsonData;
In .HTML :
<div *ngFor="let array of jsonArray">
// MY COMPONENTS
</div>
Now, I am getting data in variable ion less than 1ms. But my components taking so much time to render and it feels like screen is freezed.
Issue 1 : Can we reduce this rendering time by using anything other than this approach?
Issue 2 : Can we Add spinner or loading until its completes its rendering?
PS.: I am mentioning here that I have no issues with getting my data as it is from local sqliteDB. My Ui is getting stucked only while rendering the 1000 components.
UPDATE of .ts file
onSelect(dbId) {
this.dbService.getData(dbId).then(data => {
this.toDisplayArray=data.set;
})
}
Update of .HTML file
<ion-select interface='popover' (ionChange)='onSelect($event.detail.value)'>
<ion-select-option *ngFor="let set of deviceList; let i of index" value="{{set.Id}}">
{{set.name}}</ion-select-option>
</ion-select>
<div *ngFor="let set of toDisplayArray;let i = index">
<div *ngIf="set.type==="range">
<range [id]="set.id" [title]="set.title"></range>
</div>
<div *ngIf="set.type==="select">
<select[id]="set.id" [title]="set.title"></select>
</div>
<div *ngIf="set.type==="both">
<range-select [id]="set.id" [title]="set.title"></range-select>
</div>
</div>
so range,select and range-select are selector of my child components where I am passing the data through input and output.

Reusing pages in WinJS's Pivot

I'm developing app for Windows Phone 8.1 using WinJS and I used Visual Studio's template for pivot application. My Applications queries external API and displays results in PivotItem. Since there are three very similar queries that reurn same type of data, I'd like to reuse one code for all the sections in Pivot. The PivotItem page consist basically only of ListView with items received from API. My section page javascript looks like this:
var ControlConstructor = WinJS.UI.Pages.define("/pages/bookmarks/sectionPage.html", {
ready: function(element, options) {
//Here I call API based on received option and render the page
}
}
WinJS.Namespace.define("bookmarksApps_SectionControls", {
SectionControl: ControlConstructor
});
My page declaring the Pivot looks like this:
<div class="bookmarks" data-win-control="WinJS.UI.Pivot" data-win-res="{ winControl: {'title': 'BookmarksTitle'} }">
<div class="section1 section" data-win-control="WinJS.UI.PivotItem" data-win-options="{ isHeaderStatic: true }" data-win-res="{ winControl: {'header': 'BookmarksNew'} }">
<div class="sectioncontrol" id="section1contenthost" data-win-control="bookmarksApps_SectionControls.SectionControl" data-win-options="{'section': 'new'}"></div>
</div>
<div class="section2 section" data-win-control="WinJS.UI.PivotItem" data-win-options="{ isHeaderStatic: true }" data-win-res="{ winControl: {'header': 'BookmarksAll'} }">
<div class="sectioncontrol" id="section2contenthost" data-win-control="bookmarksApps_SectionControls.SectionControl" data-win-options="{'section': 'all'}"></div>
</div>
<div class="section3 section" data-win-control="WinJS.UI.PivotItem" data-win-options="{ isHeaderStatic: true }" data-win-res="{ winControl: {'header': 'BookmarksHistory'} }">
<div class="sectioncontrol" id="section3contenthost" data-win-control="bookmarksApps_SectionControls.SectionControl" data-win-options="{'section': 'history'}"></div>
</div>
</div>
Now, when I open the app,pivot page correctly loads and displays first section with data. But when I swipe the different section, new data is loaded (so the ready function is called, but nothing is displayed (page is blank, only PivotItems' headers are visible). But if I swipe back to section1, it contains data, that I want to display in section2.
Is it possible to reuse my SectionPage.html and SectionPage.js in different PivotItems, preferably without too much of boilerplate code?
You need to create custom HTML control which will host these pages, custom control can accept uri as data-win-options, then inside your control you can have updateLayout() which will render the page and append to parentElement.
Sample code in update layout method:
var options = {} //Page options
if (!this._isLoaded) {
this._isLoaded = true;
WinJS.UI.Pages.render(this.uri, this._pageElement, options);
}
I found source of my problem. In page /pages/bookmarks/sectionPage.html I had <div> with an id meant for holding my ListVIew. And I was getting win control for listview using document.getElementById("listViewId").winControl. This is wrong, because then I had three divs with same id (each for every section), so getElementById was always returning same list (the one on the first section).
So I changed getting of the wincontrol to
var discussionList = document.querySelector("#" + contentHost + " .disucssionsListView").winControl;
where contentHost depends on data-win-options received from main page and everything works as expected.

How do I generate nested DOM elements based on an AJAX result in Angular?

I'm new to Angular so be gentle with me! I'm looking at rendering subsection DOM elements based on an AJAX response, how do I go about implementing the below in Angular?
On page load a list of section headers is returned from the controller:
Clicking on any of these sections (red) would show a subsection list (blue), each of these blue headers can be clicked to show another list (black), bearing in mind I only want to show the immediate-child sections for each section/subsection/sub-subsection header click:
I've got the template code I want to use for each of these, but how do I go about bringing these templates together?
So far from looking around I get the impression I should be creating a directive for the section, sub-section and sub-sub-section (yes?), can I then bind a template to the result of an HTTP Service call? I.e expanding as the detail screenshot above:
<div area="PSED">
Personal, Social and Emotional Development
<div aspect="MH">
Making Relationships
<div goal="BLAH">
<input type="checkbox"> Blah, Blah, Blah
</div>
</div>
</div>
I was hoping to reduce page load time by returning as little data as necessary and populating sections as-required by the user.
I hope this is a reasonable question as I couldn't find anything demonstrating what I need (perhaps my ignorance of ng was causing me to omit an important keyword from my searches).
Thanks in advance for any advice provided.
Andy
If I understand the question, you are trying to dynamically add nodes to a tree-like structure after an ajax call. You can use a combination of ng-include and a recursive template to do this. Here's a rough example that doesn't include the logic for collapsing nodes but I think it gets the idea across.
View:
<script type="text/ng-template" id="tree_item_renderer.html">
<span ng-click="add(data)">{{data.name}}</span>
<ul>
<li ng-repeat="data in data.nodes" ng-include="'tree_item_renderer.html'">
</li>
</ul>
</script>
<ul ng-app="Application" ng-controller="TreeController">
<li ng-repeat="data in tree" ng-include="'tree_item_renderer.html'"></li>
</ul>
Controller:
angular.module("myApp", []).
controller("TreeController", function($scope, $http) {
$scope.delete = function(data) {
data.nodes = [];
};
$scope.add = function(data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
//make your call here and set your child node data
//$http.get('...').then(function(res){
// data.nodes.push({name: newName,nodes: res.data});
//});
//test data
data.nodes.push({name: newName,nodes: []});
};
$scope.tree = [{name: "Top Node", nodes: []}];
});
Working jsFiddle: http://jsfiddle.net/nfreeze/c9mrhxf2/1/