Kendo Grid for Angular2/4 - block grid while loading data - kendo-grid

I would like to block/disable Kendo Angular2/4 Grid when it is loading a data.
What is the best approach?
In my component i have a isWorking variable which is true/false if the data are loading(ajax API call):
export class GridFilterComponent {
public view: Observable<GridDataResult>;
public state: State = { skip: 0, take: 10 };
public isWorking = true;
The only option I found, without using is to hide/show grid using *ngIf="!isWorking" on the kendo-grid element, but it is a bit clunky and not very user-friendly.

Put grid in <div>
<div [ngClass]="isWorking ? 'gridDisabled': ''">
<kendo-grid [data]="gridData"></kendo-grid>
</div>
.gridDisabled{
pointer-events: none;
opacity: 0.5;
}
Short version:
<div [class.gridDisabled]="isWorking">
<kendo-grid [data]="gridData"></kendo-grid>
</div>

Related

Custom Blazor Server Component Not Invoking the OnClick Function

I have created a layout component in the same directory as with the MainLayout component. The CustomLayout #inherits LayoutComponentBase and has #Body which gets the content to be rendered inside the layout.
As obviously expected, the layout component has its own css files and UI display is fine. I am only having a problem with the #onclick function. It cannot be invoked. Is there a way to trigger the function on button click?
The reason I want to do this is that I am creating a navigation bar which will have to show/hide a dropdown. I hope I can get some ideas on this. Appreciated.
I am calling a server function this way: #onclick="SomeFuction" or #onclick="SomeFuction" or #onclick="#(() => SomeFuction())"
LayoutComponent:
#inherits LayoutComponentBase
#Body
Home page referencing a custom layout:
[AllowAnonymous]
[Layout(typeof(CustomLayoutComponent))]
[AllowAnonymous] is just for allowing anonymous access.
I am using Blazor Server and .NET 6
Additional Information:
I have put all the code in one page so that it becomes easier to read and understand.
Here is the LayoutComponent razor component:
#inherits LayoutComponentBase
<div class="navbar" id="custom-navbar">
<div class="wrapper">
<a>Home</a>
<a>Service</a>
<button class="btn btn-primary" #onclick="ToggleProductsUI">Products</button>
</div> </div>
<!--Component to show Products UI Component. Scoped css for styling-->
<ProductsComponent TValue="string" UIState="#ProductsUIState"></ProductsComponent>
#code{
// state of the Products submenu Component
string ProductsUIState { get; set; }
// a control function for toggling the Products UI
// this function somehow is not invoked.
void ToggleProductsUI()
{
if (string.IsNullOrWhiteSpace(ProductsUIState))
{
ProductsUIState = "show-ui";
return;
}
ProductsUIState = string.Empty;
}
}
Here is the ProductsUI component code (I have removed the unnecessary event to avoid confusion. The component can be fully controlled by the parent):
public partial class ProductsComponent<TValue> {
[Parameter]
public string? UIState { get; set; } }
Blazor pages that will use the custom layout component will point to it like this:
[AllowAnonymous]
[Layout(typeof(CustomLayoutComponent))]
public partial class Index
{
}
This is working fine. The issue is in the CustomLayoutComponent when trying to invoke the ToggleProductsUI() function
I accidentally inluded a <body> element in the layout and as such, the <script src="_framework/blazor.server.js"></script> was not being run hence the click event was not being invoked. I removed the <body> element. I was misled by the default body{} style in the underlining scopped css. Resolved.

#ContentChildren not picking up items inside custom component

