ExpressionChangedAfterItHasBeenCheckedError when calling a method as a source of an image - javascript

I have created this Angular 7 application where I'm trying to get the source of an image as follows:
<div *ngIf="showAllRec" class="pt-3">
<div *ngFor="let recommendation of allRecommendations">
<div class="row pt-2">
<div class="col-12">
<img [src]="generateProfilePictures()">
</div>
</div>
</div>
generateProfilePictures() {
const profiles = [
'../assets/profiles/dark-blue.png',
'../assets/profiles/dark-grey.png',
'../assets/profiles/light-blue.png',
'../assets/profiles/light-green.png',
'../assets/profiles/light-grey.png',
'../assets/profiles/light-red.png',
'../assets/profiles/medium-blue.png',
'../assets/profiles/medium-brown.png',
'../assets/profiles/medium-orange.png',
'../assets/profiles/medium-purple.png',
'../assets/profiles/medium-red.png',
'../assets/profiles/medium-yellow.png',
];
return profiles[Math.floor(Math.random() * profiles.length)];
}
The following results in an error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Any idea's why this is happening?

This Error occurs, because in development Angular will run Change detection twice to make sure while the first CD run was being done, components which are already checked by CD should not change values while CD run is in Progress.
In your case the method generateProfilePictures() acts as a getter for src property and every get called by CD gets a different value.
RT now i am on a mobile device, so it's hard for me to prepare a stackblitz be demo.
But you can delay this calculation either by catching the index you are generating randomly, or wrapping the function content in a settimeout/observable (still have to try, can't be sure on a cellphone )

Use ChangeDetectionStrategy.OnPush into your component.
import { Component, ChangeDetectionStrategy } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush //<====Add this property
})
Here is working stackblitz: ExpressionChangedAfterItHasBeenCheckedError Solved

Related

Angular - Disable Button

<div id="home-container">
<div class="sidebar">
<button mat-raised-button [disabled]="true">This is a button</button>
</div>
<div class="main-panel">
<ac-map></ac-map>
</div>
</div>
Environment:
Angular 10
Chrome/Firefox (Incognito mode)
Hi guys,
I'm experiencing some undesirable behaviour when displaying my angular project. The above example shows a simple component with a button that is disabled by default (I'm using 'true' as a placeholder for a variable). When I load the component the button should be disabled. HOWEVER. When the component is loaded the button is enabled for the first second or two and then is disabled - making it look disorganised. How can I avoid this?
Kind regards,
Scott.
According to your requirement, I suggest you read the document about the component lifecycle of Angular. https://angular.io/guide/lifecycle-hooks
Please check this out for a live sample.
import { Component, OnInit } from "#angular/core";
/**
* #title Basic buttons
*/
#Component({
selector: "button-overview-example",
templateUrl: "button-overview-example.html",
styleUrls: ["button-overview-example.css"]
})
export class ButtonOverviewExample implements OnInit {
disabled = true;
toggle() {
this.disabled = !this.disabled;
}
ngOnInit(): void {
this.disabled = false;
}
}
https://stackblitz.com/edit/toggle-angular-material-button-sample?file=src/app/button-overview-example.html
In this sample, I used the variable disabled to indicate the button state, and implemented the OnInit hook which I change false to true.

Include htm template into another one on Angular 6

