This is the first time I post here, I'm quite desperate.
Im using Angular 7. The problem is that when I manually type the desired url, it works well, but when I press a button and use RouterLink, the url changes but nothing else happens. If I press F5, the page will reload and will go to that new URL that has changed without trouble, so it's not a url routing problem.
I only implemented the routerLink in two buttons for the moment (in cities-nav), while I test it.
I also implemented a temporary one in app-component.html just in case it was a Material menu problem, but it doesn't work either.
Console says nothing. And I have a console.log inside the onInit of the cities-nav component, which triggers the first time that loads the web, but not when I use routerLink, so it doesn't even get there.
I also tried executing a method with a (click) event. Doesn't work either.
My code is the following:
My app.component.html
<div style="text-align:center">
<nav>
<a routerLink="/">Go home</a>
</nav>
</div>
<router-outlet></router-outlet>
This is my app-routing.module
import { NgModule } from "#angular/core";
import { Routes, RouterModule } from "#angular/router";
import { CitiesNavComponent } from './cities-nav/cities-nav.component';
const routes: Routes = [
{ path: "", redirectTo: "cities/Madrid", pathMatch: "full" },
{ path: "cities/:name", component: CitiesNavComponent }
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
This is my cities-nav.component.html
<div class="container">
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<span *ngIf="cityName" class="mdl-layout-title">{{this.cityName}}</span>
<div class="mdl-layout-spacer"></div>
</div>
</header>
<div class="mdl-layout__drawer">
<span class="mdl-layout-title">Cities</span>
<nav class="mdl-navigation">
<a class="mdl-navigation__link" routerLink="/cities/Madrid" routerLinkActive="active">Madrid</a>
<a class="mdl-navigation__link" routerLink="/cities/Barcelona">Barcelona</a>
<a class="mdl-navigation__link" (click)="goToCadiz()">Valencia</a>
<a class="mdl-navigation__link" href="">Cádiz</a>
<a class="mdl-navigation__link black-text" href="">ABOUT</a>
</nav>
</div>
<main class="mdl-layout__content">
<div class="page-content"><app-content *ngIf="cityName" [cityName]="this.cityName"></app-content></div>
</main>
</div>
</div>
This is my cities-nav.component.ts
import { Component, OnInit, NgZone } from "#angular/core";
import { Location } from "#angular/common";
import { ActivatedRoute } from "#angular/router";
import { Router } from "#angular/router";
#Component({
selector: "app-cities-nav",
templateUrl: "./cities-nav.component.html",
styleUrls: ["./cities-nav.component.scss"]
})
export class CitiesNavComponent implements OnInit {
cityName: string;
constructor(
private location: Location,
private route: ActivatedRoute,
private router: Router,
private ngZone: NgZone
) {}
ngOnInit() {
console.log("Cities nav component OnInit executed");
this.cityName = this.route.snapshot.paramMap.get("name");
}
goToCadiz() {
this.ngZone.run(() => {
this.router.navigateByUrl('/cities/Cadiz');
});
}
}
First screenshot of tracing
Second screenshot of tracing
Okay, after a long time I found out what was happening. I actually had two problems.
First one, I had to change this line in cities-nav.component:
this.cityName = this.route.snapshot.paramMap.get("name");
To:
this.activeRoute.params.subscribe(routeParams => {
this.cityName = routeParams.name;
})
Because I always was on the path 'cities/:name', so even if I changed from one city to another, it didn't get the name again from the input. So that subscribe solves the problem, and will give you the new param every time you change the variable inside the same path.
The other problem was inside the app-content, because inside onInit I called the cityService which got a city by a name. The problem was similar to the other one, it only reached the city once, even if the city input name changed. So the solution was creating a method ngOnChanges as follows:
ngOnChanges(changes: SimpleChanges): void {
//Add '${implements OnChanges}' to the class.
this.getCityByName(this.cityName);
}
This way every time the component input 'cityName' changes, it will get the new city and update.
console log in the ngOnInit will be called only once.If you want to detect change in the parameters then subscribe to the paraMap in activateRoute.
created a sample demo in the link below.
https://stackblitz.com/edit/angular-mriyze?file=src%2Fapp%2Fcities-nav.component.ts
Related
I have some pages and when I change router link to some wrong url and it redirect me to 404 page. In this 404 Error page I have button that will redirect me to previous page. Now I have some problems that this redirect button when I click it redirects to that wrong url and again to 404.
I try to show by photo:
1)My route. write wrong url -> redirect to 404 page.
Now the problem is when I click button to redirect at the previous route "Home" it redirect me to this wrong route.
goBack() {
this._location.back();
}
You can use JS history API to achieve this.
This is a sample to match your use case
goBack() {
window.history.go(-2);
}
And this also keeps navigiation within the SPA scope (Page wouldn't refresh).
Side effect for this is that you need to make sure that the user follows a particular flow; in which he needs to be within a working page, then goes somewhere not found then get redirected to the 404 Page..
I would recommend a button that navigates to Home or Dashboard features instead.
You can achieve this by number of ways , however I think the most elegant way is to use power of Injectable services in Angular.
Here is my solution of this problem:
Create an Injectable service which will set a variable as soon as the current navigation end. The value of this variable can be used in any of the other components where ever we would need. Obviously one has to inject the service in the required component.
In my example, I have created RouterService:
import { Injectable } from '#angular/core';
import { Router, NavigationEnd } from '#angular/router';
#Injectable({ providedIn: 'root' })
export class RouterService {
private previousUrl: string = undefined;
private currentUrl: string = undefined;
constructor(public router : Router) {
this.currentUrl = this.router.url;
router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
this.previousUrl = this.currentUrl;
this.currentUrl = event.url;
};
});
}
public getPreviousUrl(){
return this.previousUrl;
}
}
To illustrate the navigation , I have created two components viz. home and not-found Components.
Below are the code snippets for both of these components :
a) home.component.ts :
import { Component, VERSION } from '#angular/core';
import { NavigationEnd, Router } from '#angular/router';
import { filter } from 'rxjs/operators';
import { RouterService } from '../../service/router.service';
#Component({
selector: 'not-home-app',
templateUrl: './home.component.html',
styleUrls: [ './home.component.scss' ]
})
export class HomeComponent {
name = 'Angular ' + VERSION.major;
constructor(private router: Router,private routerService: RouterService) {}
takeMeToNotFoundPage(){
this.router.navigate(['/notFound']);
}
}
b) not-found.component.ts:
import { Component, VERSION } from '#angular/core';
import { NavigationEnd, Router } from '#angular/router';
import { filter } from 'rxjs/operators';
import { RouterService } from '../../service/router.service';
#Component({
selector: 'not-found-app',
templateUrl: './not-found.component.html',
styleUrls: [ './not-found.component.scss' ]
})
export class NotFoundComponent {
previousUrl: string;
constructor(private router: Router, private routerService: RouterService) {
router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationEnd) => {
console.log('prev:', event.url);
this.previousUrl = event.url;
});
}
loadPreviousRoute(){
let previous = this.routerService.getPreviousUrl();
if(previous)
this.routerService.router.navigateByUrl(previous);
}
}
When the application will first load, I will route it to 'home' page. There I provided a button to navigate to not-found page.
As you see in the above code of home component , I am setting the previous url in the router service which I can use in next component ( in this example it is 'not-found' component'
c) below are the html code snippets for both home and not-found components :
Home.component.html :
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<button type="button" (click)="takeMeToNotFoundPage()">Not FOUND!</button>
not-found.component.html:
<p>
<span style="background-color: red">NOT FOUND!!</span>
</p>
<button type="button" (click)="loadPreviousRoute()">Go Back to Previous Route</button>
Hope this will help you to solve your problem. If it will , please provide your feedback and upvote it accordingly as it will help others in future too.
If i am on same route , and i click again from nav bar. It doesn't do anything .
How can I refresh my component from navbar itself. Is there any method in routerLink.
<li [routerLinkActive]="['active']"><a [routerLink]="['/campaigns']">Creative</a></li>
<li [routerLinkActive]="['active']"><a [routerLink]="['/market-survey']">Survey</a></li>
click again means, suppose you click on creative (in my example) corresponding route will activate and component is loaded . Then I again click on creative . I want that component should refresh
This can be fixed this by creating another component
refresh.component.ts
import { Component, OnInit } from '#angular/core';
import {Router, ActivatedRoute, Params} from '#angular/router';
#Component({
selector: 'app-refresh',
template: `
<p>
refresh Works!
</p>
`,
styles: []
})
export class RefreshComponent implements OnInit {
constructor(private activatedRoute:ActivatedRoute,private route:Router) { }
ngOnInit() {
this.activatedRoute
.queryParams
.subscribe(params => {
if(params.url){
this.route.navigate([params.url]);
}
});
}
}
app.routing
{path: 'refresh',component:RefreshComponent},
and my new naviation li is
<li [routerLinkActive]="['active']"><a [routerLink]="['/refresh']" [queryParams]="{url:'campaigns'}" skipLocationChange="true">My AMP Creative</a></li>
i'm struggling about this problem and can't figure out.
I simply need to show a popup div situated in the page clicking from a menu entry in my navbar.component.
I added a property "show" in my popup which prints the "show" class on my div using the ngClass (with if) directive. I can get this working if the action button is inside my popup component but i cannot print the show class clicking on another component. The property in the Object get updated but the class is not printed. I'm using angular 4 with ng-bootstrap. I tried both with services and with parent/child emit event.
This is is my situation:
app.component.html
<app-nav-bar></app-nav-bar>
<app-login></app-login>
<router-outlet></router-outlet>
<app-footer></app-footer>
navbar.component.html
...
<button class="dropdown-item" (click)="showPopup()">LOGIN</button>
...
navbar.component.ts
import {Component, EventEmitter, Input, OnInit, Output} from '#angular/core';
#Component({
moduleId: module.id,
selector: 'app-nav-bar',
templateUrl: 'navbar.component.html',
styleUrls: ['./navbar.component.css'],
})
export class NavbarComponent implements OnInit {
#Output() show = new EventEmitter<boolean>();
ngOnInit() {
}
showPopup() {
this.show.emit(true);
}
}
login.component.html
<div id="wrapper-login-popup" class="fade-from-top" [(class.show)]="show">
<div id="container-login-popup">
<div class="row">
<div class="col-sm-12 text-center">
<img id="popup-bomb" src="assets/images/bomb.png" alt="bomb"/>
<img id="popup-close" class="close-icon" src="assets/images/close.png" alt="close"
(click)="closePopup()"/>
</div>
</div>
</div>
</div>
login.component.ts
import {Component, Input, OnInit} from '#angular/core';
import {AuthService} from '../services/auth.service';
import {IUser} from './user';
#Component({
selector: 'app-login',
templateUrl: 'login.component.html',
styleUrls: ['login.css']
})
export class LoginComponent implements OnInit {
private username: string;
private password: string;
#Input() show: boolean = false;
constructor(private AuthService: AuthService) {
}
ngOnInit() {
}
login() {
...
}
showPopup() {
console.log(this); //Show is false
this.show = true;
console.log(this); //Show is true but does not trigger the show class
}
closePopup() {
this.show = false;
}
}
The issue here is that your nav-bar and login components are siblings and can't directly communicate with each other. You have show as an output of navbar and as an input of login, but you haven't connected the dots.
You need to update your app.component to connect them.
export class AppComponent implements OnInit {
show = false;
onShow() { this.show = true; }
}
and in the template:
<app-nav-bar (show)="onShow()"></app-nav-bar>
<app-login [(show)]="show"></app-login>
There's a lot of two way binding going on here which works for something simple liek this, but generally it's a bad idea as it leads to unmaintainable code. You should choose one owner of the show variable and force all changes to it through him. In this case the app component is the most logical owner, so I'd change the login component to emit an event that changes the show variable in app component adn remove all 2 way bindings, but in a bigger app, you may even want a separate service that manages hiding/showing pop ups. This eliminates the need for the sending a message up and down your component tree, you can inject the service where it's needed.
As another commenter mentioned, you also should be using ngClass for class manipulation like
[ngClass]="{'show':show}"
a service based solution would look like
import {Subject} from 'rxjs/Subject';
#Injectable()
export class PopUpService {
private showPopUpSource = new Subject();
showPopUp$ = this.showPopUpSource.asObservable();
showPopUp() { this.popUpSource.next(true); }
closePopUp() { this.popUpSource.next(false); }
}
Then you provide in app module or at app component level:
providers:[PopUpService]
make sure you don't re provide this later, as you only want one copy to exist so everyone shares it.
then inject into both components, and have them call the services close or show pop up methods.
then in the login component you bind to the popUp$ observable like
constructor(private popUpSvc:PopUpService){}
show$;
ngOnInit() { this.show$ = this.popUpSvc.showPopUp$; }
showPopUp() { this.popUpSvc.showPopUp(); }
closePopUp() { this.popUpSvc.closePopUp(); }
and in the template subscribe w async pipe like
<div id="wrapper-login-popup" class="fade-from-top" [ngClass]="{'show': (show$ | async) }">
The reason for using the async pipe is garbage collection managemetn is simpler. If you don't use async, you need to garbage collect manually in ngOnDestroy by calling unsubscribe(), otherwise your subscriptions will keep stacking up. There is also a more nuanced benefit in that the async pipe triggers change detection, but this only becomes important if you start using onPush change detection for performance optimization.
new to Angular so please bear with me (or just point me to the appropriate docs article if this is just that obvious)
Basically, the structure of my site (for all of the pages) is this:
navigation (home, about, resources, contact)
header-div
content
footer
I want it so that any of the links you click will change the contents of the header-div; for now I'll start with changing the background color. For example, the home page's header is blue, about is red, resources is green, contact is yellow.
What I started doing but got stuck with was directly manipulating the style by using a method and click listener on the links
How would I got about attaching a class to the div, based on the link that's been clicked?
This is my app.component.ts
import { Component } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
#Component({
selector: 'app-root',
template: `
<div class="app-nav">
<nav class="set-margin">
<ul>
<li><a routerLink="/" routerLinkActive="active" (click)="showStyle = !showStyle">Home</a></li>
<li><a routerLink="/about" routerLinkActive="active">About</a></li>
<li><a routerLink="/resources" routerLinkActive="active">Resources</a></li>
<li><a routerLink="/contact" routerLinkActive="active">Contact</a></li>
</ul>
</nav>
</div>
<div [ngStyle]="getHeaderStyle()" class="app-mainheader">
<div class="app-mainheader__content set-margin">
this is the header for the
</div>
</div>
<router-outlet></router-outlet>
<app-footer></app-footer>
`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
showStyle = false;
getHeaderStyle() {
// ???
}
}
You could store the activateRoute as a member variable and style according to that.
export class AppComponent {
activatedRoute = "";
Then, when you click on a link, you set the activatedRoute.
<a routerLink="/" routerLinkActive="active" (click)="activateRoute('home')>
activateRoute(activatedRoute: string) {
this.activatedRoute = activatedRoute;
}
For the styling of the div, you use NgClass.
[ngClass]="{'home-class': activatedRoute === 'home', 'about-class': activatedRoute === 'about', ...}"
If you do not only want to do it, when someone clicks one of the links but always when the route is activated, then you should inject Router and check for the url.
[ngClass]="{'home-class': router.url === '/', 'about-class': router.url = 'about', ...}
// inject the router
constructor(public router: Router) {}
see a running example in this plunker
If you want to keep the style / conditional code out of the template in an a function, you can test the route value and return a class based on the current path. This is easy to read / maintain, although it may not be the most elegant solution:
import { Router } from '#angular/router';
constructor(
private router: Router
) {
}
getHeaderStyle() {
if (this.router.url.includes('/about/')
return 'red';
} else if (this.router.url.includes('/resources/')){
return 'green';
} else if (this.router.url.includes('/contact/')){
return 'yellow';
} else {
return 'blue'
}
In your component user [ngClass] to apply the class based on the function:
<div [ngClass]="getHeaderStyle()" class="app-mainheader">
<div class="app-mainheader__content set-margin">
this is the header for the
</div>
</div>
Then create your styles for each colour:
.red {
background-color: red;
}
etc.
I have a problem to hide the navbar when the user is not logged in (on the public views), I check if the item currentUser exists on the localStorage and then I use
*ngIf on the html template to show/hide.
When I login at first I don't see the navbar, but after refreshing the page it's displaying, the same when I logout, at first it shows it and after refreshing the page it's gone.
There is my app.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnInit {
userLogged = JSON.parse(localStorage.getItem('currentUser'));
ngOnInit() {
console.log(this.userLogged);
}
}
And my app.component.html
<!-- main app container -->
<div class="jumbotron">
<div class="container">
<ng-navbar *ngIf="userLogged"></ng-navbar>
<div class="col-sm-12">
<alert></alert>
<router-outlet></router-outlet>
</div>
</div>
</div>
In case you need more information just ask for it, is my first Angularjs 4 question and I don't know what to show exactly.
Try this:
<ng-navbar *ngIf="userLogged()"></ng-navbar>
userLogged() { return JSON.parse(localStorage.getItem('currentUser')) };