I'm trying to use #ContentChildren to pick up all items with the #buttonItem tag.
#ContentChildren('buttonItem', { descendants: true })
This works when we have the ref item directly in the parent component.
<!-- #ContentChildren returns child item -->
<parent-component>
<button #buttonItem></button>
<parent-component>
But, if the element with the #buttonItem ref is wrapped in a custom component, that does not get picked by the #ContentChildren even when I set the {descendants: true} option.
<!-- #ContentChildren returns empty -->
<parent-component>
<child-component-with-button-ref></child-component-with-button-ref>
<parent-component>
I have created a simple StackBlitz example demonstrating this.
Doesn't appear to be a timeline for a resolution of this item via github... I also found a comment stating you cannot query across an ng-content boundary.
https://github.com/angular/angular/issues/14320#issuecomment-278228336
Below is possible workaround to get the elements to bubble up from the OptionPickerComponent.
in OptionPickerComponent count #listItem there and emit the array AfterContentInit
#Output() grandchildElements = new EventEmitter();
#ViewChildren('listItem') _items
ngAfterContentInit() {
setTimeout(() => {
this.grandchildElements.emit(this._items)
})
}
Set template reference #picker, register to (grandchildElements) event and set the $event to picker.grandchildElements
<app-option-picker #picker [optionList]="[1, 2, 3]" (grandchildElements)="picker.grandchildElements = $event" popup-content>
Create Input on PopupComponent to accept values from picker.grandchildElements
#Input('grandchildElements') grandchildElements: any
In app.component.html accept picker.grandchildElements to the input
<app-popup [grandchildElements]="picker.grandchildElements">
popup.component set console.log for open and close
open() {
if (this.grandchildElements) {
console.log(this.grandchildElements);
}
else {
console.log(this.childItems);
}
close() {
if (this.grandchildElements) {
console.log(this.grandchildElements);
}
else {
console.log(this.childItems);
}
popup.component change your ContentChildren back to listItem
#ContentChildren('listItem', { descendants: true }) childItems: Element;
popup.component.html set header expression
<h3>Child Items: {{grandchildElements ? grandchildElements.length : childItems.length}}</h3>
Stackblitz
https://stackblitz.com/edit/angular-popup-child-selection-issue-bjhjds?embed=1&file=src/app/option-picker/option-picker.component.ts
I had the same issue. We are using Kendo Components for angular. It is required to define Columns as ContentChilds of the Grid component. When I wanted to wrap it into a custom component and tried to provide additional columns via ng-content it simply didn't work.
I managed to get it working by resetting the QueryList of the grid component AfterViewInit of the custom wrapping component.
#ViewChild(GridComponent, { static: true })
public grid: GridComponent;
#ContentChildren(ColumnBase)
columns: QueryList<ColumnBase>;
ngAfterViewInit(): void {
this.grid.columns.reset([...this.grid.columns.toArray(), ...this.columns.toArray()]);
this.grid.columnList = new ColumnList(this.grid.columns);
}
One option is re-binding to the content child.
In the template where you are adding the content child you want picked up:
<outer-component>
<content-child [someBinding]="true" (onEvent)="someMethod($event)">
e.g. inner text content
</content-child>
</outer-component>
And inside of the example fictional <outer-component>:
#Component()
class OuterComponent {
#ContentChildren(ContentChild) contentChildren: QueryList<ContentChild>;
}
and the template for <outer-component> adding the <content-child> component, re-binding to it:
<inner-template>
<content-child
*ngFor="let child of contentChildren?.toArray()"
[someBinding]="child.someBinding"
(onEvent)="child.onEvent.emit($event)"
>
<!--Here you'll have to get the inner text somehow-->
</content-child>
</inner-template>
Getting that inner text could be impossible depending on your case. If you have full control over the fictional <content-child> component you could expose access to the element ref:
#Component()
class ContentChildComponent {
constructor(public element: ElementRef<HTMLElement>)
}
And then when you're rebinding to it, you can add the [innerHTML] binding:
<content-child
*ngFor="let child of contentChildren?.toArray()"
[someBinding]="child.someBinding"
(onEvent)="child.onEvent.emit($event)"
[innerHTML]="child.element.nativeElement.innerHTML"
></content-child>
You may have to sanitize the input to [innerHTML] however.

How to show a loading indicator until all async http calls are completed - Angular

My question is how to show a loading spinner until all of my async http requests are completed. This way I wouldn't show bits and pieces of the screen until all of the data is received from the server.
My biggest issue is that I have components that are triggered specifically through the html, so I can't simply put an *ngIf statement over part of the html when I want to show it.
Here's what I have so far. FYI, the Template variable that currently triggers the visibility of the html is set when one of the http requests complete in this component. I want to wait for the child component's http requests to complete before showing the html, but I must execute the logic in the html in order to call the child components.
The *ngIf statement does NOT currently work in the way I desire, I'm just showing what I'm currently doing.
<div class="col-sm-12"
*ngIf="Template">
<div id="nav" style="height: 200px">
<div id="outer"
style="width: 100%">
<div id="inner">
<o-grid>
</o-grid>
</div>
</div>
</div>
<collapsible-panel *ngFor="let s of Template?.s; let i = index"
[title]="s.header">
<div *ngFor="let c of s.c">
<fact [eC]="c.c"
[label]="c.l">
</fact>
</div>
</collapsible-panel>
<collapsible-panel title="T">
<div>
<i-f >
</i-f>
</div>
</collapsible-panel>
</div>
<div *ngIf="!Template" class="spinner"></div>
EDIT (SOLUTION): Here's the solution I implemented, per the answer below from #danday74.
I instantiated the variable inside of my service where I make all of my http requests. I defined it as true to start, and set it to false in one of the child components when the subscribe completes.
I'll just need to make sure in the future to set cService.asyncRequestsInProgress to false wherever the last async http request takes place, if it ever changes.
Parent HTML:
<div class="col-sm-12"
[ngClass]="{hideMe:cService.asyncRequestsInProgress}">
......
</div>
<div *ngIf="cService.asyncRequestsInProgress" class="spinner"></div>
Service:
#Injectable()
export class CService {
asyncRequestsInProgress: boolean = true;
constructor(public http: HttpClient) { }
}
Child Component (Where the last async request completes):
export class FComponent implements OnInit {
....
doSomething() {
this.cService.getWhatever().subscribe(x => {
this.cService.asyncRequestsInProgress = false;
}
}
}
styles.css
.hideMe {
visibility: hidden;
}
You could use a resolver. A resolver ensures data is loaded before the component loads.
Alternatively, if you don't want to use *ngIf you could just use [ngClass]="{hideMe: allAsyncRequestsComplete}" to style the bit you don't want to show until loading is complete. CSS might be:
.hideMe {
visibility: hidden;
}
And set allAsyncRequestsComplete to true when loading is done.
You can use resolvers for the loading, then in app.component.ts, set your variable to true or false depending on the event:
navigationInterceptor(event: RouterEvent): void {
if (event instanceof NavigationStart) {
//true
}
if (event instanceof NavigationEnd) {
//false
}
// Set loading state to false in both of the below events to hide the spinner in case a request fails
if (event instanceof NavigationCancel) {
//false
}
if (event instanceof NavigationError) {
//false
}
}

Angular2 all image loaded

I'm trying to figure out how should I detect if all image has been loaded in my element.
My element is like this now:
<div class="flexbox" appMyMasonry>
<div *ngFor="let new of specificNews | async}" class="box">
<md-card class="example-card">
<img md-card-image src="{{new.coverImageUrl}}">
....
appMyMasonry is a directive of mine: it makes an order/positioning based on how the elements should fill the available space... the important thing here is, that right now it works only if I call a method of the directory like this:
#ViewChild(MyMasonryDirective) directive = null
ngAfterViewChecked(): void {
this.directive.sortElements();
}
basically it works... but because of the ngAfterViewChecked() it call the function all the time.. one after an other and I hope there is a better way than just call it 10times in every second..
thanks for the help!
Listen to the error event of the image element:
<img [src]="someUrl" (error)="doSomething($event)">
where doSomething(event) { ... } provide your manipulation with size or what you want.
Plunker example
If you want to check in code only you can use the method explained in Checking if image does exist using javascript
#Directive({
selector: 'img[default]',
host: {
'(error)':'updateUrl()',
'[src]':'src'
}
})
class DefaultImage {
#Input() src:string;
#Input() default:string;
updateUrl() {
this.src = this.default;
}
}
Directive Plunker example

Component Templating

I'm an angular novice, currently building an angular2 app.
What I want to do is generate a series of DOM components from the following object data:
// Class construct with alphabeticalized properties
export class Screens {
screens: Array<Object>;
}
export var screenData: Screens = {
// Lists all of the audio files in the course
screens: [
{
id: 0,
template: 'templateURL-0.html',
css: 'templateURL-0.css'
},
{
id: 1,
template: 'templateURL-1.html',
css: 'templateURL-1.css'
},
{
id: 2,
template: 'templateURL-0.html',
css: 'templateURL-0.css'
}
]
};
I want the end result to be something similar to the following where template 0 will be displayed twice, and template 1 once; in order:
<app-screen></app-screen> <!-- templateURL-0.html content -->
<app-screen></app-screen> <!-- templateURL-1.html content -->
<app-screen></app-screen> <!-- templateURL-0.html content -->
I read the tutorial on Structural Directives and I think I need to implement something along those lines, however I'm honestly feeling a little lost on the best approach.
Ideally I would like to have something like:
<app-screen *ngFor="let screen of screenData.screens"></app-screen>
Which would then somehow set the template URL depending on what screenData.screens.template is.
Or should I do something like this? (unsure if correct syntax)
<div *ngFor="let screen of screenData.screens" [ngSwitch]="screenData.screens.template">
<app-screen-template1 [ngSwitchCase]="'templateURL-0.html'"></app-screen-template1>
<app-screen-template2 [ngSwitchCase]="'templateURL-1.html'">Ready</app-screen-template2>
</div>
Note: I will never change the templateURL reference.
I found that the best method to achieve this is to implement routing with the built in RouterModule.
So in the end I have the following in my class, where the template property is a url path / url segment.
// Class construct with alphabeticalized properties
export class Screens {
screens: Array<Object>;
}
export var screenData: Screens = {
// Lists all of the audio files in the course
screens: [
{
id: 0,
template: 'template/template-0'
}
]
};
Then when I want to load / instantiate this template, all I have to do is navigate to this url using something like:
<!-- Goes to localhost:4200/template/template-0 -->
<button [routerLink]="[screen.template]"></button>
Where screenis a bound variable in my .ts.
More on routing and navigation here.