Angular: data passed from another component becomes undefined on the page reload - javascript

In my angular app, I need a component to pass data to another component which do not have parent child relationship.I have an Orders table in one component with each row representing an order. When user clicks on any specific row, I need navigation to OrderDetails component& pass the order object representing the clicked row along with it
validation.component.html
<tr *ngFor="let order of allOrders" (click)="onNavToOrderDetails(order)">
<td>{{order.id}}</td>
</tr>
validation.component.ts
onNavToOrderDetails(order) {
this.router.navigate(['orderdetails'], { state: {data: order} });
}
orderdetails.component.ts
order;
ngOnInit(): void {
this.order=history.state.data;
console.log(this.order);
}
orderdetails.component.html
<p>{{order.id}}</p>
orderdetails.component.html displays order id when navigated from validation.component.html but refreshing the orderdetails page cause order id to disappear. I understand on the page refresh history.state.data becomes undefined but how to get around this issue? Since the app is a SPA, storing the data from the validationcomponent to a service and using that service in the orderdetailscomponent won't work either.
Page refresh means reloading the entire angular app ,and order object stored in the serivce by the validation componentwill also disappear. How to solve this issue? I want previously stored data in a serivce to stay unaffected and display it again on page reload?

There are 3 ways to handle it:
Use sessionStorage (don't go for localStorage) , but then make sure to maintain sessionStorage data as per the scenarios
ngOnInit(): void {
if(history.state.data){
this.order=history.state.data;
sessionStorage.setItem('order_page_info', JSON.stringyfy(this.order));
}else{
this.order = JSON.parse(sessionStorage.getItem('order_page_info'))
}
}
Use Cache (not recommended)
Rather than passing entire data, pass the id as router data and make server call to fetch details for the id. This would maintain id in url and so you can fetch details on refresh by calling the server

try putting ngOnChanges
ngOnChanges detects changes from other component specially from api call of services
ngOnChanges(){
this.order=history.state.data
}

Related

When should I set localStorage in Angular?

I have a list of employee list on EmployeesComponent and there is "Education Overview" and "Salary Overview" buttons for each records. When I click one of the overview button it goes to the OverviewComponent first and then load the correponding component (salary or education) into this OverviewComponent. There is also a "Back" button on each of these salary and education components. The structure is as shown on the following image:
components
The problem is that: When I come back to the EmployeesComponent, I need to reload the paging params e.g. the last page number before navigating to the overview pages. For this I use localStorage and check the saved value on each page load of the EmployeesComponent.
searchParams: any;
ngOnInit() {
let searchParams = JSON.parse(localStorage.getItem('routeParams'))?.searchParameters;
if(searchParams){
this.searchParams = searchParams;
window.localStorage.removeItem('routeParams'); // remove routeParams from localStorage
// load list using this.searchParams
}
But I save the page params on the OverviewComponent so that use a single place for salary and education pages. I think it is not a good approach and it may cause the localStorage items to be mixed as they use the same key (for some reason I need to use the same key sometimes).
So, should I set the paging parameters just before navigating to the overview page in the EmployeesComponent? And then check them on loading EmployeesComponent? What is a proper way for this scenario?
You can use the query-params in routing.
So now when you redirect from employess component to overViewComponent, then based on click i.e., Education Overview or Salary Overview just send the query params with the url.
Then now when you get back to employess component, just use the query params value you get in overView component and you can get the information you want back in employess component.
Q- what is the most proper place for adding and removing paging items to local storage
A- Most proper place for adding and removing localstorage item is where it get's change.
In your case, just set localstorage in overView component where you are getting params ( this.activateRoute.params() ) inside this function. And remove the localstorage on ngOnInit function of employee component.

How to persist the view of a route in angular

Is there any way to persist the view of a page in angular so that as a User I can get the same view when I revisit the same route.
For eg. RouteA has a search bar and when user search something it loads results below, now if user has searched something on that page and
after that he leave that page and move to RouteB for some other operation. When he will revisit the page it should have the same view, that is searched term in the search bar and loaded data in the grid.
For your use case, you can save the FormGroup instance before the user navigates to a different route.
if (this.searchFormGroup.valid) {
this.searchService.setLastSearch(this.searchFormGroup);
}
where the definition for setLastSearch could be like
setLastSearch(searchFormGroup) {
this.lastSearch = searchFormGroup;
}
And when the user revisits the page, in the ngOnInit method check if there is already a saved search(the formGroup instance you saved before navigating).
this.lastSearch = this.searchService.getLastSearch();
. If the answer is yes use the patchValue to populate the current formGroup instance. Something like below
if (this.lastSearch) {
this.patchLastSearchedValue();
}
where the code for patchLastSearchedValue could be like
patchLastSearchedValue(): void {
Object.keys(this.lastSearch.value).forEach(name => {
this.searchFormGroup.controls[name].patchValue(
this.lastSearch.controls[name].value
);
});
}

Losing data on a browser refresh in Vue.js application

Background
In a Vue.js application I am calling Getter VueX function in the mount life cycle method. This works great and retrieves all my customer data when the page loads.
The problem I seem to be facing is when I refresh the web page the mount method does not fire and the data is all removed from state which I have no access to till I navigate away from the page and then come back. At the point I return the mount function fires and I have the customer data back again.
Example
mounted() {
this.getCustomers();
},
async getSelected() {
console.log("Customer ", this.customer.email);
this.currentCustomer = this.customer;
this.updateCustomerData.clientID = this.currentCustomer.client_id;
this.updateCustomerData.email = this.currentCustomer.email;
this.updateCustomerData.password = this.currentCustomer.password;
this.customer = this.customer.email;
await this.RECEIVE_CLIENT_NOTES(this.currentCustomer.client_id);
},
That function above calls a VueX Getter to retrieve data that is used inside of a Material Auto Complete Search component. It fires off when I navigate to the page but does not fire at all when I am on the page and refresh. The refresh clears the state and leaves me without data on page reload.
Question
How should I handle page refreshes in order to maintain my data or recall the Getter from VueX to repopulate my data?

AngularJs change back button location while redirecting to new location

In our shop the user selects a product (/shop/products) and then gets redirected to the first customization page (/shop/details). He does some customization and clicks "next". In our controller (productDetailsController) we create a new object (Order) with the selected properties and redirect to the next page (shop/individualization?orderId=2) where that order is further customized. Whenever the user now uses the browser back button we want to make that Order available to the previous page via parameter. So we need to change the url that the back button is directing to (/shop/details?orderId=2 instead of /shop/details).
In short:
/shop/products -nextButton- /shop/details -nextButton- shop/individualization?orderId=2
shop/individualization?orderId=2 -BROWSER-BACK- /shop/details?orderId=2
If I just use $location.replace() inside the controller it will back-button from shop/individualization?orderId=2 to the product selection /shop/products.
If I do two $location.path() inside one digest cycle it will just ignore the first one:
// inside the order creation promise...
var search = {orderId: createdOrder.id};
$location.path("/shop/details").search(search);
$location.path("/shop/individualization").search(search);
I can't use replace() when navigating from /shop/products to /shop/details because using the back button from there still needs to navigate to /shop/products.
Any suggestions?
Thanks!
Outlining a possible solution:
a service keeps track of the order (could be just the orderId, depends on your exact use case)
shop/individualization (or the action of shop/details that navigates to shop/individualization) sets the orderId in the service
both shop/individualization and shop/details define reloadOnSearch: false
both shop/individualization and shop/details bind the URL search parameter to the service; the controller logic could be:
app.controller("XXX", function($scope, orderService, $location) {
// initialization
var orderId = $location.search("orderId");
if( orderId ) {
orderService.setOrderId(orderId); // setOrderId() could handle loading the order too
}
else {
orderId = orderService.getOrderId();
// reloadOnSearch is false, so this doesn't trigger a navigation
if( orderId ) $location.search("orderId",orderId);
}
// ...rest of controller logic
});
Keep in mind:
What you are doing makes shop/details bookmarkable, so that the user can return at any time to see an old order. The system should be prepared to handle this case, e.g. reload the order from the server. If bookmarking is not desired, things are simplified: just use the service and drop the search param altogether.
You may also want to remove the order object from the orderService at some time.

Ember.js when using ModelFor('') in conjunction with the back button fails to load data

I am using Ember data and The RESTAdapter with an extension for Django.
Here is a JSBin
Here is how our routes are set up:
App.Router.map(function() {
this.resource('locations');
this.resource('location', {path:'locations/:location_id'}, function() {
this.resource('items', function() {
this.route('create');
});
this.resource('item', { path:'item/:item_id' }, function() {
this.route('edit');
});
});
});
App.LocationsRoute = Ember.Route.extend({
model: function () {
return this.get('store').find('location');
}
});
App.ItemsRoute = Ember.Route.extend({
model: function(){
//Get the model for the selected location and grab its item list.
//This will do a call to /locations/1/items
return this.modelFor('location').get('items');
}
});
Now this all works fine when we navigate to locations/1/items. The user is presented with a list of items relevant to the location with id 1.
When the user clicks one of these items it brings the url to #/locations/1/item/1 and displays the details of the item with id 1.
Now what doesnt work is this:
When I hit the back button the #/locations/1/items route loads but it does not have its data any more and no REST call to api/locations/1/items occurs. Even though the data displayed just fine when we first navigated to #/locations/1/items.
It is like Ember said "Well we already loaded that data, so we dont need to call the api again" but the data is somehow not being displayed in our template.
If I change the ItemsRoute model to this:
return this.get('store').find('item');
The scenario above works perfectly fine but the data is not based on our location.
Is there something I am missing with using this.modelFor? Or is my route set up incorrect?
Let me know if theres any extra info you need.
Update 1:
Having changed the model function to this I have discovered some new insights:
model: function(){
//Get the model for the selected location and grab its item list.
//This will do a call to /locations/1/items
var location = this.modelFor('location');
var items = location.get('items');
return items;
}
Upon first load of #/locations/1/items the location variable holds the location model and the items variable holds something which has a 'content' property, 'isFulfilled: true' and some other things.
This correctly works and displays the list of items. When i click on a particular item and got to #/locations/1/items/1 then hit the back button. The breakpoint triggers and location is still correctly populating with the location model.
However the items variable seems to just be a PromiseArray, do I need to somehow wait for the promise to be fulfilled before this model hook returns? I thought Ember already did this automatically? I suppose this means that the route is loading before the promise is fulfilled and thats why there is not data displayed?
Therefore my list is not populated correctly.
I'm on a phone, so I'll update this a bit later with more, but the problem is your location resource isn't a child of locations. Becaude of that, ember says why waste time fetching that route if it isn't a part of that resource path. It only hits the location route, which I'm betting you don't have a model hook for fetching the single location (at least based on the code above).
Ok, here is the deal. I have fixed the issue but have no idea why it fixed the issue.
Without further ado:
Here is a jsbin example of the exact setup I have. The only difference between the real one and the jsbin is the fact that I am using the RestAdapter instead of the FixtureAdapter. This is not technically true because I am using the ember-django-rest-adapter which extends the REST one.
The issue described in the question does not present itself in the jsbin but the exact setup with the ember-django-rest-adapter does present the issue.
The fix was to break the cyclic relationship between User -> Site -> Items -> User
For example if I comment out the 'locations' relationship in the User model, the back button works.
Or if I comment out the 'owner' relationship to User in the Item model the back button works.
I might ask a separate question to see what the reasoning behind the problem is, although if someone can shed any light in to why this is happening I'll happily accept the answer here.

Categories

Resources