IONIC : Loading spinner while UI completes its rendering - html

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.

Related

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

Why databinding is not working in Angular 13?

I have created an event in my header component and trying to listen it in app component, but it is not working as expected.
In header.component.html, on clicking on "Recipes" it is sending 'recipe' string to "onSelect()" method and on clicking on "Shopping List", it is sending string 'shopping-list' to "onSelect()" method.
<li> Recipes</li>
<li> Shopping List</li>
In header.component.ts , I have created event "featureSelected" which is emitting data, whatever we receive in "onSelect()" method.
#Output() featureSelected = new EventEmitter<string>();
onSelect(feature:string){
this.featureSelected.emit(feature);
}
Now, we are listing event "featureSelected" in app.component.html, if it emits string "recipe" we load, recipe component else we load "shopping-list" component.
<app-header> (featureSelected)="onNavigate($event)"</app-header>
<div class="container">
<div class="row">
<div class="col-md-12">
<app-recipes *ngIf="loadedFeature === 'recipe'"></app-recipes>
<app-shopping-list *ngIf="loadedFeature !== 'recipe'"></app-shopping-list>
</div>
</div>
</div>
app.component.ts
loadedFeature='recipe';
onNavigate(feature:string){
this.loadedFeature=feature;
}
It is not loading shopping-list component on clicking on "Shopping List". I think listener is not working properly even though code looks fine. Please help me in finding the issue and please let me know, if any additional information required.
<app-header> (featureSelected)="onNavigate($event)"</app-header>
First of all this is not correct. You need to have output method inside of app-header like this.
<app-header (featureSelected)="onNavigate($event)"> </app-header>
And with that you can access in the onNavigate($event) and store it to some property.
There is stackblitz with working example StackBlitzDemo
If this helps mark it as answer pls.

Working with custom components for input generates "No value accessor for form control with path X->0->Y"

I have a working form taking the following HTML markup. No errors or warnings.
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
I transformed it into a custom component, which also works, as shown below.
<app-input-text [info]="'Title'"
formControlName="targetField"
ngDefaultControl></app-input-text>
In my next view, I need to use FormArray as follows - still working code.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
</div>
</div>
Now, I expected that combining both (i.e. being able to use custom input component and being able to form array for components) would post no problem. However, the sample below doesn't work.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<app-input-text [info]="'Title'"
formControlName="targetField"
class="col-sm-6"></app-input-text>
</div>
</div>
It generates the following error.
No value accessor for form control with path: 'stuff -> 0 -> targetField'
The custom component is design like this (although given that it works in the explicit markup example, I'm not sure if it's relevant information). The only (wild) guess I have might be that value isn't jacked into the form array field somehow.
export class InputTextComponent implements OnInit {
constructor() { this.value = new EventEmitter<string>(); }
#Input() info: string;
#Output() value: EventEmitter<string>;
onEdit(value: any): void { this.value.emit(value); }
}
The group and array creating in the current view is done like this (not sure if this is of any relevance neither, as it works for the explicit HTML markup case).
this.form = builder.group({
id: "",
stuff: builder.array([
builder.group({ targetField: "aaa" }),
builder.group({ targetField: "bbbb" }),
builder.group({ targetField: "cc" })
])
});
Is there a limitation in Angular in this regard that I'm not aware of? I'm rather sure there's not and that I'm just doing something fairly clever simply missing a tiny detail.
I do understand the error but I can't see how it relates to the code. The form can't find the 0th element in the array or that element has no field of that name. Since I do get to see a few rows, I know there must be a 0th element. Since I specified the name of the field, I know there is indeed such. What else am I missing?

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

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

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.