I'm implementing a web application which will support different views according to different browsers. For example, In mobile browsers, it will show a smaller view to users with less UI elements. But we'd like to use same presenters.
I have a solution on hand - adding browser type detecting logic in ClientModule, e.g:
if (browser == "iphone") {
bindPresenter(HomePresenter.class, HomePresenter.MyView.class, HomeView.class, HomePresenter.MyProxy.class);
} else if (browser == "ipad") {
bindPresenter(HomePresenter.class, HomePresenter.MyView.class, IPadHomeView.class, HomePresenter.MyProxy.class);
} else {
bindPresenter(HomePresenter.class, HomePresenter.MyView.class, IPhoneHomeView.class, HomePresenter.MyProxy.class);
}
I'm wondering if it is possible to use some ways like deferred binding in GWT-platform. (but I'd like to follow GWT-plarform's structure rather than adding deferred binding code in xxx.gwt.xml).
So my questions are:
1) Are there any other ways to implement the feature mentioned above?
2) Which way is the best, and why?
Thanks in advance!
Best regards,
Jiakuan W
There is an example in the gwt samples folder that does something like you are wanting. I use a version of the sample code in my project -except using Gin to handle the clientfactory functionality. The sample is called mobilewebapp. It involves using a formfactor method in your .gwt.xml to determine which system you are on - in this case it breaks it down into desktop, mobile, and tablet. Then later in your gwt.xml it trades out client factories based on the form factor - I trade out gin models instead. Here is a link to the source for mobilwebapp
GWT does not allow you to set custom user agent types. You're limited to their set of gecko, gecko1_7, safari, IE6, IE7, IE8, and opera.
That being said, you can access the user agent directly and set your logic to switch accordingly with Window.Navigator.getUserAgent(), or via a property provider.
See this similar question on how to do mobile browser detection in GWT for MVP.
Check the gwtp google group, its a good source, and someone posted a pdf about his efforts regarding the sake problem in there.
Anyway, if I recall correctly, he holds multiple gin modules for each client with the presenters and views, runs custom js code on loading and than installs the correct module on the the ginClinet class.
Related
Adapting the MVVMCross framework in Xamarin crossplatform application development, we have PCL (containing Model and View Model) and View (for each platform) as in here.
a) Where does the Xamarin.mobile (for gaining single set of API access) reside? I think inside the PCL. But, i see different binaries for Xamarin.mobile (eg: Android and IOS), do we put all the Xamarin.mobile library inside the PCL? They all have the same name, won't there be any conflict?
b) Where do we keep codes like accessing bluetooth (not available in Xamarin.mobile)? Using MVVMCross decouples the view and business logic, so do all the codes for creating view items after an event has occured (btn click), reside in the view?
c) Where can we use the conditional compilation adapting MVVMCross? I guess in the Model, but is it only used for file access or can it also be used to show view items (toast message on Android) according to the target platform, by placing it on the PCL?
(Excuses if inappropriate, just gathered some information on MVVMCross and Xamarin.mobile and had some reasonings/confusions in mind)
Thank You!
Regards,
Saurav
a) Where does the Xamarin.mobile (for gaining single set of API access) reside? I think inside the PCL. But, i see different binaries for Xamarin.mobile (eg: Android and IOS), do we put all the Xamarin.mobile library inside the PCL? They all have the same name, won't there be any conflict?
Xamarin.Mobile is not portable code - it can't be called directly from PCLs.
For many Xamarin.Mobile functions (and many, many functions which Xamarin.Mobile does not cover) then MvvmCross provides Plugins - you can see some of that in https://www.nuget.org/packages?q=mvvmcross
For the remaining few methods that X.M has but we haven't already included - e.g. contacts lookup - then you can either:
access the Xamarin.Mobile functions by writing a portable interface (a facade) through which to access them
write a new plugin to implement them
For more on plugins:
see N=8 - adding the location plugin - from the N+1 videos in http://mvvmcross.wordpress.com/
for writing a plugin see https://speakerdeck.com/cirrious/plugins-in-mvvmcross
b) Where do we keep codes like accessing bluetooth (not available in Xamarin.mobile)?
Generally this is done the same way as above. For example, for Bluetooth take a look at the Sphero example:
http://blog.xamarin.com/xamarin-developer-showdown-winning-entries-showcase-xamarin-mobile/
https://github.com/slodge/BallControl/tree/master/Cirrious.Sphero.WorkBench/Plugins/Sphero
Using MVVMCross decouples the view and business logic, so do all the codes for creating view items after an event has occured (btn click), reside in the view?
Yes - if it's a 'view concern', then it belongs in the view (this is the same as any Mvvm code)
c) Where can we use the conditional compilation adapting MVVMCross?
I try not to use 'conditional compilation' including #if and partial classes. Sometimes I'll use it in plugin platform-specific modules, but generally I try to use inheritance or abstraction instead - the reason for this is because I use tools like 'refactoring' and 'unit tests' a lot and conditional compilation simply does not work with these.
For more on the benefits (and disadvantages) of using PCLs rather than file-linking and other project-based techniques, see What is the advantage of using portable class libraries instead of using "Add as Link"?
I have a single-page web app that presents a multi-step photo management "wizard", split up across several discrete steps (photo upload, styling, annotation, publishing) via a tab strip. On switching steps I set the URL hash to #publishing-step (or whichever step was activated).
How do I set up Optimizely tests to run on the various discrete steps of the wizard?
The browser never leaves the page, so it only gets a single window.load event. Its DOM isn't getting scrapped or regenerated, but just switches what page elements are visible at any one time via display: none or block, so the part I am trying to figure out is really mostly about in what way I go about the Optimizely test setup itself - it's fine (and likely necessary) if all edits get applied at once.
This thing unfortunately has to work in IE9, so I can't use history.pushState to get pretty discrete urls for each step.
There's actually several ways you could go about doing this, and which option you choose will largely depend on what's easiest for you AND how you plan to analyze the data.
If you want to use Optimizely's analytics dashboard:
I would recommend creating one experiment which will activate a bunch of other experiments at different times. The activation experiment will be targeted to everyone and run immediately when they get to your wizard. The other experiments will be set up with manual activation and triggered by this experiment.
The activation experiment would have code like:
window.optimizely = window.optimizely || [];
function hashChanged() {
if(location.hash === 'publishing-step') {
window.optimizely.push(['activate', 0000000000]);
}
if(location.hash === 'checkout-step') {
window.optimizely.push(['activate', 1111111111]);
}
}
window.addEventListener('hashchange', hashChanged, false);
Or you could call window.optimizely.push(['activate', xxxxxxxxx]); directly from your site's code instead of creating an activation experiment and listening for hashchange.
If you want to use a 3rd party analytics tool like Google Analytics:
You could do this all in one experiment with code similar to above, but in each "if" section instead of activating an experiment, you could run your variation code that makes changes to the wizard and sends special tracking information to your analytics sweet for later reporting. You'll have to do your own statistical significance calculation for this method (as Optimizely's data won't be "clean"), but this method actually works out better usually if properly configured.
Alternatively you could use the method outlined above but still try to use the Optimizely analytics dashboard by creating custom events on your experiment and sending data to them using calls like window.optimizely.push(["trackEvent", "eventName"]);
This article may also be helpful to you.
You'll probably need to do this yourself, using Optimizely's JS API to trigger actions on their end and tell it what your users did: https://www.optimizely.com/docs/api
How can I atatch an onclick event to an ImageElement in using ScriptSharp?
I'm getting an error when i'm trying this:
ImageElement Button = (ImageElement)Document.CreateElement("img");
Button.Src = "image.png";
Button.AddEventListener("click", delegate(ElementEvent e)
{
Script.Alert("Clicked");
}, false);
It works when I view the page in Google Chrome, but not in IE8...
So I guess that ScriptSharp cant create code to use with IE < 9?
This is a problem with how you are using the DOM. IE7 through 8 does not support addEventListener. You need to use attachEvent or a library that wraps the differences between the browsers (such as jQuery).
https://developer.mozilla.org/en/DOM/element.addEventListener
The github issue alsoo covers this - https://github.com/NikhilK/scriptsharp/issues/1
In short, I'd recommend either an existing wrapper, like jQuery, or your own custom one that abstracts the eventing differences and support for DOM events in different browsers, rather than conditionally calling attachEvent from within app code.
In general script#, as the compiler, doesn't do abstraction of differences across browsers - frameworks do that. This is folks either have their own abstractions, or framework choices they've already made, and often opinions about how the abstractions should work.
I've divided my GWT app into multiple modules, what's the best way to navigate between them?
Currently I'm using Window.Location.assign("foo.html#bar") but is there a better way?
History.newItem only works for history within the current module. To change to another page I think the best way is to use Window.Location.assign.
I don't fully remember the issue (and perhaps it has been fixed now), but in our application we stopped using relative URLs as they would sometimes break (we have a comment referencing http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/f79e7d5e002b48f6).
To this end we had a method that did the following:
public void goToRelativePage(final String relativeURL) {
Window.Location.assign(GWT.getHostPageBaseURL() + relativeURL);
}
I'm after some validation that I'm doing the right thing. I have my Ruby on Rails application in the following structure:
/home
about.rhtml
index.rhtml
/display
index.rhtml
/data <--This is called by jQuery from the display\index page to provide the data to render
push.js.erb
pull.js.erb
/layout
home.rhtml
display.rhtml
Everything is working fine, but I now want to add a site targeted for mobile devices. While the iPhone renders the website correctly, it would be nice to provide a more targeted experience. Ideally, I'm thinking about having an iPhone.domain.com which would be redirected to via .htaccess.
For this, I was thinking about adding another view for each device
/iPhone
home.rhtml
about.rhtml
display.rhtml
However, it feels like a lot of the data would be duplicated, for example the about page would be in two places. I guess I could have a partial and do something like render :partial => 'home/about' but that seems a little hacky.
How can I develop my site to support this?
I was thinking about a structure such as, but again not sure how to structure the code - how do I tell it to render the view in the iPhone directory... while not having the master layout applied
/display
/iphone
index.rhtml
I would really like some advice on the best way to approach this and structure the application. While the applications follow a structure at the moment, they could go off in different directions..
Thank you
Ben
I would strongly recommend leaving the controller structure the same across all device types. Particularly if you are using Rails' RESTful routes your controllers should be closely matched to the domain model of your data. Whether that data is then presented to a desktop browser, to an iPhone, to a different type of mobile device, to a JSON/XML REST API client etc. is mostly a matter of the presentation layer, not the controller/routing layer.
So an elegant solution would be:
Detect device type based on User Agent (you may want to refer to the WURFL User Agent database);
use Rails' respond_to mechanism to render a different view format for each device type;
define a layout for each device type (e.g. using the XHTML Mobile Profile doctype for mobile devices);
include different CSS files depending on device type.
There are some plugins which try to make this easier: have a look at brendanlim's Mobile Fu and noelrappin's Rails iUI (both on GitHub). Also Brendan Lim's presentation at Rails Underground has a few ideas.
What you should be aiming for is something like:
def show
#foo = Foo.find(params[:id])
respond_to do |format|
format.html # => show.html.erb
format.iphone # => show.iphone.erb
format.blackberry # => show.blackberry.erb
end
end
You should also allow users on mobile devices to override the user agent detection if they really want to see the desktop version of the site. A cookie with a long expiry time is probably the best way to do this, so that the site remembers the choice next time the user returns. Some mobile devices have rubbish cookie support, but then they probably won't want the desktop version of the site anyway because it probably won't work.
Rails 4.1 includes Variants, a great new feature that:
Allows you to have different templates and action responses for the same mime type (say, HTML). This is a magic bullet for any Rails app that's serving mobile clients. You can now have individual templates for the desktop, tablet, and phone views while sharing all the same controller logic.
In your case, you only need to set the variant for the iphone in a before_action, e.g:
class HomeController < ApplicationController
before_action :detect_iphone
def index
respond_to do |format|
format.html # /app/views/home/index.html.erb
format.html.phone # /app/views/home/index.html+phone.erb
end
end
private
def detect_iphone
request.variant = :iphone if request.user_agent =~ /iPhone/
end
end
What's new in Rails 4.1
The Iphone actually does a pretty good job of rendering web pages without any special formatting.
However on my Android phone floated content seems to get cut off and so a custom view for that phone is required.
To achieve this you need to create a different layout (e.g. mobile_application.html.erb) and in your application_controller add the following:
layout :select_layout
def select_layout
session.inspect # force session load
if session.has_key? "layout"
return (session["layout"] == "mobile") ? "mobile_application" : "application"
end
return detect_browser
end
def detect_browser
agent = request.headers["HTTP_USER_AGENT"].downcase
MOBILE_BROWSERS.each do |m|
return "mobile_application" if agent.match(m)
end
return "application"
end
where MOBILE_BROWSERS is a an array of user agent strings you want to match as a mobile device.
I wrote a blog about this here:
http://www.arctickiwi.com/blog/2-mobile-enable-your-ruby-on-rails-site-for-small-screens
Cheers
firstly, you should be using .html.erb as your template extension
secondly you can use logic to detect the type of layout to use based on the user agent (request.user_agent).
layout :site_layout
def site_layout
some_way_to_detect_the_layout_to_use
end
Note, the user_agent can be faked, but a majority of people wont bother faking it so the solution should be "good enough" for 99.9% of cases.