I am trying to understand what a basic implementation of loading a routerLink while also pulling in saved url params would look like. Normally, the way I handle routing in my app is via a subscribing to an observable, that looks like this:
private onFilterProcessed(value: any, type: string, body, page)
{
if (type === 'zip')
{
this.processType('addresses.zip', value);
} if (type === 'location')
{
this.processType('addresses.city', value);
this.filtersService.getByCategory(
page, this.pagesize, this.body)
.subscribe(resRecordsData => {
let data = resRecordsData.data;
},
responseRecordsError => this.errorMsg = responseRecordsError);
}
This allows me to pass in some filter parameters to the api call as part of the body in a POST request. This will return results where the user's filter selections are passed in before returning the data.
This all works as expected. When a user goes "back" to a previously loaded component, their previous filter selections will be passed into the api call, so the "page" looks just like it did when they were last on that page/component.
However, I also have a couple of sections in my app where I'm loading components via routerLink instead. They initially looked like this:
<a routerLink="/customer-history" routerLinkActive="selected">Customer History</a>
The thing is, now that I have filter params in the url, this alone won't work, because each time I click on these particular links, it will wipe out the url and re-load it with just the page identifier "customer-history" -- because that's what I'm currently telling it to do.
If, for instance, the filters had been used by the user to filter results based on a city, the url would look like this:
http://localhost:4200/customer-history;locations=true;locations_place=Seattle
So the problem is, if they were to click away, and then RETURN to that page/component via the routerLink link, instead of getting the filtered data for that page, it will instead load this:
http://localhost:4200/customer-history
So my question is about how to pass those url params in as part of the routerLink. I assume it would look something like this, with square brackets for binding:
<a [routerLink]="['/customer-history', getParams()]" routerLinkActive="selected">Customer History</a>
What I'm not clear on is how I get those specific url params (just the filter params, not the component/page name) and pass them in via binding like this.
I know Angular makes available activatedRoute.snapshot, which I could get like this, to pass into getParams():
getParams()
{
return this.activatedRoute.snapshot;
}
But this will return the full url, not just the filter params part, which is what I need. So how would I get the part of the url I need, and pass it in here to append to "customer-history" in the url?
What would that look like in a basic implementation?
A way to resolve this, instead of using routerLink in the template, is to pass a function that resolves the correct page/component while subscribing to and navigating to that page and all relevant url params.
To do this I do this in the view:
<a (click)="goToCustomerHistory()">Customer History</a>
And that function in the component looks like this:
goToCustomerHistory()
{
this.route.params.subscribe(
(params: any) => {
this.page = params['page'];
this.locations = params['locations'];
this.locations_place = params['locations_place'];
}
);
this.router.navigate(
['/customer-history', {
page: this.page,
locations = this.locations;
locations_place = this.locations_place;
}]);
}
And of course, you also need to import Router and ActivatedRoute and inject in the constructor:
constructor(private router: Router,
private route: ActivatedRoute){}
Related
In my Angular app I have a component where I'm loading a grid view of some data being returned from the API. I also have some filters on the component, that the user can make use of to filter the data being returned.
This all works as expected -- until the user navigates away, and then returns to the component. At that point the filter selections are no longer being applied. In other words, the component is re-loaded in its "virgin" state, as if no filters had been selected.
So what I think I need to do is use ngOnInit() to read the url state from the browser window -- since all of the filters are still there at this point, as part of the query string, like so:
http://localhost:4200/consulting;page=1;pn_firstName.e=1;pn_firstName.v=John;pn_lastName.e=;pn_lastName.v=
According the above url/query string, the data should load after filtering on "firstName":"John".
So, my question is, what does the syntax look like in Angular to pre-load from the url like this within ngOnInit? Something like this?
ngOnInit() {
this.router.navigateByUrl(`consulting/${}`); // Should be current url/query string in the browser window
}
Basically I just need to know what the syntax should look like when I want this to be read dynamically from the current state of the url/query string in the browser window. Because, that way, whatever filters had been applied will work accordingly upon re-load of the component -- because they're still available in the query string.
UPDATE:
After a comment below, I'm thinking I could do something like this:
constructor(private route: ActivatedRoute) {}
And then, because this is an observable, I'd do something like:
ngOnInit() {
this.loadData()
}
loadData() {
this.router.navigate([this.route.queryParams.toArray()]);
}
Will something like this work?
Inject activatedRoute: ActivatedRoute into your component and the query params are available either through activatedRoute.queryParams or activatedRoute.queryParamMap.
See the docs about router.
Here is simple stackblitz demo which just renders the query params and you can play with the query parameters here.
There's a well known approach to support loading more than one model promise in an Ember route, using Ember.RSVP.hash:
// app/routes/posts.js
export default Ember.Route.extend({
model(params) {
return Ember.RSVP.hash({
posts: this.store.findAll('post', params),
tags: this.store.findAll('tag', params),
});
},
});
Now I have a page param, to be able to load the posts in batches, instead of loading them and showing them all at once. But page changes do not alter the tags. However, when the page param changes, the whole model of the route is triggered again to re-load, causing the app to re-fetch both the posts for the new page, and the same tags all over again.
Is there a way to fine-tune this so that the tags are not loaded when certain params change?
There are several ways to refacto your code. Like moving tags out of model. Or doing pagination differently (w/o model refresh). The one I like is writing a custom getAll utility.
var cache = {}; // model_name => Promise<[Record]>
function getAll(store, model) {
if(!store.modelFor(model).cacheable) {
return store.findAll(model);
}
if(!cache[model]) {
cache[model] = store.findAll(model);
}
return cache[model];
}
And in your model now
import { getAll } from 'utils/store';
...
tags: getAll('tag'),
I want to change state to force a reload of the browser window but I need to make the url hold a id on the end.
scope.takeAssessment = function (id) {
state.go('app.tab.assessmentsDetails');
};
This is the state that I'm trying to get to and as you can see I'm passing through an ID that ID needs to go on the end of the url to form something like below:
assessment/12312312312312 < (the numbers being the ID)
This is being called by a ng-click. There might be a better way to achieve what I want so please let me knwo in the comments below,.
When you need to pass params to state using $state.go, pass it as an object like below:
scope.takeAssessment = function (id) {
$state.go('app.tab.assessmentsDetails', { id: id});
};
You can also use ui-sref directive to specify state and params and it will be converted to its corresponding href. E.g.:
<a ui-sref="app.tab.assessmentsDetails({id: id})">Take assessment</a>
Refer this for details: https://github.com/angular-ui/ui-router/wiki/URL-Routing#using-parameters-in-links
I want to get part of a path in URL via Angular.js and i found solution:
http://mywebsite.com/one/HEREiWANT/three
first i do this:
app.config(function($locationProvider){
$locationProvider.html5Mode(true);
});
then i define my controller like this:
app.controller("mainCtrl", function ($scope,$location) {
//...
}
then with this method i can get what i want and it works!:
$scope.getURLPart = function () {
return pId = $location.path().split("/")[3]||"Unknown";
};
But this has a huge problem, right now after this changes, all of my linkes in my website doesn't work. When i click on a link, address in browsers address bar changes but i stay at the same page and redirection process doesn't happen. Why? How i can achieve what i want without with this problem?
In your state if your using state and yor passing params to your state then you can use
$stateparams to get the params
$stae.go("particular state name") to route to states
Using meteor for a test project. Can't figure out how to pass an ID and a search parameter when playing with the sample todo app they have.
For the moment, I have in my iron router:
this.route('team', {
path: '/team/:_id',
onBeforeAction: function() {
this.todosHandle = Meteor.subscribe('todos', this.params._id);
// Then filter mongoDB to search for the text
}});
The thing is, I also want to pass an optional search parameter to search for todos. So something like path: '/team/:_id(/search/:search)?'
Any ideas how to do this?
From your explanation, it sounds like you would like to carefully control which documents are actually published to the client, rather than publishing all of them and narrowing down your result set on the client. In this case, I would suggest first defining a publication on the server like so:
Meteor.publish('todosByTeamIdAndSearch', function(todoTeamId, searchParameter) {
var todosCursor = null;
// Check for teamId and searchParameter existence and set
// todosCursor accordingly. If neither exist, return an empty
// cursor, while returning a subset of documents depending on
// parameter existence.
todosCursor = Todos.find({teamId: todoTeamId, ...}); // pass parameters accordingly
return todosCursor;
});
To read more about defining more granular publications, check this out.
With a publication like the one above defined, you can then setup your route like so:
Router.route('/team/:_id/search/:search', {
name: 'team',
waitOn: function() {
return Meteor.subscribe('todosByTeamIdAndSearch', this.params._id, this.params.search);
},
data: function() {
if(this.ready()) {
// Access your Todos collection like you normally would
var todos = Todos.find({});
}
}
});
As you can see from the example route definition, you can define the path for the route exactly as you would like to see it directly in the call to the Router.route() function and access the parameters directly passed in like in the waitOn route option. Since the publication has been defined like I suggested, you can simply pass those route parameters right to the Meteor.subscribe() function. Then, in the data route option, once you have checked that your subscription is ready, you can access the Todos collection like normal with no further narrowing of the result set if you do not need to do so.
In order to learn more about how to configure your routes, check these two links out: Iron Router Route Parameters and Iron Router Route Options
On the client, you would just use Meteor.subscribe('todos'); in top-level code. 'todos' here doesn't refer to the Collection, it's an arbitrary string. Subscriptions don't care about what route you're on.
On the server, you would have a publish function like this:
Meteor.publish('todos', function() {
if (!Meteor.userId()) return;
// return all todos (you could pass whatever query params)
return Todos({});
});
Then, on your route definition:
Router.route('team', {
path: '/team/:_id',
data: function() {
if (this.params.query) { //if there's a query string
return Todos.find(/* according to the query string */).fetch();
else {
// return all the user's todos
return Todos.find({ uid: this.params._id }).fetch();
}
}
});