ASP.NET MVC 4 Mobile Display Modes Stop Working - asp.net-mvc

Mobile display modes in ASP.NET MVC 4 stop serving the correct views after about an hour of uptime, despite browser overrides correctly detecting an overridden mobile device.
Recycling the application pool temporarily solves the problem.
The new browser override feature correctly allows mobile devices to view the desktop version of a site, and vice-versa. But after about an hour of uptime, the mobile views are no longer rendered for a mobile device; only the default desktop Razor templates are rendered. The only fix is to recycle the application pool.
Strangely, the browser override cookie continues to function. A master _Layout.cshtml template correctly shows "mobile" or "desktop" text depending on the value of ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice, but the wrong views are still being rendered. This leads me to believe the problem lies with the DisplayModes.
The action in question is not being cached:
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
I am using 51Degrees for mobile detection, but I don't think this should affect the overridden mobile detection. Is this a bug in DisplayModes feature for ASP.NET MVC 4 Beta & Developer Preview, or am I doing something else wrong?
Here is my DisplayModes setup in Application_Start:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
ContextCondition = context =>
context.GetOverriddenBrowser().IsMobileDevice
&& (context.Request.UserAgent.IndexOf("iPhone", StringComparison.OrdinalIgnoreCase) >= 0
|| context.Request.UserAgent.IndexOf("Android", StringComparison.OrdinalIgnoreCase) >= 0
|| !context.Request.Browser.IsMobileDevice)
});
/* Looks complicated, but renders Home.iPhone.cshtml if the overriding browser is
mobile or if the "real" browser is on an iPhone or Android. This falls through
to the next instance Home.Mobile.cshtml for more basic phones like BlackBerry.
*/
DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("Mobile")
{
ContextCondition = context =>
context.GetOverriddenBrowser().IsMobileDevice
});

This is a known issue in MVC 4 (Codeplex: #280: Multiple DisplayModes - Caching error, will show wrong View). This will be fixed in the next version of MVC.
In the meantime you can install a workaround package available here: http://nuget.org/packages/Microsoft.AspNet.Mvc.FixedDisplayModes.
For most applications simply installing this package should resolve the issue.
For some applications that customize the collection of registered view engines, you should make sure that you reference Microsoft.Web.Mvc.FixedRazorViewEngine or Microsoft.Web.Mvc.FixedWebFormViewEngine, instead of the default view engine implementations.

I had a similar issue and it turned out to be a bug when mixing webforms based desktop views with razor based mobile views.
See http://aspnetwebstack.codeplex.com/workitem/276 for more info

Possibly a bug in ASP.NET MVC 4 related to caching of views, see:
http://forums.asp.net/p/1824033/5066368.aspx/1?Re+MVC+4+RC+Mobile+View+Cache+bug+

I can't speak for this particular stack (I'm still in MVC2) but check your output caching setup (either in your controllers or views - and in your web.config in your app and at the machine level). I've seen it work initially for the first few users and then a desktop browser comes in right around the time ASP decides to cache, then everyone gets the same view. We've avoided output caching as a result, hoping this would get addressed later.

If you want all mobile devices to use the same mobile layout you can use
DisplayModeProvider.Instance.Modes.Insert(1, new DefaultDisplayMode("Mobile")
{
ContextCondition = context =>
context.GetOverriddenBrowser().IsMobileDevice
});
And of course you need to make a view in the shared layout folder named _Layout.Mobile.cshtml
If you want to have a separate layout for each type of device or browser you need to do this;
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Android")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("Android", StringComparison.OrdinalIgnoreCase) >= 0)
});
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("iPhone", StringComparison.OrdinalIgnoreCase) >= 0)
});
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Mobile")
{
ContextCondition = (context => context.GetOverriddenUserAgent().IndexOf
("IEMobile", StringComparison.OrdinalIgnoreCase) >= 0)
});
And of course you need to make a view in the shared layout folder for each named
_Layout.Android.cshtml
_Layout.iPhone.cshtml
_Layout.Mobile.cshtml

Can you not just do this?
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Code removed for clarity.
// Cache never expires. You must restart application pool
// when you add/delete a view. A non-expiring cache can lead to
// heavy server memory load.
ViewEngines.Engines.OfType<RazorViewEngine>().First().ViewLocationCache =
new DefaultViewLocationCache(Cache.NoSlidingExpiration);
// Add or Replace RazorViewEngine with WebFormViewEngine
// if you are using the Web Forms View Engine.
}

So guys here is the answer to all of your worries..... :)
To avoid the problem, you can instruct ASP.NET to vary the cache entry according to whether the visitor is using a mobile device. Add a VaryByCustom parameter to your page’s OutputCache declaration as follows:
<%# OutputCache VaryByParam="*" Duration="60" VaryByCustom="isMobileDevice" %>
Next, define isMobileDevice as a custom cache parameter by adding the following method override to your Global.asax.cs file:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (string.Equals(custom, "isMobileDevice", StringComparison.OrdinalIgnoreCase))
return context.Request.Browser.IsMobileDevice.ToString();
return base.GetVaryByCustomString(context, custom);
}
This will ensure that mobile visitors to the page don’t receive output previously put into the cache by a desktop visitor.
please see this white paper published by microsoft. :)
http://www.asp.net/whitepapers/add-mobile-pages-to-your-aspnet-web-forms-mvc-application
Thanks and Keep coding.....

