My application is based on AngularJS 1.8 and angular-ui-router. On Successful login of the user, application is navigating user to a particular URL location by doing:
$window.location.href = "../"; //Let's call it Navigation-1
Now based on user type, the application adds query parameters to the URL, and causes another URL navigation
/?user=admin //Let's call it Navigation-2
These Navigations causes the states to retrigger (twice in this case), thus sending duplicate REST calls to the server.
My question is How do I avoid duplicate REST calls? I tried to capture events such as 'load' etc. but it doesn't work.
Related
I am building a library for listing products in a web application. It has to have filter, search and sort features. I have a web service that when called with filter, search and sort parameters can return the result set with all the those parameters applied. If page number is passed along with that, with number of products per page, it can return that specific page as well. It looks very much suitable to have the data populated through AJAX at client side using this web service. However the page will lose all the parameters (filter, search & sort) when clicking back button and coming back and the page will display the default list of products, as the URL will remain the same as below, irrespective of the filter or page or search or sort parameter
<domain>/productlist
. To retain them, I have to save these in sessionStorage or any other such mechanism. Will this be a violation of REST principles? Do I have to avoid AJAX and have the parameters always passed in the URL for the actions to be repeatable and abide by REST principles like
<domain>/productlist?filter=f1f2f3&search=apple&sort=price&order=1&page=3&items=10?
I may be wrong in understanding REST as well, as I am a bit new to this. So would like to understand better to have a proper & compliant design.
To retain them, there should have some better approaches instead of putting those params into session storage; one of the approach would be for every AJAX requests, pushing the search params into the window.history via window.history.pushState function, once user go back to previous page, all you have to do is check whether urlParams is filled with something or empty, and fetch the data according to the urlParams.
REST is a concept that how you should handle the requests throughout the frontend and backend.
AJAX is a method of fetching data from backend.
They could coexist therefore you could use AJAX and at the same time abide the statelessness of REST.
I've a single page application with my own custom router.
window.onhashchange = function(event) {...
and I've hash bangs like following.
#!/products
#!/products/1
#!/brands
#!/brands/1
But they seem to emphasize GET requests, I'm listening to URL changes and is there a REST based convention clean URL's based on industry standard, perhaps used by angular, React etc. to indicate a POST request or DELETE request.
So router can dispatch a respective call accordingly.
hashbang urls are not meant to indicate any REST based remote calls, instead it's used to define to state of a SPA page it's currently in. State means the kind of content that's visible for the specific hash bang url.
for instance for #!/products display product related forms, controls and associated content and vice versa for #!/brands.
It's the user actions on the associated content that the developer needs to interpret and translate them into REST specific calls.
I have product listing page, where I do have all data to show user, if user is applying filters, I am filtering list on client side itself using Angular 2,
Now if user move forward to project details page and click browser back button, all applied filters vanished as it should be, But I need to implement so that on back button all applied filters should persist.
Solutions I am thinking: -
Approach : Whenever User apply filter we add that in URL and redirect.
Problem : On every URL redirect API's will be called.
Is there a better way I can approach this problem ?
Storing things in the URL as arguments is a good approach, since you don't depend on hidden state (global application variables) to build your view.
However I'd not intercept the routing component, but rather use Angular's support for structured URLs and filter the data in the onInit method or whenever it is available.
If you want the filter criteria being visible in the URL (and to be bookmarkable) use router parameters and redirect to the URL containing the updated parameters. If you use routerCanReuse() { return true; }, then the component is not even reloaded but justrouterOnReuse()` is called where you can acquire the updated parameters.
If you don't want the filter criteria to appear in the URL, use a shared service to store the parameters, then navigating doesn't destroy the parameter values. Ensure that the provider for the service is provide high enough (AppComponent for example) for it to not get destroyed when a component gets destroyed by routing away.
I had a similar problem with the added condition that, I had to redirect the user back to listing page with the filters applied when he used the save functionality.
The approach I used was to combing both a service and having filters in the URL parameters.
The reason to use both is,
You could enter the product detail page in 3 ways:
From Product Listing.
Directly from URL.
From some other, where you might have given the link.
And so you cannot just use this._location.back(); as you might not want to redirect to the last page in all cases.
So my approach was to store the last URL in a service, and in the detail component check if there is any stored URL.
An important thing here is to check and pop the stored URL on the ngOnInit() as the user might not always click on save.
Attaching the code to the service, it's fairly basic.
#Injectable()
export class ReturnHistoryService {
private _returnURL: string;
saveReturnURL(returnURL: string) {
this._returnURL = returnURL;
}
popReturnURL() {
let returnURL = this._returnURL;
this._returnURL = null;
return returnURL;
}
`
Make sure the provider is not per component.
I want to allow users to request webpages of my website both directly from the server or using links, which will be handled by Backbone's router.
When a user requests a webpage directly from the server, a full page is served (with html, head, body, stylesheets and scripts).
On the other hand when a user requests a webpage by clicking on a link, only the relevant part of the page is requested and then inserted in the correct place, and other elements of the webpage remain untouched.
By inserting in the correct place I mean creating a View once a particular route is reached. The view is then initialized and calls its render method to fetch the relevant part of the webpage and inserts it into DOM using $el.html(content).
But I do not want to call the view's render method when a webpage was fetched directly from the server, because all needed content has already been rendered, and re-rendering it only causes some ui-flickering effects.
Is there some common way to let Views know that they shouldn't render themselves, because the fully rendered webpage has been fetched from the server?
I could pass a flag like clientSideNavigation = true to the router, everytime a link is clicked, which then will be passed to views by the router so that they know whether to render the content or not.
But it does not work when user uses aa back/foward buttons.
I could also check in a view if within its $el there is some particular element that should be present on this webpage - for instance if I had a view called CatsView I could check if #cats-box is within its $el element. But it involves some more DOM manipulations, which I would prefer to avoid.
Have a root view and have place holder for child views. First time render the complete page from the server.
For rendering parts of the view on link clicks, you can define corresponding events hash on the root view.
Let the event handler callbacks call a controller(custom js object) which does the job of loading the data ,constructing the view and passing the data to it.
Finally also update the url with Router navigate(http://backbonejs.org/#Router-navigate) method with {trigger:false} to the corresponding url, so then when refresh is hit, the user comes back to the same view.
In the router callback for the specified url call the same method on controller object by passing a flag so that the functionality is in sync and also using the flag you can prevent calling router navigate method since its not required.
I would like to allow my users to share the the pages on my website, using a plug and play add-on such as 'AddThis'.
The problem with AddThis, is that I want to replace the value of the current page, with another page, that gets a unique hash as a query parameter, to identify the person who shared the link, in case anyone clicks the link and enters the website.
To obtain the unique hash, I need to call a RESTful resource.
What I need is:
An ability to have a callback for the share click event, where I will trigger the AJAX request to get the unique hash from the server(creating a post entity, which holds a reference to the user_id, and the shared object).
After the AJAX request completes, I need to set the share link.
Of course I need to repeat 1 and 2 for each new post \ user, and I need them to happen before the share dialog actually opens(with the default \ previous link)
This SO question demonstrates a similar issue, but it hasn't been correctly answered either.