I'm working on a auth based application where the user must be logged to access to a certain routes of the website. I haven't found documentation on how to accomplish this so I worked on a idea on how to solve it.
This is my function to know if a user is logged or not:
App.filterAuth = function(navigateTo) {
navigateTo = typeof navigateTo !== 'undefined' ? navigateTo : '#';
if (App.logged == false) {
App.vent.trigger('navigate', navigateTo);
}
};
My idea is that when a user is logged sets the App.logged = true. Then, when my router tries to execute a concrete router I can just call App.filterAuth(). This actually works and doing some debugging I see that everything is triggered corrected.
The problem? For example I execute this on a protected route that loads a feed of images for a user. When App.filterAuth() is executed I see that it tries to navigate to # but the execution of this feed view isn't stopped and also the view associated to my # route is not triggered.
Any idea on how I can improve this function that makes it work correctly?
Thanks in advance!
This might not be an answer to question directly, but there is a backbone plugin which can help you with what you try to do: https://github.com/boazsender/backbone.routefilter
You can specify additional methods, which will be triggered before / after route code is processed. If you return false from before callback, router code won't be evaluated: https://github.com/boazsender/backbone.routefilter#returning-false-from-within-a-before-filter
So, you could create before filter, which could check, if route should be secured. If it is secured and user is not authenticated, you just return false and router code is not used.
Related
Having Difficulty in Authorization part in Reactjs.In my app.js got permissions from backend and passed permissions to all components via context api . my main problem arise when user go to usercrudauthorisation.js ,and toogle the switch on / off then Permission value changes accordingly But my context api provides value i.e "u_create": 1,"u_delete": 1 at loading app.js component.The changed toogle switch button value will load when i reload the whole comoponent and re-render the app. I have supplies modulepermissions value according to module_name and what user can perform CRUD in that module via context api. i think route changes doesnot re render the app.js while route changes so that it provides module permissions accordingly? how to solve it? what will be best way to achieve it?
**app.js**
const getPermissions=()=>{}
above function return permission={"id": 1,"module_name": "Product","role": "users","u_create": 1,"u_delete": 1,}
that i have Passed to all components via context api shown in below code
let curr_module=routes.filter(route=>{
if(window.location.pathname===route.path){
return true;
}
return false
})
let curr_module_permission={}
if(curr_module&&curr_module.length>0 ){
let curr_permissions=permissions&&permissions.filter((p)=>{
if(p["module_name"].toUpperCase()=== "PRODUCT" ) {
return true
}
return false
})
if(curr_permissions.length>0){
curr_module_permission=curr_permissions[0]
<UserContext.Provider value={{ permissions : curr_module_permission}}>
**Usercrudauthorization.js**
Two toggle switch button i.e create ,delete i.e ON/OFF
I have a login page(Functional Component), When a user tries to login without entering the required fields, I have to show the error message(ex:"Email is required"). When an invalid field exists, I shouldn't make the API call.
But, without entering fields, when I click on login button, API call is done. Again clicking on login button stops the API call.
I have made this demo - https://stackblitz.com/edit/react-pd6vvk?file=login.js which explains the issue.
Steps to follow:
1.Click on login without filling any text values. You can see "API request made" statement when fields are invalid.
2.Again click on login button, "API Request stopped" statement is displayed now.
I am new to React and I don't know, the reason and the solution to fix this issue.
Can somebody please help me out?
Thank you,
Abhilash
Because, setValidFields has async behaviour :
validateLoginForm(); //<--- inside this setValidFieldsis async
console.log(validFields);
if (isFormValid()) { //<---- so,this will still have true value ( means not updated )
// inside validateLoginForm()
// this state change won,t be reflected immediately
setValidFields(validFields => ({
...validFields,
[key]: error.length == 0
}));
I have made few changes as per your code structure, as long as I know you will need useEffect also as shown in working demo.
WORKING DEMO
trying to use Microsoft's SSO with React, and I want to
on first load to activate the method to attempt to sign in. So that I don't need to ask the user to click a button, it should just be automatic.
I'm new to react and lifecycle methods, but it doesn't make sense to use componentWillLoad or componentDidLoad because it would just be checking everytime. Basically the whole site should be only viewable if logged in, so I believe setting up my protected paths after this should be doable
Any ideas on how to attempt this?
Edit:
useEffect = () => {
!this.props.account ? this.props.onSignIn() : <AuthWrapper/>
}, [];
You can use the useEffect hook in react and pass in an empty dependency so that the effect is only called once on initial page load. The same thing could be done with componentDidMount. You could put a function within the hook or lifecycle method to check if the user is logged in then show different pages based on if that value. Something like this:
useEffect(() => {
// check here if user is logged in
loggedIn ? showPageA : showPageB
}, []);
I need to simply update the URL params in some cases (through code) and not trigger any events. But, I still need to trigger certain actions if the user goes back using the browser back button or if he changes the url params manually.
Prior to UI-Router v1.x, I used to use { notify: false } in combination with the $stateChangeSuccess event for this kind of stuff. But now that the dynamic params are the way to go I can't figure out how to make this work any more.
I have defined all the params of the route as dynamic, and when I execute the $state.go(".", params), the controller does not get refreshed. Which is expected. The $transition.onSuccess does however still fire which I find slightly odd and I don't expect this event to fire in this case.
When I move back with the back button, or if I change a param manually, the same thing happens. The controller doesn't get refreshed and the $transition.onSuccess fires.
My main problem is how do I know one event came from the user, and the other one came from the code? What am I missing here? I checked out the transition param of the onSuccess callback, but couldn't find anything on it that would help me. Is there a different event/hook I should use or is this simply not possible any more with the latest UI-Router?
Thnx.
Seems like I found a way to find out where the transition came from. If somebody has a different/better solution, please post it, I don't find this the neatest solution ever but it works and considering I couldn't find a better one, I'll stick with it until someone shows me a better way.
// The "to:" ensures the event doesn't trigger for other routes
this.$transitions.onSuccess({ to: "your.route" }, (trans) => {
let changedParams = trans._changedParams();
if (trans._options.source === "url" && // transition came from URL change
changedParams && changedParams.length > 0) { // at least one param changed
// do something
}
});
I have read in several places that calling the Backbone.history.navigate function is considered bad practice.
For example Addy Osmani sais in his book "Developing Backbone.js Applications"
It is also possible for Router.navigate() to trigger the route along
with updating the URL fragment by passing the trigger:true option.
Note: This usage is discouraged...
http://addyosmani.github.io/backbone-fundamentals/#backbone.history
Or Derick Bailey in his blog post even sais:
You shouldn’t be executing the route’s handler from within your application, most of the time.
But I don't really understand the reasoning behind it and what would be a better solution.
In my opinion it is not really bad to call the navigate function with the trigger:true option. The route function could upon calling always check if the considered data is already loaded and show this loaded data instead of doing the whole work all over again...
There seems to be some confusion about what Router#navigate does exactly, I think.
Without any options set it will update the URL to the fragment provided.
E.g. router.navigate('todo/4/edit') will update the URL to #todo/4 AND will create a browser history entry for that URL. No route handlers are run.
However, setting trigger:true will update the URL, but it will also run the handler that was specified for that route (In Addy's example it will call the routers editTodo function) and create a browser history entry.
When passing replace:true the url will be updated, no handler will be called, but it will NOT create a browser history entry.
Then, what I think the answer is:
the reason why the usage of trigger:true is discouraged is simple, navigating from application state to application state to application state requires most of the time different code to be run than when navigating to a specific application state directly.
Let's say you have states A, B and C in your application. But state B builds upon state A and state C builds upon B.
In that case when you navigate from B to C only a specific part of code will need to be executed, while when hitting state C directly will probably execute some state checking and preparation:
has that data been loaded? If not, load it.
is the user logged in? If not redirect.
etc.
Let's take an example: State A (#list) shows a list of songs. State B (#login) is about user authentication and state C (#list/edit) allows for editing of the list of songs.
So, when the user lands on state A the list of songs is loaded and stored in a collection. He clicks on a login-button and is redirected to a login form. He successfully authenticates and is redirected back to the song list, but this time with delete-buttons next to the songs.
He bookmarks the last state (#list/edit).
Now, what needs to happen when the user clicks on the bookmark a few days later?
The application needs to load the songs, needs to verify the user is (still) logged in and react accordingly, stuff that in the state transition flow had already been done in the other states.
Now for a note of my own:
I'd never recommend the above approach in a real application as in the example. You should check whether the collection is loaded when going from B to C and not just assume it already is. Likewise you should check whether the user really is logged in. It's just an example.
IMO the router really is a special kind of view (think about it, it displays application state and translates user input into application state/events) and should always be treated as such. You should never ever rely on the router to transition between states, but rather let the router reflect the state transitions.
I have to disagree with #Stephen's answer here. And the main reason why is because the use of router.navigate({trigger : true}) gives the router responsibility to handle the application's state. It should only reflect application state, not control it.
Also, it is not a View's responsibility to change the hash of the window, this is the router's only job! Don't take it away from it! Good modularity and separation of concerns makes for a scalable and maintainable application.
Forwarding a person to a new section within your application
Backbone is an event driven framework, use events to communicate. There is absolutely no need to call router.navigate({ trigger : true }) since functionality should not be in the router. Here is an example of how I use the router and I think promotes good modularity and separation of concerns.
var Router = Backbone.Router.extend({
initialize: function(app) {
this.app = app;
},
routes: {
'videoLibrary' : function() { this.app.videoLibrary(); }
}
});
var Application = _.extend({}, Backbone.Events, {
initialize: function() {
this.router = new Router( this );
this.listenTo( Backbone, 'video:uploaded', function() {
this.router.navigate('/videoLibrary');
this.videoLibrary();
});
},
videoLibrary: function() {
//do useful stuff
}
});
var uploadView = Backbone.View.extend({
//...
uploadVideo: function() {
$.ajax({
//...
success: function() { Backbone.trigger('video:uploaded'); }
});
}
});
Your view does not need or want to know what to do when the user is done uploading, this is somebody else's responsibility. In this example, the router is just an entry point for the application's functionality, an event generated by the uploadView is another. The router always reflects the application state through hash changes and history but does not implement any functionality.
Testability
By separating concerns, you are enhancing the testability of your application. It's easy to have a spy on Backbone.trigger and make sure the view is working properly. It's less easy to mock a router.
Modules management
Also, if you use some module management like AMD or CommonJS, you will have to pass around the router's instance everywhere in the application in order to call it. Thus having close coupling in your application an this is not something you want.
In my opinion it's considered bad practice because you should imagine a Backbone application not like a Ruby On Rails application but rather like a Desktop application.
When I say RoR, I'm just saying a framework supporting routing in sense that a route brings you to a specific call to the controller to run a specific action (imagine a CRUD operation).
Backbone.history is intended just as a bookmark for the user so he can, for example, save a specific url, and run it again later. In this case he will find the same situation he left before.
When you say:
In my opinion it is not really bad to call the navigate function with
the trigger:true option. The route function could upon calling always
check if the considered data is already loaded and show this loaded
data instead of doing the whole work all over again...
That to me sounds smelly. If you are triggering a route and you are checking for the data to see if you have it, it means that you actually already had them so you should change your view accordingly without loading again the entire DOM with the same data.
That said trigger:true is there so do we have reason use it? In my opinion it is possible to use it if you are completely swapping a view.
Let's say I have an application with two tabs, one allows me to create a single resource, the other one let me see the list of the created resources. In the second tabs you are actually loading a Collection so data is different between the two. In this case I would use trigger:true.
That said I've been using Backbone for 2 weeks so I'm pretty new to this world but to me it sounds reasonable to discourage the use of this option.
It depends on your context.
If you have done something in your current view that might affect the view you are about to navigate to, for example creating for deleting a customer record, then setting trigger to true is the right thing to do.
Think about it. If you delete a customer record don't to want to refresh the list of customers to reflect that deletion?