I have an application where I can use popup views over the normal windows.
For getting the data the user needs to login so I have a token.
Has anyone experience with the way if there is no token, show first a view, then go further where you was heading to?
To make it more clear:
public void Init()
{
if (!CheckToken())
{
Task.Run(() => ShowViewModel<InsertPasswordViewModel>())
.ContinueWith(t => GetData());
}
else
{
//Do your thing
GetData();
}
}
The problem now is that the task is run so view is shown and immediately starts GetData but he has no token and crashes.
Any ideas or fixes are welcome
You shouldn't do navigation in Init. You are essentially navigating away from a page you are in the middle of navigating to. The navigation isn't finished executing when Init is called.
You are not getting a result from ShowViewModel either, so you can't rely on that returning anything when you are done with everything in InsertPasswordViewModel.
What you are probably looking for is navigating to your ViewModel with some kind of parameter telling where you came from. Then let InsertPasswordViewModel use that parameter to navigate back once password is OK.
You probably want to modify the View hierarchy too, which you can do through a custom presenter and a set of presentation hints.
Related
I am reading that the recommendation is to use OnParametersSetAsync() for any async calls (such as database calls) inside a Blazor server component. The problem though is that OnParametersSetAsync() will fire twice in a default Blazor Server application, and I obviously don't want to access the database twice.
To avoid this problem, some people recommend replacing the "ServerPrerendered" render-mode in _Host.cshtml to "Server", which will avoid OnParametersSetAsync() firing twice. However, I don't like this solution, because the application will take longer to load for the user if we removing the initial static HTML phase.
So, my solution so far has been to put my database access calls inside OnAfterRenderAsync(), and to call StateHasChanged() once I am done. It looks something like this:
public partial class ExampleComponent
{
[Inject]
public IUserDataAccess UserAccess { get; set; }
[Parameter]
public string UserEmail { get; set; }
public IUser User { get; set; }
private bool _isLoading = true;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender == false)
return;
User = await UserAccess.GetOneAsync(UserEmail);
_isLoading = false;
StateHasChanged();
}
}
The database call gets made only once, because of the "if (firstRender == false)" condition.
This approach has been working well for me so far, but I suspect that there is something wrong with this way of proceeding, because the examples given online of a valid call to OnAfterRenderAsync() usually only mention JavaScript calls. I don't why that is though. Is there anything wrong with the example code I am giving above? And if this approach is not recommended for some reason, then how can we avoid the double call to the database if we use OnParametersSetAsync() (excluding the server-mode change discussed above)?
Thanks.
Everyone gets hung up on the initial double load and make huge design compromises that they don't need to. [Sorry, and please don't take this the wrong way]This is a probably a good example.
If you're using Blazor Server then, yes the SPA "Start page" gets rendered twice - once on the static first load and once when the SPA starts in the page.
But that's it. Everything after that only gets rendered once. If you are so hung up on that initial load, don't do "too much" in the landing page, make it "light".
To restate - This only happens once, every other routing component load in your SPA only happens once unless you force a re-load or hit F5.
On putting lots of stuff in OnAfterRender{Async}, in general DON'T. It's designed to handle after render activity such as JSInterop stuff. Doing things that then need a call to StateAndChanged is a bit self defeating!
All your once only Db stuff should be in OnInitialized{Async} - normally OnInitializedAsync as it will be async code. You have little control over when OnParametersSet{async} gets run: code in here gets called whenever the component is re-rendered.
I have come across an issue with my mvc razor website where it doesn't refresh properly my page after hitting a timeout.
Here's how I process the whole thing:
My application allows to go through multiple steps. Each step reloads the same view (index.cshtml), the difference being in the session variables. The session variables are used as parameters to make ajax calls to a webservice to get the content to be displayed within the page.
The user can select items displayed within the page and go to the next step. When they click next, it triggers a post (ajax to controller) that will add these items into a session variable so that the selection stays throughout the process and that the user can see all the items selected in the previous steps as he progresses.
I don't want the application to crash when accessing methods (like next, previous, save, etc.) because the session variables have expired.
The way I figured I would prevent that is by decorating my methods in the controller with an attribute that checks if a session variable (isActive) is set, and if it is not, I add a RedirectResult to my filterContext.
public class SessionExpireAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.ActionDescriptor.GetCustomAttributes(typeof(SkipSessionExpireAttribute), false).Any()){
return;
}
if (HttpContext.Current.Session["isActive"] == null)
{
HttpContext.Current.Session["timedOut"] = "true";
filterContext.Result = new RedirectResult("~/Session-Expired", true);
return;
}
}
}
I have set in my routes a route for this url so that it "triggers" the Index method of the controller. (which is the method that returns the view). That way, all session variables would reset, the URL is changed to "Session-Expired" and the page is reload (sending back to the first, default, step).
I tested and it does "trigger" the Index method and goes through it as intended and the Index method returns the view. The problem is, my page never gets reloaded.
Index method is:
[SkipSessionExpire]
[Themed]
public ActionResult Index()
{
// Some code.
return View();
}
So I thought maybe it would be related to caching. I tried several methods found on this wonderful website such as adding:
[OutputCache(Location = System.Web.UI.OutputCacheLocation.None)]
with no real success.
I also tried some "hack" by redirecting to another action which would load a new page (test.cshtml) instead and this page would then redirect to the original view (index.cshtml). The test page worked when I tryed calling it with URL but for some reason, it still won't load the page when redirecting to the action.
My "Next" method (which doesn't get called because it is skipped because of the attribute, which is intended):
[HttpPost]
[Themed]
public JsonResult Next(object item)
{
//Some code.
return Json((string)Session["CurrentUrl"]);
}
The attribute [SessionExpire] is applied to the Controller (so every method within the controller gets it).
I just now tried to put a timeout on the ajax call made to the controller (from the view) in hopes that after tha ajax calls timeouts, it could just redirect to the Index page. It doesn't work (nothing happens on first click on the "next button", and second click bring me one step too far because it is like I went back to first page and then clicked next once).
Also, if I simply refresh the page after the timeout (manually). It realoads the first page with all session variables cleared (which is what I want to do programmatically).
To sum it, up. I would like to know if anyone has experienced something similar or if someone can guide me towards a solution so that I can get a way to refresh the page so that it's sent back to the first (default) step. Also, if I misunderstood something, feel free to tell me! I'm still learning so I'm more than glad to hear constructive feedbacks to help me understand how all of this works.
If you feel I'm too vague or you simply need additional details to help me solve this, just let me know and I'll add whatever is needed. I did quite a bit of research but I might have missed something so if you find something that could be useful, make sure to share.
Thanks!
I am trying to use MVVM with my windows phone 7 app. I am kinda confused on how I should do this.
I have a codebehind event for Navigating in a web browser. Now this event will eventually call a method that will log the user in using Google oAuth.
Now the code to call the login method to Google is in my service layer. I am not sure if I should make a property in my VM that calls my service layer or if I should just directly make a service layer object in my codebehind page and call it directly.
What is the best way to go?
Edit
Here is what I did
// code behind
private void wbGoogle_Navigating(object sender, NavigatingEventArgs e)
{
var queryParmas = e.Uri.ParseQueryString();
var code = queryParmas.ToList().Where(x => x.Key == "code").FirstOrDefault();
if (!code.Equals(new KeyValuePair<string, string>()))
{
loginVm.AuthenticateUser(code.Value, TestCallBack);
}
}
private void TestCallBack(Authorization a)
{
var d = a;
// maybe assign "a" to some property back in the VM.
}
// Vm
public void AuthenticateUser(string code, Action<GoogleContacts.Model.Authorization> CallbackOnSuccess)
{
loginService.SendCode(code, CallbackOnSuccess);
}
// service layer
public void SendCode(string code, Action<Authorization> CallbackOnSuccess)
{
var request = new RestRequest(TokenEndPoint, Method.POST);
request.AddParameter("code", code);
request.AddParameter("client_id", ClientId);
request.AddParameter("client_secret", SecretKey);
request.AddParameter("grant_type", GrantType);
request.AddParameter("redirect_uri", RedirectUrl);
client.ExecuteAsync<Authorization>(request, response =>
{
CallbackOnSuccess(response.Data);
});
}
The View Model is a model of your view - it is where all your logical work is actually done. The responsibility of the View is to present information to the UI and collect user input. Any other tasks performed by the View begins to tightly couple your code-base, and you end up with a maintenance nightmare.
To answer your question directly, your View (Code-Behind) should pass the fact that a UI element has been clicked on to a method (command binding or other mechanism) in your View Model.
Then, you Veiw Model decides what to do next. It may decide that it's time to navigate to a particular web-page, at which time it signals the view to move to the requested page.
The precise method upon which the ViewModel and View communicate has a lot to do with which framework you are using, and if you are a "ViewModel First" or a "View First" kinda guy.
To keep it simple... View Models are your business logic and brains of the app. The View should only deal with the display of data. I would consider authentication part of the "brains" and put that in your view model somewhere. You might even get a bit more sophisticated (depending on the needs of your app) and put the authentication Login method in a base class of your view model. This way the login call would be accessible via any page if need be.
Putting it in your code behind just tightly couples that code with your view - making it not very reusable. I would avoid that if you are going for the best architecture. Its ok to put stuff in your code behind of a view.
I would strongly disagree with the comment that MVVM exists to avoid code behind. The rule of thumb I use is this: Does the purpose of the View's codebehind exist to support view/display functions only? (perhaps you need to code to animate something or change a data template - thus changing its display). Code behind logic should be display specific. Thus, by this rule, a Login is not display specific and should not be a code behind method..
My 2 cents
This is specifically a Caliburn.Micro question I think, as it has to do with how CB handles navigation in windows phone 7.
I have a view that has the option of launching a phone number chooser. Once the result comes back I store it and navigate away, only the navigation wont work. I assume this is because the Handle method is working with the task and not my view. I know I can stick a button down the end of the page to navigate after the handle is finished but I would like this to happen once the result comes back.
This is what I am doing.
public void Handle(TaskCompleted<PhoneNumberResult> message)
{
webtext.Recipient = message.Result.PhoneNumber;
webtext.RecipientDisplayName = message.Result.DisplayName;
CommitWebtextToStorage();
events.Unsubscribe(this);
navigationService.UriFor<ComposeViewModel>();
}
Which wont work. I also can't call a method inside that as that would be the same as what I am doing. I need to let the handle method exit and then call the navigation service.
Actually, the navigation should look like:
navigationService.UriFor<ComposeViewModel>().Navigate();
(note the final Navigate method)
If it was just a typo in the question, I guess the issue could have to do with the timing of application resuming (which occurs when you return back to the application after the chooser task is completed).
In that case, could you please create an issue for this?
I am in a bit tricky situation. I am using JavaScript's PageMethod functionality where I am invoking a PageMethod which works like a gem. However I am having an issue in accessing the HttpContext's state which returns me a value "SYSTEM" for
HttpContext.Current.User.Identity.Name
which is not the actual current User Name.
I know there are couple options like storing HttpContext.Current in a Session or saving Context's state in some other custom container but given a web farm environment I am assuming that this will not work as expected.
Here is the code I am working on with
function MyFunction(){
PageMethod.MyPageMethod();
}
here is the signature of the server method
[System.Web.Services.WebMethod()]
public static void MyPageMethod()
{
// gives me "SYSTEM"
var user = HttpContext.Current.User.Identity.Name;
}
Also if I use the above code to access user name in OnLoad event of the page then it works fine and returns me the CurrentUserName.
I am trying to get the above code to work in an ASP.NET Webform... :)
So I am wondering if there is a way to access the current actual user in page methods without making use of sessions.
Any help will be deeply appreciated.
NiK...
After quite some reading I think I was trying to do something which is not correct as to how page methods work. It gets quite tricky when your application's authentication system is windows based and these page methods when you invoke from JavaScript will not cause a postback and do not invoke the HttpModules. Instead it just calls that page method.
FYI, we had our own custom HTTPModule to handle security.This is even before any other HttpModule occurs and this was not being invoked while calling the page method as we are not doing a postback or even a partial postback (so the whole "niche" of a HTTPPost was missing). Moreover this led to a conclusion that we were making service calls without any authentication and was potentially a big security issue for us.
The bottom line is it was a bad design, well having said that I would like to mention about the solution/workaround we came up with and here is what we did. So, the only option we had is to do a postback keeping the UI alive and we wanted to update a label's message asynchronously and we achieved it by doing a hack using Sys.Application.add_init.
<script language="javascript" type="text/javascript" >
Sys.Application.add_init(function() {
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);
});
function beginProcess() {
processCurrentItem();
}
var currentItem = 0;
function processCurrentItem() {
if (currentItem < 5) {
currentItem = currentItem + 1;
__doPostBack('updatePanel', currentItem);
}
}
function endRequest() {
processCurrentItem();
}
</script>
The markup we had in place was pretty simple with a label in the update panel and a button that invokes the "beginProcess()" function. Finally in the OnLoad we had the following code in place
protected override void OnLoad(EventArgs e)
{
if (this.IsPostBack)
{
this.lblLabel.Text = "text you may wanna update with";
// Call the Method you may wanna call
// also you may use Request["__EVENTARGUMENT"] to know who caused the
// postback and Request["__EVENTTARGET"] to access the argument you may
// have passed in.
}
}
And this solution is no longer using the JavaScript Page methods. And based on this solution if anyone thinks I am missing something here or think there is any other other way of doing this then do update this post with your suggestions.
NiK