Related

Render view from DB in MVC 6

We are working on ASP.NET MVC 6 project and it's necessary to render Razor views from other than file system source (Azure Blob storage in particular but it's not important).
Earlier (in MVC 5) it was possible to create and register custom VirtualPathProvider which can take view content from DB or resource DLLs (for example).
It seems that approach has been changed in MVC 6. Does anybody know where to look for?
UPD:
Here is an example of code I'm looking for:
public IActionResult Index()
{
ViewBag.Test = "Hello world!!!";
string htmlContent = "<html><head><title>Test page</title><body>#ViewBag.Test</body></html>";
return GetViewFromString(htmlContent);
}
The question is: how to implement that GetViewFromString function?
You need to configure a ViewLocationExpander:
services.SetupOptions<RazorViewEngineOptions>(options =>
{
var expander = new LanguageViewLocationExpander(
context => context.HttpContext.Request.Query["language"]);
options.ViewLocationExpanders.Insert(0, expander);
});
and here is the implementation for the LanguageViewLocationExpander :
https://github.com/aspnet/Mvc/blob/ad8ab4b8fdb27494f5dece6f1186acea03f9dd52/test/WebSites/RazorWebSite/Services/LanguageViewLocationExpander.cs
Basing your AzureBlobLocationExpander on that one should put you in the right track.
Just posted a sample of store .cshtml in Azure Blob Storage to GitHub.
See also my answer to another question on this
Basically you need to create an implementation of IFileProvider. This can then be registered in Startup.cs by configuring RazorViewEngineOptions
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProvider = new AzureFileProvider(Configuration);
});

Vaadin : How to get Element by ID?

How to get HTML Elmement (or DOM) in Vaadin ?In GWT I can use as DOM.getElementById("myId");
I can set id attribute on my Vaadin components by setId() method. For example:
Button button = new Button("Say Hello");
button.setId("myButton");
So, how can I retrieve this DOM Element in Vaadin ?
You can use this:
public static Component findComponentById(HasComponents root, String id) {
for (Component child : root) {
if (id.equals(child.getId())) {
return child; // found it!
} else if (child instanceof HasComponents) { // recursively go through all children that themselves have children
Component result = findComponentById((HasComponents) child, id);
if (result != null) {
return result;
}
}
}
return null; // none was found
}
Source: https://vaadin.com/forum/#!/thread/3199995/3199994
Vaadin 10 (Vaadin Flow)
The new Vaadin Flow generation replaces the internal use of GWT for Web Components.
This new architecture provides us with easy direct access to the DOM from the Java-based server-side, if you so desire. You can read the DOM, and you can manipulate elements in the DOM. Read about the new Element API in the manual.
Vaadin 6, 7, & 8 (Vaadin Framework)
This Answer expands on the comment by Vaadin expert, Henri Kerola.
Vaadin is a server-side app framework. It's purpose is to shield the app developer from the details of HTML, CSS, JavaScript, DOM, GWT, HTTP, WebSocket, and such web technologies. The app developer writes in pure Java (and maybe a tiny touch of CSS for tweaking). Vaadin transparently and auto-magically generates the HTML-CSS-JavaScript-GWT-DOM necessary to render a representation of the app’s user-interface within a web browser.
So there is no way to access the DOM from that Java server-side, nor any need to do so generally.
If you want to take control of the web technologies then Vaadin is probably not the best framework for you.
In Vaadin8 you may try this:
JavaScript.getCurrent().execute("document.getElementById('refreshButton').click()");

Set MVC for IPad devises to use regular web version, not .mobile

I have made a mobile version of my mvc site (.mobile views) and it's works ok with mobile, but when emulating ipad it's also using .mobile version.
How can i tell mvc that it should pick regular version of the web-page for ipad devised and other non mobile devises?
We fixed this by adding the following in the global Application_Start method.
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode()
{
ContextCondition = (context => context.Request.UserAgent != null && context.GetOverriddenUserAgent().IndexOf("iPad", StringComparison.OrdinalIgnoreCase) >= 0)
});
This tells MVC to use the default views rather than mobile views for iPad devices.
Actually we have a list of 'excluded devices' that are listed in the configuration file.
// For each excluded device, set to use the default (desktop) view
foreach (var excludedMobileDevice in ExcludedDevices)
{
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode()
{
ContextCondition = (context => context.Request.UserAgent != null && context.GetOverriddenUserAgent().IndexOf(excludedMobileDevice, StringComparison.OrdinalIgnoreCase) >= 0)
);
}
Also in our application, we have a requirement to disable all of the mobile views through a single configuration setting (without having to remove all of the mobile view files). This enables us to turn the mobile views on and off as required. Again in Application_Start we find and remove the display mode with the Id "Mobile".
// Remove the built in MVC mobile view detection if required
if (!MobileViewEnabled)
{
var mobileDisplayModeProvider = DisplayModeProvider.Instance.Modes.FirstOrDefault(d => d.DisplayModeId == "Mobile");
if (mobileDisplayModeProvider != null)
{
DisplayModeProvider.Instance.Modes.Remove(mobileDisplayModeProvider);
}
}
This sounds like a known bug with MVC4
There is a fix - upgrade to MVC5 or install the following NuGet package:
Install-Package Microsoft.AspNet.Mvc.FixedDisplayModes
Further reading about the NuGet Package.
http://www.nuget.org/packages/Microsoft.AspNet.Mvc.FixedDisplayModes
Further reading about the bug: http://forums.asp.net/t/1840341.aspx, http://aspnetwebstack.codeplex.com/workitem/280

