Inertia Manual Share State Lazily Inside Controller not Working - javascript

I'm going crazy because of this one, I have this address form in a very nested component and a route to validate the form input data. In the controller, I want to return back to the same page but share the form data to be able to fetch it in a parent component. I'm trying to follow Inertia DOCs to lazily share the data to be available to all components but for some reason this isn't working!
1- I'm submitting the form:
const submitAddressCheck = () => {
shippingDetailsForm.post(
route("cart.checkaddress", [props.webshop_slug]),
{}
);
};
2- The form gets validated as expected but it doesn't share the data globally to all components.
CartController.php
public function checkaddress(StoreAddressCheckRequest $request)
{
Inertia::share(
'testing',
fn ($request) => $request
? $request
: null
);
return redirect()->back();
}
Once I submit the form it gets validated and that's it, no new props passed, the data isn't being shared to my parent component or anything. Am I missing something?

Inertia::share() will not persist any data passed between requests. So if you share data and then redirect to another route, then your shared data will not be passed.
You are probably looking for Flash messages. With flash messages you use the with() method when redirecting to show errors, success messages, anything you like. Make sure you follow the documentation and add the code to the HandleInertiaRequests middleware.
When it is time to redirect, you do something like this:
return redirect()->back()->with('testing', $request->all());

Related

How to pass data from ActionFunction to Loader function in Remix.run?

I'm making an application with Remix.run. It consists of multiple steps where user fills forms and in the end gets success screen. All form steps are located on single route, but success is on it's own route.
How everything works
I have redux-like (actually XState, but it is not important) message processing on server. It receives current (or initial for initial load) state, message type and data from fields. It then returns new state to client and page is rendered based on that state. Server don't have any storage an I don't want to introduce one, since it will complicate things.
// form page
export async function loader() {
return json(await getInitialState())
}
export async function action({request}) {
let fd = await request.formData();
let {current_state, message_type, ...data} = Object.fromEntries(fd.entries());
return json(await getNextState(current_state, {type: message_type, data}));
}
export function unstable_shouldReload({submission}) {
return !submission
}
export default FormPage() {
let loaderData = useLoaderData();
let actionData = useActionData();
// awful, isn't it?
let currentState = actionData || loaderData;
}
My problems
First my problem was that loader was triggered on every form submit. So, ActionFunction returns new state, but LoaderFunction returns initial state. It would be fine, but to get initial state, loader runs API, so it's wasted calls. I solved it with unstable_ShouldReload, and it's kind of ok, but not really.
Now I got to final step of the form and feel that I have same problem, but now I have separate route, so I can't just prevent running LoaderFunction. I need LoaderFunction to actually run on new page, and get data passed from previous steps for it. Basically, it's the same problem - I need to use data posted in form usable inside loader.
I tried to replace code above using session cookie with all the data, but I have other problems with it. Action returns cookie with session data, loader reads it and returns new state. Unfortunately it fails in few scenarios. If there is current session, user can reload page, prevent sending info again and get result as data was sent. It is not how web "usually" works. I can change it, by destroying session on each load, but then very first POST request yields no result, if there is no JS on user side. Otherwise it is best solution yet (but limited by size of the cookie).
Question
How to pass data from last action to next loader, or process POST request in loader itself?
How to pass data from last action to next loader
The only way is using sessions and or cookies. If you want to only be able to read the session data once and ensure it's not there anymore after a reload you could use session.flash(key, value), then in your loader, when you do session.get(key) it will remove the flash key from the session, if you commit the session in the loader then the cookie will be updated to don't have that key anymore, after a reload it will not be there.
process POST request in loader itself
This is not possible, loader functions only handle GET and OPTIONS requests, for every other request method only the action function is used.

Using modal in CakePHP3 (or: Javascript in Controllers)

I have a standard form, generated via cake/bake.
When the form is send, my controller checks a (trivial) condition,
on condition A it just saves the data (patchEntity($foo,
$this->request->getData())
on condition B it should call a JS warning, and then save the data.
My favorite warning would be a Bootstrap4 Modal, but how can I call a modal in a controller?
I know this kind of violates the idea of MVC. So, alternative ideas are welcome.
The only solution I see atm is that I redirect to a new action which just opens a modal.
Given that the condition can only be evaluated on the server side, you could for example issue an AJAX request, either before sending the form, or for sending the form itself, and then respond with corresponding information that you can evaluate in your AJAX response handler to decide whether you need to show a modal.
You should then probably (re)send the form with an additional flag that indicates that the consecutive request stems from the warning dialog, and that you can go on with saving the data.
What is the problem in using flash component, like so :-
$this->Flash->error(__('This is the warning.'));
Remember to add $this->loadComponent('Flash'); in the initialize function of your controller, like so:-
class ArticlesController extends AppController{
public function initialize(){
parent::initialize();
$this->loadComponent('Flash'); // Include the FlashComponent
}
// Rest of your code here...
}

MobX - how to comunicate between stores?

In my application i have two MobX stores - store_A for handling user information (who is currently logged, etc), and store_B for handling events for all users.
After user login, i want to display all events regarding that user.
How can i access logged user info (from store_A) from within store_B so that i can filter events correctly?
At this point i have to store loggeduserName data inside my store_b to retrive that data...
Code from my events store:
class ObservableEventsStore {
...
//after logIn, save userName:
#action setUser(userName) {
this.givenUser = userName
}
...
#computed get filteredByUser() {
let filteredByUser = this.wholeList
.filter((event) => this.givenUser === event.user)
// this.givenUser is what i want to get from store_A
return filteredByUser
}
I want to get loggedUser data from UserStore, i have it stored there as well ...
There is no idiomatic approach, any means to obtain a reference to the userStore is valid. I think in general you can take three approaches to achieve this:
construct the userStore before the EventStore and pass the reference to the EventStore in it's constuctor (or set it afterwards)
If the UserStores is a singleton, just import and use it
Use a dependency injection system like InversifyJS

