ExpressionChangedAfterItHasBeenCheckedError in Directive with Angular - css

After I get all the hate, I know there's a thread about this problem but I haven't managed to find a solution for my problem. I'm a rookie.
What I wanted to do was to change the nav header background only when the user is in a particular route, so I created a directive in which I retrieve the current url and then I styled the nav header with setElementStyle. For that I'm comparing if the current url matches a particular url that I store in a variable.
The app is working fine but I still get that error.
This is my directive:
import {Directive, ElementRef, Renderer, OnInit, ChangeDetectorRef} from '#angular/core';
import { Router, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, RoutesRecognized } from '#angular/router';
import 'rxjs/add/operator/filter';
#Directive({
selector: '[styled]',
})
export class StyledDirective implements OnInit {
constructor(public el: ElementRef, public renderer: Renderer, public _router: Router) {
renderer.setElementStyle(el.nativeElement, 'color', '#212121');
renderer.setElementStyle(el.nativeElement, 'backgroundColor', 'rgb(247, 247, 247)');
}
ngOnInit(){
const profileUrl = "/app/userprofile";
this._router.events
.filter(event => event instanceof NavigationStart)
.subscribe((event:NavigationStart) => {
if (event.url == profileUrl) {
return this.el.nativeElement.style.backgroundColor = '#ffffff';
}
else {
return this.el.nativeElement.style.backgroundColor = 'rgb(247, 247, 247)';
}
});
this._router.events
.filter(event => event instanceof NavigationStart)
.subscribe((event:NavigationStart) => {
if (event.url == profileUrl) {
return this.el.nativeElement.style.color = "#03A9F4";
}
else {
return this.el.nativeElement.style.color = '#212121';
}
});
}
}
Probably its not the best code ever but that's how I tried to resolve my problem, and probably there's a more elegant solution for this. Thanks for your help guys!

I prefer this way
First inject the Router in constructor, then return a function according to route
constructor(private router: Router) {}
getRoute(){
if (this.router.url === '/client'){
return "client";
}
}
in your html
<header [ngClass]="getRoute()">
and in css
header.client{background-color:yellow}

Related

Could not Detect Online Connectivity in Angular 11

I tried to show online status in angular 11.
I did same as like this https://dzone.com/articles/how-to-detect-the-internet-connection-status-in-an
My ts.file
import { ConnectionService } from 'ng-connection-service';
isConnected = true;
noInternetConnection: boolean;
constructor(private connectionService: ConnectionService,) {
this.connectionService.monitor().subscribe(isConnected => {
this.isConnected = isConnected;
if (this.isConnected) {
this.noInternetConnection=false;
}
else {
this.noInternetConnection=true;
}
})
}
my.html file
<nb-action *ngIf="noInternetConnection">
<p>online</p>
</nb-action>
<nb-action *ngIf="!noInternetConnection">
<p>offline</p>
</nb-action>
BUT it shows offline. MY wifi connection is on
Can anyone help me to find the bug?
I personally don't recommend to use another library to listen for native browser events as this makes the application more robust, and probably the library does the same as the following code :(
When I achived such functionality I created event listeners for the native "online" and "offline" events.
Here is the network monitor service.
import { Injectable } from '#angular/core';
import { fromEvent, ReplaySubject } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
#Injectable()
export class NetworkStatusService {
private init: boolean = navigator.onLine;
private online: ReplaySubject<boolean> = new ReplaySubject();
get initStatus() {
return this.init
}
constructor() {
if (this.init==false) {
this.online.next(false);
}
fromEvent(window, "online").subscribe((val) => {
this.online.next(true);
})
fromEvent(window, "offline").subscribe((val) => {
this.online.next(false);
})
}
getOnlineStatus() {
return this.online.pipe(distinctUntilChanged());
}
}

Return firebase values from a service to a component angular 6

