I have to pass data to a route (Angular v12.0.1):
// my-module-routing.module.ts
const routes: Routes = [
{ path: '', component: MyComponent, data: { key: '-> THIS HAVE TO BE DYNAMIC <-' } }
]
This dynamic data have to be passed through the call of the component, can be in HTML with RouterLink, or in TS with navigateWithHistory(), but what have to change is the data inside Route.
I don't know if this is possible, if isn't, is there alternatives?
You can use resolvers to load dynamic data.
The Angular documentation has a good example showing how to fetch data from a service, but you can return any data.
Then you can get the data inside the component this way:
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.data = this.route.snapshot.data;
}
Important to notice that the code on the resolver is going to run before the component is loaded.
Related
In my Angular app, I have list and details pages and I want to lkeep the pageIndex value before navigating to details page. There is a Back button in the details page and I can return the list page by clicking on that button. However, I want to get the pageIndex value when navigating back to the list page and let the user open the page where he/she was before. For example I navigate 3rd page on the list and click details. At this stage I set the pageIndex to 3 and then navigate to details. Then by clicking the Back button I can navigate back to the list page, but I need to retrieve the pageIndex as 3.
So, what is an elegant way to fix this problem in Angular 10+?
list-componnet
details(id) {
this.router.navigate(['/details'], { state: { id: id } }); // I pass id value of the record
}
details-componnet
constructor(private router: Router) {
this.id = this.router.getCurrentNavigation().extras.state.id;
}
back() {
this._location.back();
}
Just write a simple example to make it work, I use the sessionStorage and router together, use router to show your the routing module, actually you can just use sessionStorage only, and wrapper it in a servive. Then you can retrieve the pageIndex anywhere.
And if you want to use router only, the pageIndex paramater will be place in both list and detail component, since this two component all need to use this value, in list component you will need pageIndex to set data-table, in detail component you need this value to pass to list component when redirect back triggered.
The routing module like below:
import { NgModule } from "#angular/core";
import { Routes, RouterModule } from "#angular/router";
import { ListComponent } from "./list/list.component";
import { DetailComponent } from "./detail/detail.component";
const routes: Routes = [
{ path: "", redirectTo: "list", pathMatch: "full" },
{
path: "list/:pageIndex=1",
component: ListComponent,
pathMatch: "full"
},
{
path: "detail/:id",
component: DetailComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Here you can navigate to list page from detail page use:
this.router.navigate(["/list/" + pageIndex]);
And then in list page's ngOnInit method to set current pageIndex to your data-table. Here is the demo: https://stackblitz.com/edit/angular-ivy-5vmteg?file=src/app/list/list.component.ts
Use sessionStorage, a listService or router queryParams to keep track of the current pageIndex.
I´d advocate for queryParams as it seems most logical and you can also link directly to specific page.
constructor(
private route: ActivatedRoute,
) { }
// Access the queryParam in list component
// Ie /list?pageIndex=4
this.route.queryParams.subscribe(params => {
// Do something with params.pageIndex if it exists
}
);
I´d also consider to change the way you handle routing to the details. If the route to the list is /list then route to the details should be /list/<listid> so you can link directly to the details if needed.
You can access the listId parameter as below but note it must also be specified as parameter in the router definition.
// Router definition
{ path: 'list/', component: ListComponent},
{ path: 'list/:listId', component: ListIdComponent}
// Access the listId in the details component
this.route.params.subscribe(param=> {
// Do somthing with param.listId
});
I am using Angular6, below is my scenario:
I am trying to pass information between Component1 and COmponent2 via a service. I see that service receives message from Component1, but Component2 doesnt receive the passed message.
In Page1:
//when a mouse move in a particular area happens, this method is called
actionMove: function(evt,obj) {
var messService = new MessService();
messService.sendMessage('Sending message from Component1 to Component2!');
}
inMessService file:
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs';
#Injectable({ providedIn: 'root' })
export class MessService {
private subject = new Subject<any>();
sendMessage(message: string) {
console.log("*** service got message from component 1 ***");
this.subject.next({ text: message });
}
getMessage(): Observable<any> { // this communicates with component2
console.log("*** component 2 trying to get message ***");
return this.subject.asObservable();
}
}
In Component2:
constructor(private messService: MessService) {
// subscribe to message service method
this.subscription = this.messService.getMessage()
.subscribe(message => { this.message = message; });
Could you please help me understand what am I missing?
Im not an Angular pro, but I think you using two different instances of MessService here because you new up one in actionMove in component1, and injecting an other one into component2, hence working with one does not reflect in the other.
Try to inject MessService into component1 also using constructor injection, as fridoo suggests. Because you provide your service in root, you'll get a singleton.
Singleton services - Angular
fridoo's comment goes to the heart of it --
to use a service in multiple components you need to take advantage of angular's dependency injection. you want to reference your service class in the constructor like in your component2 in component1, this way both components are using the same MessService.
you can just create an empty constructor like
constructor(private messService: MessService) {}
to get the service injected -- have you tried doing this?
I have been searching for certain feature we use in Laravel to specify where the routes will go in controllers methods like so:
Route::get('/user', 'UserController#index');
In above code when user navigate to /user the app will send him to the UserController and directly into index method.
I'm looking for something similar to help me handle delete routes because I want to restrict them for super admins only and don't want to write additional component for that
You need to use middleware for that. For example:
Route::get('user/{id}/delete', 'UserController#delete')->middleware('superadmin');
AFAIK this is not possible directly with Angular where as you can do this
Send some data with the router like
{path : 'heroes', component : HeroDetailComponent, data : {some_data : 'some value'}}
And in the component onInit check for the data using ActivatedRoute
and assign it to the proper method of the compoenent
Update
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.sub = this.route
.data
.subscribe(v => console.log(v));
}
I'm trying to unit test a component that subscribes to a data service variable. I'm trying to mock the service and override the component's private data service variable but I'm not sure how to test the subscription of the mocked variable. Here's my (limited) code:
Service:
export class Service {
constructor(private dataService: DataService) {
this.dataService.newData.subscribe(data => {
//do something
});
}
}
Data Service:
export class DataService {
private source = new BehaviorSubject<any>([]);
newData = this.source.asObservable();
//code to update source
}
unit test for Service:
mockDataService {
private source = new BehaviorSubject<any>([]);
newData = this.source.asObservable();
}
describe('Service', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
Service,
{provide: DataService, useClass: mockDataService} // is this correct?
]
}).overrideComponent(Service, {
set: {
providers: [
{provide: DataService, useClass: mockDataService}
]
}
}).compileComponents();
});
it('should register subscription', inject([Service, DataService], (service: ServiceA, mock: DataService) => { //should I be injecting the mock class?
expect(before()).toBeTruthy();
mock.newData.next("test"); // what's the correct way to test?
expect(after()).toBeTruthy();
}));
}
Am I overriding correctly? And if so, how do I correctly test that my component does the correct actions when subscribing to a private service's variable?
Thanks!
First, it would be helpful if you could provide the annotations alongside the classes. I am assuming that the Service class is a component, because you reference is when calling TestBed.overrideComponent. In that case the naming is confusing. It should have at least a suffix "Component" (see Angular style guide.)
If Service should actually be a service class, nesting services into another one is probably not a good practice (see docs.)
You are basically asking for two things.
1. Do I need to override the providers property of the module via TestBed.configureTestingModule?
For your example above, this is not necessary. You can easily omit the providers attribute from the object. It will then looks like
TestBed.configureTestingModule({})
There might be some cases where changing the providers is needed - but not in your case.
2. How should I test the service properly?
It seems like you are mixing up integration testing with unit testing. You want to test the service in both ways.
First: Unit test the service (Angular docs)
Second: Integration test – what you seem to be doing here. There is a recommended best practice as of the docs (link):
it('should register subscription', () => {
const fixture = TestBed.createComponent(Service);
dataService = fixture.debugElement.injector.get(DataService);
// do things
});
Regarding the mock.newData.next("test"), it is not really clear what you are trying to achieve here. This method call would probably give you an undefined function test error. Why? You are referring to this.source.asObservable() which returns an Obersvable. This object does not have a next method. You should maybe do some basic tutorials on RxJs.
Hope this helps!
Best,
Benji
I am angular 2.0 beginner and wondered if anyone can help me with passing data from one component to another.
My project is made up of three parts, a login window component, a table component and center component to handle routing. when the users log in in the login component, a http.post request will be sent to server to authenticate. if the credentials are authentic, a json containing users' information will be returned from the server . The page will also be routed to a table component showing the name of this user and his/her other info.
these two component are on the same page using router-outlet.
Here is my app.component.ts:
import { Component } from '#angular/core';
#Component({
selector: 'td-app',
template: `
<router-outlet></router-outlet>
`
})
export class AppComponent {
}
app.module.ts:
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
JsonpModule,
RouterModule.forRoot([
{
path: 'login',
component: LoginComponent
},{
path: 'table',
component: TableComponent
},{
path: '',
redirectTo: '/login',
pathMatch: 'full'
}
])
],
Here is the data I wanna pass into another component. the postUser method is doing http.post and an json string will return. I wanna pass this json data to my next component. I was thinking using promise to make sure the data is assigned to the variable userInfoPack and then pass userInfoPack to next component's template.
login.component.ts:
postUser(body: User): Promise<any>{
return Promise.resolve( this.getUser(body))
.then(response => this.userInfoPack = response );
}
getUser(body: User){
this.userJson = JSON.stringify(body);
this.loginservice.postUser(this.userJson)
.subscribe(response => this.userInfo= response);
return this.userInfo; //subscribe method ends here
}
this is the template I wanna pass data into. I want to use #input
to pass the data but I dont know how.
table.component.ts:
template: `
<h2>Hi, {{userInfoPack}}</h2>
`
Please help me with this, thank you!
Also is there any way that we can route the page to another component, since i use a button to route the page and send http.post request to authenticate account at the same time.
<button type= "button" class="btn btn-primary btn-lg"
(click)= "postUser(user)" style=" position: relative;
left: 90%;" routerLink= '/whoison'>
Login
</button>
I don't how to route the component after the credentials are sent and authenticated. (can we use routeLink in a function instead of a directive? )
Any suggestion or advice will be helpful, thank you!
Best solution would be create a sharing service to maintain your data. I had a similar challenge in one of my Angular application. Created a sharingService directive/class and injected/imported into both controller. Now due to singleton nature of services once you update data from one controller it could be fetched/read from other controller.
A good example with Angular 2 :
Bootstrapp your shared service into your application:
bootstrap(AppComponent, [ SharedDataService ]);
Do not add it again in providers attribute of your components. This way you will have a single instance of the service for the whole application.
#Component({
selector : "selectorName",
// providers : [SharedDataService ], //---- not needed
template : `I'm MyComponent and shared data is: {{data}}`,
})
export class MyComponent implements OnInit{
(...)
}
I found an easy way to do this.
We can use local storage if you only want to share string variable.
e.g.
app.component.ts
localStorage.set('name', 'yeehaah');
other.component.ts
var name = localStorage.get('name');
This way, you can access the name variable 'yeehaah' in 'other.component' from 'app.component'
Welcome to elaborate on this!