ie9: annoying pops-up while debugging: "Error: '__flash__removeCallback' is undefined"

I am working on a asp.net mvc site that uses facebook social widgets. Whenever I launch the debugger (ie9 is the browser) I get many error popups with: Error: '__flash__removeCallback' is undefined.
To verify that my code was not responsible I just created a brand new asp.net mvc site and hit F5.
If you navigate to this url: http://developers.facebook.com/docs/guides/web/#plugins you will see the pop-ups appearing.
When using other browsers the pop-up does not appear.
I had been using the latest ie9 beta before updating to ie9 RTM yesterday and had not run into this issue.
As you can imagine it is extremely annoying...
How can I stop those popups?
Can someone else reproduce this?
Thank you!
I can't seem to solve this either, but I can at least hide it for my users:
$('#video iframe').attr('src', '').hide();
try {
$('#video').remove();
} catch(ex) {}
The first line prevents the issue from screwing up the page; the second eats the error when jquery removes it from the DOM explicitly. In my case I was replacing the HTML of a container several parents above this tag and exposing this exception to the user until this fix.
I'm answering this as this drove me up the wall today.
It's caused by flash, usually when you haven't put a unique id on your embed object so it selects the wrong element.
The quickest (and best) way to solve this is to just:
add a UNIQUE id to your embed/object
Now this doesn't always seem to solve it, I had one site where it just would not go away no matter what elements I set the id on (I suspect it was the video player I was asked to use by the client).
This javascript code (using jQuery's on document load, replace with your favourite alternative) will get rid of it. Now this obviously won't remove the callback on certain elements. They must want to remove it for a reason, perhaps it will lead to a gradual memory leak on your site in javascript, but it's probably trivial.
this is a secondary (and non-optimal) solution
$(function () {
setTimeout(function () {
if (typeof __flash__removeCallback != "undefined") {
__flash__removeCallback = __flash__removeCallback__replace;
} else {
setTimeout(arguments.callee, 50);
}
}, 50);
});
function __flash__removeCallback__replace(instance, name) {
if(instance != null)
instance[name] = null;
}
I got the solution.
try {
ytplayer.getIframe().src='';
} catch(ex) {
}
It's been over a months since I last needed to debug the project.
Facebook has now fixed this issue. The annoying pop-up no longer shows up.
I have not changed anything.

SharePoint Publishing HTML Field Control Converts Relative URL to Absolute URL

So, after much research on whether or not we should the CEWP or the HTML Field Control on an external facing SharePoint site, we settled on using the Field Control (much thanks to AC). Now, we are having an issue that all the blogs I read say should not be an issue.
When we put a relative URL into the HTML Editor and hit OK, it is automatically changed to an absolute URL. This is apparently a "feature" of Internet Explorer from some of the research I have been doing. TinyMCE has a work around for this. I was wondering if there was some work around for the SharePoint control that I am missing.
This is kind of a big issue for us because we have an authoring site and the www site. So, when the authoring is done on the authoring site and all the links get migrated to the www site, they are http:// authoring.domain.com/en-us/Pages/... instead of /en-us/Pages/...
I encountered this issue as well. We had custom site fields and content types deployed via feature. The RichText property of the HTML Field is properly as true in caml, but once deployed the SPField in the root web fields collection and every Pages list the RichText attribute becomes false.
I was able to successfully resolve the issue by using a feature receiver on the feature that deploys the site columns and content types. My code loops every web in the site and then iterates over the fields to update them.
code snippet:
private void processweb(SPWeb web)
{
SPList list = web.Lists["Pages"];
SPField field;
for (int i = 0; i < list.Fields.Count; i++)
{
field = list.Fields[i];
//to work around a sharepoint defect ... make html fields work in richtext mode
if (field != null && string.Compare(field.TypeAsString, "HTML", true) == 0 && (field as SPFieldMultiLineText).RichText == false)
{
(field as SPFieldMultiLineText).RichText = true;
field.Update(true);
}
}
foreach (SPWeb w in web.Webs)
{
processweb(w);
}
}

Resources