I'm creating an application with angular 6 and firebase using angularfire2, I chose to use the firestore where I have a collection called pages like in the image:
basically I created a service - "PagesService" where I have a function that returns the data of the page that I sent. I'm trying to use getPage to return the values to my component, and assign them to the form, nothing else I tried worked, only returns an "observable" that I can not work, does anyone have an idea of what I can do?
Full code, service:
import { Injectable } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from 'angularfire2/firestore';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable()
export class PagesService {
private pagesCollection: AngularFirestoreCollection<any>;
private page: AngularFirestoreDocument<any>;
constructor(private afs: AngularFirestore) {
this.pagesCollection = afs.collection('pages');
}
getPage(pageName: string) {
return this.afs.doc<any>('pages/${pageName}').valueChanges();
}
addPages(pageName: string, pageForm: any) {
this.pagesCollection.doc(pageName).set(pageForm.value);
}
}
My component:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
import { Observable } from 'rxjs';
import { PagesService } from '../../services/pages.service';
#Component({
selector: 'app-quem-somos',
templateUrl: './quem-somos.component.html',
styleUrls: ['./quem-somos.component.scss']
})
export class QuemSomosComponent implements OnInit {
pageForm: FormGroup;
pageName: string = "wo-we-are";
page: any;
constructor(private pagesService: PagesService, private fb: FormBuilder) { }
ngOnInit() {
this.page = this.pagesService.getPage(this.pageName);
console.log(this.page);
this.pageForm = this.fb.group({
title: '',
content: ''
});
}
save() {
this.pagesService.addPages(this.pageName, this.pageForm);
}
}
obs: Sorry my english
If I have understand you right, When you say "Observable that I cannot work" is mean that you cannot access his data when you are trying to assign its values in the form?
In this case (I assume that your service is working as expected), just subscribe to it and populate the form after your values are ready to use. for example:
ngOnInit() {
this.pagesService.getPage(this.pageName).subscribe(v => {
// Here your data is ready, so you can send it to a function and populate the form as you need.
this.populateForm(v);
});
// Here I just construct the FormGroup, so your application can rendered.
this.pageForm = this.fb.group({
title: '',
content: ''
});
}
And add this function to do the task:
populateForm = (data) => {
console.log(data); // Just log it in console, and see if its the data that you seek for
}
Instead of console.log() you can populate your form or do what ever you need to.
Good Luck !
--EDIT--
I just noticed now, In your service:
getPage(pageName: string) {
return this.afs.doc<any>('pages/${pageName}').valueChanges();
}
You call the doc with ' ' instead of ``, so In fact, you are not using Template Strings. So your call is wrong and not fetch with the right path.
Change it to:
return this.afs.doc<any>(`pages/${pageName}`).valueChanges();

Angular Component Expects 2 Arguments

I'm attempting to create a Wordpress theme compatible with 4.8.x that will render single posts and list of posts as per [this tutorial]:1
When I run the test script, I receive the following errors:
ERROR in C:/MyTheme/src/app/posts/post-list/post-list.component.spec.ts
(9,25): Expected 2 arguments, but got 0.
ERROR in C:/MyTheme/src/app/posts/post-single/post-single.component.spec.ts
(8,25): Expected 2 arguments, but got 0.
The code for both components is very similar and calls into the PostsService which is defined as:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Post } from './post';
import { environment} from '../../environments/environment';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class PostsService {
private _wpBase = environment.wpBase;
constructor(private http: HttpClient) { }
getPosts():Observable<Post[]> {
return this.http.get<Post[]>(this._wpBase + 'posts');
}
getPost(slug: string): Observable<Post[]> {
return this.http.get<Post[]>(this._wpBase + 'posts?slug=${slug}');
}
}
My post-list-component includes the following:
import { Component, OnInit } from '#angular/core';
import { Post } from '../post';
import { PostsService} from '../posts.service';
import { HttpErrorResponse } from '#angular/common/http';
import {Router} from '#angular/router';
#Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css'],
providers: [PostsService]
})
export class PostListComponent implements OnInit {
posts: Post[];
constructor( private postsService: PostsService, private router: Router ){}
ngOnInit() {
this.postsService.getPosts().subscribe(
(posts: Post[]) => this.posts = posts,
(err: HttpErrorResponse) => err.error instanceof Error ?
console.log('An error has occurred:',
err.error.message):console.log('Backend returned code $(err.status),
body was: ${err.error}'));
}
selectPost(slug) {
this.router.navigate([slug]);
}
}
The error is thrown in the following post.list.component.spec.ts:
/* tslint:disable:no-unused-variable */
import { TestBed, async } from '#angular/core/testing';
import { PostListComponent } from './post-list.component';
import {Router} from "#angular/router";
describe('Component: PostList', () => {
it('should create an instance', () => {
let component = new PostListComponent();
expect(component).toBeTruthy();
});
});
I am not sure how to resolve the errors. It seems to me that PostLisComponent() needs to be passed 2 arguments as per the error, but it's not clear what arguments should be passed. Can anyone assist me in better understanding how to resolve the errors?
its because the constructor use TestBed
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { PostListComponent } from './post-list.component';
describe('PostListComponent ', () => {
let component: PostListComponent ;
let fixture: ComponentFixture<PostListComponent >;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PostListComponent ]
})
.compileComponents();}));
beforeEach(() => {
fixture = TestBed.createComponent(PostListComponent );
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create an instance', () => {
expect(component).toBeTruthy();
});
});
From Angular's Testing Guide in regards to the TestBed, and why it would fit such a scenario:
TestBed is the first and most important of the Angular testing
utilities ... In effect, you detach the tested component from its own
application module and re-attach it to a dynamically-constructed
Angular test module tailored specifically for this battery of tests.
Right now, you're statically constructing instead of dynamically constructing using the TestBed, which is causing the error since the constructor of the PostListComponent contains two parameters which would be required to be filled in case of static constructing.

