I'm currently working on a hosted/client Blazor WebAssembly app, which when initially run works fine.
In the 'OnInitializedAsync' method of the page being rendered is a javascript call to retrieve the width of an html element on the page. If I set a breakpoint in the javascript function, on initial app run the browser is displaying the rendered page and the element properties are correctly retrieved. However, when I refresh the browser page, when the breakpoint is hit the browser hasn't finished rendering the page and so the element's width is zero.
Can anyone tell me why this is and how I can fix the problem?
<div #ref="_carPanelWidth">
....
</div>
#code {
private ElementReference _carPanelWidth;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
var carPanelWidth = await JsRuntime.InvokeAsync<int>("utilityFunctions.getElementWidth", new object[] { _carPanelWidth });
StateHasChanged();
}
}
window.utilityFunctions = {
getElementWidth: function (element) {
return element.offsetWidth;
}
};
I deal with a similar situation when rendering grids. It causes a flashiness which looks unprofessional.
What I do is mark the html elements as hidden and later show them, when I know everything is loaded. This completely eliminates flash, if that is the problem you are having.
<div id="table_id_div" style="visibility:hidden">
I use interopt to apply the DataTable.com stuff to the table and when that is complete the javascript runs...
$("#" + tableName + "_div").attr('style', 'visibility : visible');
This seems to do the trick.
Related
I am using a blazor sever app
I have a control that when clicked using the #onclick event handler I want to navigate to a new page using the NavigationManager in the click event method.
it doesn't really matter what the control is (button, a, tr, etc) they all have the same behavior
if I put a break point in the HTML I can see the current page is re-rendering before it goes to the new page.
a simple way to reproduce this behavior is to make a new blazor project and in the counter.razor page
change the code to this
#page "/counter"
<h1>Counter</h1>
#if (1 == 1)
{
<p>Current count: #currentCount</p>
}
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private void IncrementCount()
{
//currentCount++;
}
}
put a break point on the HTML line "#if (1 == 1)"
when the button is clicked it calls the click event which does nothing (code commented out), it then re-renders the page and the break point is hit.
The same happens if I add code in the click event calling navigationManager to navigate away from the page, it re-renders before it leaves the page when nothing has changed on the page.
adding onclick:preventDefault and/or #onclick:stopPropagation does not change this
the only thing I have found that does work is adding
private bool c_blnStopRendering = false;
protected override bool ShouldRender()
{
if (c_blnStopRendering == true) { return false; }
return base.ShouldRender();
}
and setting the c_blnStopRendering = true; in the click event
but this seems like over kill and very manual to add it everywhere it is needed
That's the only way to do it at the moment. There is a request in the backlog https://github.com/dotnet/aspnetcore/issues/18919
I was wondering how you got on with this. I had exactly the same problem (I just wanted to NavigateTo without any re-rendering). Overriding ShouldRender to return false, stopped everything - i.e. it didn't NavigateTo the URL. I was however, Navigating to the same page, but with different parameters in the QS. I seem to have solved the problem, (I think !) when I discovered an optional parameter in the NavigateTo method (public void NavigateTo(string uri, bool forceLoad = false);). I set that to true and it seems to be OK now. Was just interested in your experience
After a postback, I want my page to have focus on a child control of a gridview, but scroll the page to a different part.
the standard myGridView.Focus(), called on the Page_Load or Page_prerender, insert a
WebForm_AutoFocus('myGridViewClientID');
in the rendered html.
This function move also the scroll not to the required position
Any suggestion?
my try: use some function injected by Asp.NET:
function FocusWithoutScroll(focusId) {
var targetControl;
if (__nonMSDOMBrowser) {
targetControl = document.getElementById(focusId);
}
else {
targetControl = document.all[focusId];
}
var focused = targetControl;
if (targetControl && (!WebForm_CanFocus(targetControl))) {
focused = WebForm_FindFirstFocusableChild(targetControl);
}
if (focused) {
try {
focused.focus();
}
catch (e) {
}
}
}
but in order to use this code, I have to include some .axd resource files: it seems ASP.NET automatically include them when you set
someControl.Focus();
in your server side code. but this in turn insert the
WebForm_AutoFocus('myGridViewClientID');
which scroll the page to the wrong position
There's a client-side method scrollIntoView that scrolls page till the element is visible. You can issue server-side command:
ClientScript.RegisterStartupScript(this.GetType(), "MyScript","document.getElementById('SecondElementID').scrollIntoView();", true);
Where 'SecondElementID' is id of the element you want to scroll to.
Demo: http://jsfiddle.net/v8455c79/ this demo shows how focus can be set on one element and page scrolled to another
Summary
I'm having style issues when flipping master pages via a button event in asp.net 4.0. The new master switches, but the css from the old master remains. I don't understand how this could happen as the styles are defined within the head of the old master, and i can clearly see via the markup the new master is being displayed with whats supposed to be a totally different set of styles. Also, viewing source shows all the new css declarations in the head. How can i get this to "refresh" or "reload"?
Some details
I'm implementing a mobile version of my asp.net site. If a mobile device is detected i set a cookie and switch the master page in the preinit to a mobile friendly one. This works fine:
protected virtual void Page_PreInit(Object sender, EventArgs e)
{
if (IsMobile)
this.Page.MasterPageFile = "m-" + this.Page.MasterPageFile;
}
I have a "full site" button at the bottom that allows you to flip back and forth between the mobile and desktop view. When clicking it, i change the value in the cookie. Then when the page redirects to itself, the value is checked, and it gives the respective masterpage. This also "works", i can tell the right masterpage is rendering via markup. Except the styles from the mobile version remain even when the desktop master is being displayed. I did the redirect thinking it would prevent this.
// desktop/mobile site toggle button click event
protected void viewMobileButton_Click(Object sender, EventArgs e)
{
HttpCookie isMobileCookie = Cookies.snatchCookie("isMobile");
if (bool.Parse(isMobileCookie.Value))
Cookies.bakeCookie("isMobile", "false");
else
Cookies.bakeCookie("isMobile", "true");
Response.Redirect(Request.RawUrl);
}
This is the first time I've done anything like this, and not sure if i'm even going about it the right way, or how to debug from here. Thanks in advance for any help.
Edit
Ok, so i figured out it's related to the JQuery Mobile Scripts. JQuery Mobile has this way of tying pages together. I don't fully understand it, i think they use it for page transitions, and it's preventing my new CSS from registering. When i turn it off, my masterpage flips fine with css included. I'm looking into a way to turn off JQuery Mobile before my redirect. Note sure how though yet.
The problem ended up being related to JQuery Mobile AJAX for page-transitions. JQuery Mobile does not load the head of the document on additional page requests after the first.
So when i'd switch the mobile master to the desktop master, the head of the document wouldn't load to bring in my styles. There are a few way's this can be fixed:
This way just turns off AJAX altogether, and fixes the problem, but then you can't benefit from it:
<form data-ajax="false">
This is a way to do it problematically, but remind you, it will not work via an event after initialization of JQuery Mobile, so again you can't benefit from it:
$.mobile.ajaxEnabled = false;
The above two solutions i support could work if you redirected through a page first if you have to use an onclick event and an event handler.
A better solution is to add rel="external" to the link to tell JQM it's and outgoing link.
<a href="myself.com?mobile=true" rel="external" >
But because i couldn't run some code i wanted to in order to change the cookie, i had to pass a query string parameter, check it on the preinit, then set the cookie which my page also looks at on the preinit and flips the master.
Here's my full solution below in case someone is out there doing the exact same thing. Note because my website is using aliasing, i had to read Request.RawUrl and parse it myself since the Request.QueryString object did not contain the values i passed.
// reusable function that parses a string in standard query string format(foo=bar&dave=awesome) into a Dictionary collection of key/value pairs
// return the reference to the object, you have to assign it to a local un-instantiated name
// will accept a full url, or just a query string
protected Dictionary<string, string> parseQueryString(string url)
{
Dictionary<string, string> d = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(url))
{
// if the string is still a full url vs just the query string
if (url.Contains("?"))
{
string[] urlArray = url.Split('?');
url = urlArray[1]; // snip the non query string business away
}
string[] paramArray = url.Split('&');
foreach (string param in paramArray)
{
if (param.Contains("="))
{
int index = param.IndexOf('=');
d.Add(param.Substring(0, index), param.Substring(++index));
}
}
}
return d;
}
Then i just use my dictionary object to evaluate and rebuild my url with the opposite mobile value, dynamically setting the href on the toggle link. Some code is obviosuly left out, but for perspective, base._iPage.QueryStringParams hold my dictionary object that was returned, and base._iPage.IsMobile is just a bool property i also have via the page interface i use, that all my pages, and user controls, ect, can talk to.
// get the left side fo the url, without querystrings
StringBuilder url = new StringBuilder(Request.RawUrl.Split('?')[0]);
// build link to self, preserving query strings, except flipping mobile value
if (base._iPage.QueryStringParams.Count != 0)
{
if (base._iPage.QueryStringParams.ContainsKey("mobile"))
{
// set to opposite of current
base._iPage.QueryStringParams["mobile"] = (!base._iPage.IsMobile).ToString();
}
int count = 0;
url.Append('?');
// loop through query string params, and add them back on
foreach (KeyValuePair<string, string> item in base._iPage.QueryStringParams)
{
count++;
url.Append(item.Key + "=" + item.Value + (count == base._iPage.QueryStringParams.Count ? "" : "&" ));
}
}
// assign rebuild url to href of toggle link
viewMobileButton.HRef = url.ToString();
}
Then on my pageinit this is where i actually check, first the quesry string, then the cookie, if neither of those are present, i run my mobile detection method, and set a cookie, and my interface bool property for easy access to conditionals that depends on it.
QueryStringParams = base.parseQueryString(Request.RawUrl);
if (QueryStringParams.ContainsKey("mobile") ? QueryStringParams["mobile"].ToLower().Equals("true") : false)
{
Cookies.bakeCookie("isMobile", "true"); // create a cookie
IsMobile = true;
}
else if (QueryStringParams.ContainsKey("mobile") ? QueryStringParams["mobile"].ToLower().Equals("false") : false)
{
Cookies.bakeCookie("isMobile", "false"); // create a cookie
IsMobile = false;
}
else
{
IsMobile = base.mobileDetection();
}
if (IsMobile)
this.Page.MasterPageFile = "m-" + this.Page.MasterPageFile;
}
On a list page, clicking on one of the items brings up the details in a modal popup window which will have its own functionality (like validation, updating etc). What's the best practice to implement this (not looking for a hack). I see two options here:
Hide the details markup until a list item is clicked at which time, do a ajax request to get the details and populate and show the details section.
Have the details section as a separate page by itself. On a list item click, show this page in a modal window (is this even possible?) This is similar to the IFrame approach and sounds like an old school approach.
What are the pros of cons of these approaches or are there other ways of doing this? There should not be a postback on list item click.
Edit: Any more opinions are appreciated.
I'm doing option 1 currently, it's very lightweight and all you need is an ajax post (jQuery or UpdatePanel) and some modal (I'm using jQery UI). It's easier than a full page post, plus you have the added benefit of being able to manipulate the page you're in as part of the result.
For example I have grids in the page, the editor is modal, usually with more detail, when you hit save, the grid is updated. I've put this in a generic template solution and it's very easy to work with, and is as light as webforms can be in that situation, so I'm all in favor of option 1.
Here's an example approach, having your modal control inherit from UpdatePanel (code condensed for brevity):
public class Modal : UpdatePanel
{
private bool? _show;
public string Title
{
get { return ViewState.Get("Title", string.Empty); }
set { ViewState.Set("Title", value); }
}
public string SaveButtonText
{
get { return ViewState.Get("SaveButtonText", "Save"); }
set { ViewState.Set("SaveButtonText", value); }
}
protected override void OnPreRender(EventArgs e)
{
if (_show.HasValue) RegScript(_show.Value);
base.OnPreRender(e);
}
public new Modal Update() { base.Update();return this;}
public Modal Show() { _show = true; return this; }
public Modal Hide() { _show = false; return this; }
private void RegScript(bool show)
{
const string scriptShow = "$(function() {{ modal('{0}','{1}','{2}'); }});";
ScriptManager.RegisterStartupScript(this, typeof (Modal),
ClientID + (show ? "s" : "h"),
string.Format(scriptShow, ClientID, Title, SaveButtonText), true);
}
}
In javascript:
function modal(id, mTitle, saveText) {
var btns = {};
btns[saveText || "Save"] = function() {
$(this).find("input[id$=MySaveButton]").click();
};
btns.Close = function() {
$(this).dialog("close");
};
return $("#" + id).dialog('destroy').dialog({
title: mTitle,
modal: true,
width: (width || '650') + 'px',
resizable: false,
buttons: btns
}).parent().appendTo($("form:first"));
}
Then in your markup (Can't think of a better name than MyControls right now, sorry!):
<MyControls:Modal ID="MyPanel" runat="server" UpdateMode="Conditional" Title="Details">
//Any Controls here, ListView, whatever
<asp:Button ID="MySaveButton" runat="server" OnClick="DoSomething" />
</MyControls:Modal>
In you pages codebehind you can do:
MyPanel.Update().Show();
Fr your approach, I'd have a jQuery action that sets an input field and clicks a button in the modal, triggering the update panel to postback, and in that code that loads the details into whatever control is in the modal, just call MyPanel.Update.Show(); and it'll be on the screen when the update panel ajax request comes back.
The example above, using jQuery UI will have 2 buttons on the modal, one to close and do nothing, one to save, clicking that MySaveButton inside the modal, and you can do whatever you want on then server, calling MyPanel.Hide() if successful, or put an error message in the panel if validation fails, just don't call MyModal.Hide() and it'll stay up for the user after the postback.
I have a JavaScript method that I need to run on one of my pages, in particular, the onresize event.
However, I don't see how I can set that event from my content page. I wish I could just put it on my master page, but I don't have the need for the method to be called on all pages that use that master page.
Any help would be appreciated.
Place the following in your content page:
<script type="text/javascript">
// here is a cross-browser compatible way of connecting
// handlers to events, in case you don't have one
function attachEventHandler(element, eventToHandle, eventHandler) {
if(element.attachEvent) {
element.attachEvent(eventToHandle, eventHandler);
} else if(element.addEventListener) {
element.addEventListener(eventToHandle.replace("on", ""), eventHandler, false);
} else {
element[eventToHandle] = eventHandler;
}
}
attachEventHandler(window, "onresize", function() {
// the code you want to run when the browser is resized
});
</script>
That code should give you the basic idea of what you need to do. Hopefully you are using a library that already has code to help you write up event handlers and such.
I had the same problem and have come across this post :
IE Resize Bug Revisited
The above code works but IE has a problem where the onresize is triggered when the body tag changes shape. This blog gives an alternate method which works well
How about use code like the following in your Content Page (C#)?
Page.ClientScript.RegisterStartupScript(this.GetType(), "resizeMyPage", "window.onresize=function(){ resizeMyPage();}", true);
Thus, you could have a resizeMyPage function defined somewhere in the Javascript and it would be run whenever the browser is resized!