Actually, i have one HTML file which contains all the code. I want to split this one on multiple files and to include them.
How can i do this?
Thanks a lot.
Let's say you have this html:
<div class="componentA">ComponentA</div>
<div class="componentB">ComponentB</div>
and this code is into the `AppComponent. You can split this two div into two component:
ComponentA.ts
#Component({
selector: 'componentA',
templateUrl: 'componentA.component.html',
styleUrls: ['componentA.component.scss'],
)}
export class ComponentA {
}
ComponentA.html
<div class="componentA">ComponentA</div>
ComponentB.ts
#Component({
selector: 'componentB',
templateUrl: 'componentB.component.html',
styleUrls: ['componentB.component.scss'],
)}
export class ComponentB {
}
ComponentB.html
<div class="componentB">ComponentB</div>
then into your AppComponent.html :
<componentA></componentA>
<componentB></componentB>
You need to write the second Component - Angular Components
Add this component to your App.module.ts - Angular Modules
If you have business logic you can also provide this by Services - Angular Services
In addition to answers above, don't forget to include
import { Component } from '#angular/core';
in your both components ts file.

What is wrong with this property binding in Angular4 while using it on style property?

In Angular4, property binding on the view (.html) picks up the value from the logic file (.ts)
This works well in the code:
<img [src]="sourceValue">
This too works well in the code:
<button [disabled]="isDisabled">
Why does this not work?
<p [style]="paragraphStyle"> This is a paragraph.</p>
abc.component.ts
isDisabled:boolean = true;
sourceValue:string = "./assets/hermione.jpg";
paragraphStyle:string = "background:red; color:yellow";
I know the usage of ngStyles and ngClass, I simply want to ask why property binding is not working in the above case. It is finally --- just a simple "Inline CSS Styling" if value is taken from .ts file and added to the html snippet in front of 'style' property in paragraph.
It's because of security Measures:
#Angular docs
Angular defines the following security contexts:
HTML is used when interpreting a value as HTML, for example, when
binding to innerHtml.
Style is used when binding CSS into the style property.
URL is used for URL properties, such as <a href>.
Resource URL is a URL that will be loaded and executed as code,
for example, in <script src>.
The Fix is to sanitize values beforehand using bypassSecurityTrustStyle()- Bypass security and trust the given value to be safe style value (CSS).
#Angular docs
WARNING: calling this method with untrusted user data exposes your
application to XSS security risks!
Component:
import { Component, SecurityContext } from '#angular/core';
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
paragraphStyle;
constructor(private _sanitizer: DomSanitizer){
this.paragraphStyle=this._sanitizer.bypassSecurityTrustStyle("background-color:red");
}
HTML
<p [style]="paragraphStyle"> This is a paragraph.</p>
NOTE:
For style property name use dash-case.
For example, font-weight ,background-color
Live Demo
I think you can do it but you have to do it like so:
[style.background]="'red'"

How to load dynamic HTML into DIV with component? Angular5

I have been trying to find the solution of this problem from two days. Unfortunately, I can not get what I want. I am using Angular5.
<div class="form-group col-md-12" [innerHTML]="GetItemsOfHolder(item.children[1],1,
'UserConfigGroupsLabelHolder') | keepHtml"></div>
This is what my function looks like:
GetItemsOfHolder(item: any,divName:string, recursive: boolean = false,typeName:string="")
{
return html;
}
Everything works fine, unless The html which I am returning contains one package named Select2
This is what I use to add the html into this div it works very fine. Until I wanted to add the dynamic package.
What I mean is return html contains the package component like this:
itemhtml +="<select2 data-type='"+holderItem.itemType+"'
[data]='this.dropdownData."+holderItem.name+"'></select2>"
This just returns the plain text to the browser and doesn't work as expected.
What I want is the string to be turned into component or any other way which works and generates the select2 dropdown.
I have been trying to search so many things.But it doesn't works
This is good but I can not understand this And dynamiccomponentloading is deprecated.
Can anyone please give me an idea How can I resolve this problem? Any example would be a great.
As commented by #Devcon
Angular will sanitize pretty much everything so that is why you are
getting plain text. What you want to look into is ReflectiveInjector
and mainly ComponentFactoryResolver. The main idea is that components
need some other info(services, other components, etc) to be rendered,
so you use the Injector to get Dependency Injection refs then the
Component factory builds your component. You then insert this to a
ViewChild reference. There is a more complicated way of dynamically
making components that uses the compiler and requires a
ModuleWithComponentFactories, this is what angular actually uses.
And searching on the angular, I accept that angular should not be done this way.
As I have to create the fully dynamic page which must be rendered in html. I changed my json little bit and using the
ng-container and ng-template and using ngswitch
I made recursive call in the template it self and found its working very fine.
I get many advantages using this:
The HTML (I render dynamically) itself is in HTML, Code is clean and readable, easily maitainable.
The example given here is pretty much the same I have done.
https://stackoverflow.com/a/40530244/2630817
A small example is here:
<ng-template #itemsList let-itemsList>
<div *ngFor="let item of itemsList;let i = index">
<div [ngSwitch]="item.itemType">
<div class="form-group" *ngSwitchCase="'TEXT'">
<label>
{{item.label}}
</label>
<input id="{{item.name}}" value="{{item.value}}" type='text' class='form-control txtbox ui-autocomplete-input'/>
</div>
<div class="form-group" *ngSwitchCase="'PASSWORD'">
<label>
{{item.label}}
</label>
<input id="{{item.name}}" value="{{item.value}}" type='password' class='form-control txtbox ui-autocomplete-input'/>
</div>
<div class="form-group" *ngSwitchCase="'BOOLEAN'">
<label style='width:40%'>{{item.label}}</label>
<div class="form-group"><input id="{{item.name}}" type='checkbox' /></div>
</div>
<div class="form-group" *ngSwitchCase="'LABEL'">
<label class="form-control">{{item.label}}</label>
</div>
<div class="form-group" *ngSwitchDefault>
<label>
{{item.label}}
</label>
<select2 class="form-control" [data]="GetDropDowndata(item.holderId)" [cssImport]="false" [width]="300" [options]="GetOptions(item.type)"></select2>
</div>
</div>
</div>
You can load every you want in one div, you have to play with ng-template and ng-content.
First you have to create one directive:
import {Directive, ViewContainerRef} from '#angular/core';
#Directive({
selector: '[dynamic]',
exportAs: 'dynamicdirective'
})
export class DynamicDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
After you have to put it in some ng-template like:
<p>
page works!
</p>
<ng-template #sider=dynamicdirective dynamic></ng-template>
and use it like
import {Component, ComponentFactoryResolver, OnInit, ViewChild} from '#angular/core';
#Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.css']
})
export class PageComponent implements OnInit {
#ViewChild('sider')
sider;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(SomeComponent);
this.sider.viewContainerRef.createComponent(componentFactory);
});
}
}
and normally will see you component loaded at the place of you ng-template (you can call https://angular.io/api/core/ViewContainerRef#clear if you want to reset your view)
I already play with this, you can find some code here https://github.com/nicearma/dynamic
I thought to leave this here for anyone who encounters the same issue in the future.
If you don't want to bother manually instantiating components with ComponentFactory as the other answers suggest, you can also use a library I wrote for the explicit purpose of loading components into dynamic content: ngx-dynamic-hooks.
You can give it any string that contains the selector of a desired component and it will automatically be loaded in its place. You can even load components by other text patterns other than just their selectors! See it in action in this Stackblitz.
There's a lot more bells and whistles, if you need them. In the link above, you'll find a fairly detailed documentation that should set you up easily.

Render Angular 4 component with text coming in from an API

I want to render incoming text from an API as subsequent HTML and component template.
Most of the solutions I found here use #ViewChild to inject the components but that doesn't work for me since I need to iterate the same behavior for all items in the *ngFor loop.
This is how the code would look like:
The template of the component rendering the incoming messages:
<div *ngFor="let item of messages">
<compile-component [template]="item.html"></compile-component>
</div>
Incoming message structure (item.html):
<my-component></my-component><div>Some html</div>
Component to compile:
#Component({
selector: 'my-component',
template: '<div>It works</div>',
styleUrls: ['./my-component.component.css']
})
export class MyComponent{ }
Output would look like this:
<div>It works</div><div>Some html</div>
I am looking for the solution for compile-component here.
Any help is much appreciated. Thanks in advance.
You should be able to do that via ComponentFactoryResolver. See angular docs about this here:
https://angular.io/guide/dynamic-component-loader

Categories

Resources