Angular2 - unable to retrieve data from API

I am currently building an Angular2 application accessing an MVC web API i have built. However, it does not seem to retrieve any data. I am obviously missing something but i am not sure what.
I know that the URL i am using works along with the headers as i am able to retrieve the data correctly through fiddler.
My repack.service.ts is as follows:
import { Injectable } from '#angular/core';
import { Headers, Http } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { RepackIndex } from './RepackIndex';
#Injectable()
export class RepackService{
private baseUrl = 'https://localhost:44321/api/Repack/All';
private headers = new Headers({'Content-Type': 'application/json'});
constructor(private http: Http) { }
getAllRepacks(): Promise<RepackIndex[]>{
var data = this.http.get(this.baseUrl)
.toPromise()
.then(response => response.json().data as RepackIndex[])
.catch(this.handleError);
return data;
}
private handleError(error: any): Promise<any>{
console.error("An error occured in repack.service", error);
return Promise.reject(error.message || error);
}
}
And this is my component:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { RepackIndex } from './repackIndex';
import { RepackService } from './repack.service';
#Component({
selector: 'index',
templateUrl: 'app/index.component.html',
providers: [RepackService]
})
export class IndexComponent implements OnInit{
repacks: RepackIndex[];
selectedRepack: RepackIndex;
constructor(private router: Router, private repackService: RepackService) { }
onSelect(repack: RepackIndex): void{
this.selectedRepack = repack;
}
getRepacks(): void{
this.repackService.getAllRepacks().then(repacks => this.repacks = repacks);
}
ngOnInit(): void{
this.getRepacks();
}
}
I have tried putting in a breakpoint and adding a console.log line but no data is returned to the component.
I am fairly new to Angular2 so any help would be greatly appreciated.
Thanks,
Right I have managed to get it to work by using an observable rather than a promise.
My service method now looks like this:
public GetAll = (): Observable<RepackIndex[]> => {
return this.http.get(this.baseUrl)
.map((response: Response) => <RepackIndex[]>response.json())
.catch(this.handleError);
}
And my Component call now looks like this:
getRepacks(): void{
this.repackService.GetAll()
.subscribe((data:RepackIndex[]) => this.repacks = data,
error => console.log(error),
() => console.log('Get All repacks complete'));
}
I found the answer here
Hope this helps someone else

Fill form after http response in angular2