Angular JS + user authentication + page refresh

So this is my problem.
I can successfully login from my angularJS app using the auth factory i made that communicates to my pp rest API.
lets say Auth.login(user) -> (POST) myapi.com/user/login: the response is the user object that Auth saves locally. Thus Auth.getCurrentUser() returns local user object.
my rest API, i also have a myapi.com/user/get_loggedin_user which returns the current logged in user (using the php session). So if Auth.getCurrentUser should actually check if local user exists, if not do an ajax to myapi.com/user/get_loggedin_user and check if logged in before responding with null. One problem here is, ajax is annoying like this, you would then need to put in a success callback function and have all your code execute inside the success callback.
Now lets say im on the Angular App (website), mydomain.com/user/dashboard (already logged in), and then i refresh my browser. Now, when the page reloads, angular does not know my current user, so before it redirects me to mydomain/login, i want it to check if the user is logged in. i can obviously do a 1 time call within the controller, but is there a more easy way where i can register within a controller with some access restrictions (Eg: logged_in == true), and when you visit any page with logged in requirement, it checks local user (gets the user if does not exist), and redirects to login page if null, or display the page once it matches the requirements?
Different common page requirements: null, logged_in, admin, function: haveAccess(user, object).
NOTE: im using stateProvider
If I understood your question correctly, you are asking about how to check whether the user is logged in before the controller is invoked, and to avoid the check for a logged-in status in each controller that needs it.
If so, you should look into the resolve parameter - this exists both in $routerProvider and $stateProvide.
Essentially you could "resolve" your loggedInUser variable (by doing whatever you need to do via your MyAuth service.
Here's an example of what I mean with $routeProvider:
$routeProvider
.when("/someSecuredContent", {
templateUrl: 'someSecuredContent.html',
controller: 'SecuredController',
resolve: {
loggedInUser: function(MyAuth){
return MyAuth.loggedIn(); // MyAuth.loggedIn() should return a $q promise
}
}
});
Then in the controller, loggedInUser will be injected.
Here's a site with more examples.
Correct me if im wrong:
Do this within the Main Controller (make sure you inject the dependancies like rootScope, state, and your own Authfactory)
$rootScope.$on('$stateChangeStart', function (event, next, toParams) {
if (needToBeLoggedIn()) { //use the next object to read any data, and you can set on the state some flag
event.preventDefault()
MyAuth.loggedIn(function success(){ $state.go(next,toParams); }, function err (){/*send somewhere else*/});
}
})
Put logged_in = true to cookieStore in your login method after authentication as below.
$cookieStore.put('logged_in',true);
$rootScope.logged_in = true;
and in your Controller, do
$rootScope.logged_in = $cookieStore.get('logged_in');
Now you can use this logged_in variable anywhere in the UI to check if the user is logged in.
Make sure to use 'ngCookies' module in your app. and pass the $cookieStore dependency to your controller. You can even keep the user object itself similar to logged_in variable in cookies and retrieve it from cookies.
Make sure to do logged_in = false and clear other variables in cookies and set it to blank in your logout method.

How do I let my controller know of the status of loading data?

I'm working on a large AngularJS app in which I am trying to encapsulate all my Ajax code into various services which the controllers get data from. The problem revolves around needing to know the status of any ajax calls and displaying the correct information to the user. There could be no data found, data currently loading, or an error that has occurred preventing data from being loaded. The user needs to be shown a loading message, a "no data found" message, or an error message.
Let's say I have a ProjectService. Ideally if there was a method called getAllProjects it would return an array of projects. But that way I have no idea what is happening with the server communication.
So how to I let the controller know if data is loaded, loading, or an error has occurred? The best way I can come up with is using callbacks like in the pseudo code below. Is there any better way to accomplish such a thing or anything I may be overlooking?
Thanks.
app.controller( "ProjectController", function( $scope, ProjectService ){
// Set the initial / default status
$scope.loadStatus = "loading";
// Return an empty array initially that will be filled with
// any data that is returned from the server
// The callback function will be executed when the ajax call is finished
$scope.projects = ProjectService.getProjects(function( status ){
// Alert the controller of a status change
setStatus( status );
});
function setStatus( ){
$scope.loadStatus = status;
// ... update the view or whatever is needed when the status changes....
}
});
app.service( "ProjectService", function( $resource ){
return {
getAllProjects: function(){
// ... load and return the data from the server ...
}
};
});
In our codebase we've just been doing
$scope.flags.loading = true;
$http(...).success(function(){
$scope.flags.loading = false;
});
Yes, this is sort of simplistic, but not all queries require a loading overlay (such as during pagination or refreshing). This is why we have opted not to simply use a decorator.
However, lets say you want to, I can think of a few ways of doing this. Lets say you're like us and keep your flags together in an object. Then you can use associations to your advantage:
MyService.flags = $scope.flags
... (inside the service) ...
this.flags.loading = true/false;
By establishing a reference as a property of the service, you can do all the state toggling from within the service, and avoid cluttering your controller. Again though, this might create the possible drawback of having 2 or more close-together queries conflicting (first query finishes and removes the loading state before the second one completes).
For this reason we have been find with setting the flag. We don't really check for 'loaded' we just check for data or use success callbacks.

Categories