I'm wondering what is a best way to load a form after getting the response from server. I wrote some code where it is getting data from server and in my component I am subscribing to the response, but My UI is loading before even I get the response.
I want to use this component for both adding and editing.
Component:
#Component({
selector: 'gate',
templateUrl: '/public/app/views/gate.html',
directives: [GateFormComponent, StrategyComponent],
providers : [MyService]
})
export class MyComponent {
private id:any;
constructor(private _routeParams:RouteParams, #Inject(MyModel) private myModel,
private myService : MyService) {
}
ngOnInit() {
this.id = this._routeParams.get("id");
if (this.id) {
this.gateDataModel.unique_display_id = parseInt(this.id);
this.myService.loadData(this.id)
.subscribe(response => console.log(response));
}
}
In my component, I am loading 2 components one of which has a form into which I have to load data once I get the response. And all this should only happen if I have an id available.
Service:
#Injectable()
export class MyService extends HTTPServices {
constructor(http:Http) {
super(http);
}
loadData(id:number) {
return this.query(url)
.map(res => res.json())
.catch(this.handleError)
}
private handleError(error:Response) {
console.log("Error : ", error);
return Observable.throw(error.text());
}
HTTPServices
export class HTTPServices {
private headers:Headers;
private http:Http;
defaultOptionsArgs:RequestOptionsArgs;
constructor(http:Http) {
this.http = http;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.defaultOptionsArgs = {
'headers': this.headers
};
}
create(servicePath:string, model:any, options?:RequestOptionsArgs) {
var url = this.getUrl(servicePath);
var options = options ? options : this.defaultOptionsArgs;
return this.http.post(url, JSON.stringify(model), options);
}
query(servicePath:string, options?:RequestOptionsArgs) {
var options = options ? options : this.defaultOptionsArgs;
return this.http.get(servicePath, options);
}
}
----Edited-----
Finally, I was able to add #CanActivate and it is working.
#Component({
selector: 'gate',
templateUrl: '/public/app/views/gate.html',
directives: [ROUTER_DIRECTIVES, GateFormComponent, StrategyComponent]
})
#CanActivate(
(next: ComponentInstruction, prev: ComponentInstruction) => {
return new Promise((resolve) => {
let id = next.params["id"];
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
if(id){
http.get(URL)
.subscribe((response) => {
console.log(response)
next.routeData.data["response"] = response;
// continue
resolve(true);
}, (error) => {
resolve(false);
});
} else {
resolve(true);
}
});
}
)
export class MyComponent{
private id:any;
constructor(private _routeParams:RouteParams, #Inject(MyModel) private myModel, routeData: RouteData) {
console.log(routeData.get("response"));
}
}
The component is loading up and then I am getting the response
Thanks
In you component you can just use
template: `
<div *ngIf="data">
<!-- form goes here -->
</div>
`
where data is a property that is set to some value when the response from the server arrived.
If you leverage Angular2 routing (and it seems to be the case), you could use leverage the OnActivate interface and its routerOnActivate:
Defines route lifecycle method routerOnActivate, which is called by the router at the end of a successful route navigation.
For a single component's navigation, only one of either OnActivate or OnReuse will be called depending on the result of CanReuse.
The routerOnActivate hook is called with two ComponentInstructions as parameters, the first representing the current route being navigated to, and the second parameter representing the previous route or null.
If routerOnActivate returns a promise, the route change will wait until the promise settles to instantiate and activate child components.
You could return a promise that will be resolved when your data will be there. Here is a sample:
#Component({ ... })
export class MyComponent {
private id:any;
constructor(private _routeParams:RouteParams,
#Inject(MyModel) private myModel,
private myService : MyService) {
}
routerOnActivate() {
this.id = this._routeParams.get("id");
return new Promise((resolve, reject) => {
if (this.id) {
this.gateDataModel.unique_display_id = parseInt(this.id);
this.myService.loadData(this.id)
.subscribe(response => {
console.log(response);
resolve();
});
} else {
resolve();
}
});
}
(...)
}
I had a similar question, but since the solution can be used for your usecase too, i would recommend to have a look at the accepted answer: How to manipulate a component on specific routes in Angular2
The basic idea is to extend the router-outlet directive and override the activate() function which will be called before the next route is activated and waits for a promise to resolve.
For example you could do something like this:
#Directive({
selector: 'custom-router-outlet'
})
export class CustomRouterOutlet extends RouterOutlet {
private parentRouter:Router;
constructor(_elementRef: ElementRef,
_loader: DynamicComponentLoader,
_parentRouter: Router,
#Attribute('name') nameAttr: string,
private _myRoutingService:MyRoutingService) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
}
activate(nextInstruction: ComponentInstruction): Promise<any> {
let someRouteSpecificData = nextInstruction.routeData.data['someRouteData'];
if(someRouteSpecificData) {
_myRoutingService.beforeRoute(someRouteSpecificData).subscribe( () => {
// go on after this has been resolved
return super.activate(nextInstruction);
// or maybe cancel the route:
return false;
// or maybe do something crazy:
nextInstruction.componentType = MyOtherComponent;
return super.activate(nextInstruction);
}
}
return super.activate(nextInstruction);
}
}
I think you could easily change this for your purposes. You could utilize your #RouteConfig for example to hold some information on what should happen or be checked on a route change.
Another approach would be to use the #CanActivate decorator like mentioned here already, but its a bit harder to accomplish. It just feels a bit hacky at this point. I could add this later if you're interested.
I have been able to implement this using the resolve functions of the router (https://angular.io/docs/ts/latest/guide/router.html#!#resolve-guard). This enables the http calls to be made and the route only completes when the http call observable returns.
There are good examples here: https://angular.io/resources/live-examples/router/ts/plnkr.html

Resources