What kind of questions should be asked when choosing between straight MVC4 and a SPA framework like Durandal or Angular? - asp.net-mvc

I'm in the middle of making this choice right now and I don't know what kinds of questions I should be asking. One that I believe is valid:
Do I need SEO / natively crawlable pages? If so, stick with MVC4.
One question that I'm not sure about is the impact on performance - I think this is valid:
Is initial load time very important? If so, stick with MVC4 (like stackoverflow).
What are some other questions that should be asked that can help point a developer in the right direction here?
PS - if this question is being asked in a way that doesn't meet quality standards, I'd appreciate any help modifying it so that it does.

I have been asked and had to be a part of decision making groups recently that made this same decision. Here is what was important for us -
How many of the devs that will be working are familiar with MVC4 vs Javascript?
How much is performance an issue? (Is single page app really necessary?)
How big is the data we will be working with? (Remember that extremely large data sets don't work well in a spa)
Durandal requires using a lot of different libraries - is it ok to have to learn each of the different usages? Each library is important in its own regard and you must know when and why to use each library.
Angular is very set in its ways and harder for a new javascript dev coming from c# and .net to understand, are you willing to provide time for learning?
Last, which browsers are you targeting? Ie6+ works great with mvc4 and durandal, angular needs some massaging.
Hope this is helpful!

Why not combine the best of both worlds? SPA and straight MVC!
I was also investigating a lot of time in durandal, sammyjs, angular frameworks. I then decided to go with sammy.js for just the routing. This way I could still make use of the easy MVC 4 razor view engine to generate my views at server side. Even though it would be more performant to generate your html and bindings at client side by using knockout, I felt more secure by doing this at the server side.
But then of course you have to deal with those hashbangs? Therefor I started to investigate more time into the history.js (or HTML 5 history API). And then things got clear to me.
My solution
What is the essential part of a SPA? Well, in fact, that your layout.cshtml is only loaded once right. From then on you you only want to load content from the server and display it in the main content div. Does it need to be json? No, in fact it does not.
By default MVC 4 controllers returns an html string. So what if your < a href="">< /a> tags would be intercepted by a simple jQuery script to get the html string from the controller and load it into a div.
I went even further and wrote my own jQuery engine on top of the HTML 5 history api. I just intercept every link that is clicked and load the content from its href attribute and then place it into the desired div. Further I push the URL with history api pushSate. Another big advantage of this approach is that your application is not broken when javascript is disabled or when HTML 5 is not supported.
My views have the following layout page:
#{
Layout = Request.IsAjaxRequest() ? null : "~/Views/Shared/_Layout.cshtml";
}
This way when javascript is disabled or html 5 is not supported, the view will render inside the _Layout.cshtml.
This also allows for URL linking. The link will hit the address bar and will be routed to the controller. As this is not an ajax request, the view will render with _Layout.cshtml.
But once your _Layout.cshtml and javascript is loaded correctly and once, all < a href="">< /a> will be intercepted, loaded by AJAX (partial with layout = null) into the content div and the url is pushed on the address bar. So it seems that you are at that location, but in fact you are not. It's just an illusion to make things more responsive and efficient. Et voila, SPA in straight MVC.
The minimum routing code would be something like this
Interception of Links
$('.spalink').click(function () {
$.ajax({
url: this.href,
success: function (content) {
$('body>#content').css({ opacity: 0 });
$('body>#content').html(content);
$('body>#content').animate({ opacity: 1 }, 300, 'swing');
history.pushState({ state : 'spa' }, null, this.href);
}
});
return false;
});
BACK and FORWARD event
window.addEventListener("popstate", function (e) {
if (e.state != null) {
$.ajax({
url: location.href,
success: function(content) {
$('body>#content').css({ opacity: 0 });
$('body>#content').html(content);
$('body>#content').animate({ opacity: 1 }, 300, 'swing');
},
cache: false
});
return false;
}
});
return true;
}
PS: if you don't feel like writing your own SPA engine, take a look at history.js (it does the same out of the box)
Ajaxify on top of History.js on top of HTML 5 history API

Related

Angularjs Light version

I use angular in many projects, specially in ASP.NET MVC, but i dont like angular-
router (also ui-router, this is not much problem indeed as it's just a plugin) - concept of web-apps kills all ASP.NET features
services and factories - as all of this can be done inside common controller. (This might sound tricky)
constants and values - as i dont think this is really needed if you use requirejs or other AMD
What i love in angular is just directives, and when i make big nested directive, i use same controller foreach, so there is no $scope interacting (less watchers)
.controller('BaseGridCtrl', function(){
//instead of
//$scope.value = 123;
var ctrl = this;
//this is for Table-base-directive
ctrl.value = 123;
//this is for Table-head-directive
ctrl.value2 = 123;
});
.directive('table-base', function(){
return {
template: '<div>{{bgc.value}}</div>',
controller: 'BaseGridCtrl',
controllerAs: 'bgc'
}
});
.directive('table-head', function(){
return {
template: '<div>{{bgc.value2}}</div>',
controller: 'BaseGridCtrl',
controllerAs: 'bgc'
}
});
.directive('table-foot', function(){
return {
template: '<div>{{bgc.value3}}</div>',
controllerAs: 'bgc',
controller: function(){
var ctrl = this;
ctrl.value3 = 123;
}
}
});
So the link function is used veeery rare. And specially i like that angular easily detects directive - which is great as you just write 1 tag <grid> instead of reactjs components (This also might sound tricky). In my projects i use DotLiquid for razor views (it's stored in database as string) Sample:
<grid theme="some" route="localhost:9000/some/action"></grid>
So dotLiquid just renders this string w/o problem, or even applies other bingings beside angularjs. And this is great as all stuff is self contained. Which cannot be achieved by reactjs - you need to bootstrap components by yourself
React.renderComponent(<Grid />, document.querySelector('#someId'))
resume
Currently it's heavy, about 100kb, but without all this unnecessary stuff it would be really light. I would like to use only directive and controller services, Also with server-side rendering angular gonna bootstrap itself on each request which is not wise, but with lighter version it's a bit tradeoff.
Has anybody tried to strip angular services succesfully? Or is there any consideration, any ideas?
edit
Angular-light looks promising, but:
it doesn't provide HTML tag detection, only attributes
really ugly, alight.directives.al.getValue this looks so bad only for me or someone else?
edit2
Ok, that 100kb might not play alot. But consider it as workout, as you might know, angular start only once, at page load, so it must build the app, pull all modules, configure each, then all services of module, then inject them where they are needed, then supply callbacks from factories and services to any who injects them, at least it gonna check if any exists (on each module), all of this happens at start, only once! By wiping up size, we also can minimize javascript execution(indeed it will), factory and service watchers, so we won't need any parts that aren't used. We can even get rid of controllers (and use them inline), only 1 service. angular.directive (which is best part of party). Have looked at mustache, handlebars, but they are just like reactjs.
You can try Angular Light ~15kb (gzipped), it doesn't have services, factories, constants, values, DI, router and so on.
In addition, it has helpful features like text-directives and a support of Object.observe (benchmark)
edit
It provides HTML tag detection, attributes and comments.
AngularJS is meant for full SPA development. This means that you are not making full round trips to the server all the time. If your application needs to pass data back and forth, utilize the WEB API controllers and pass JSON up to them.
An Asset Management System I am currently working on is full SPA with WEB API and MVC fully wrapped into its needs. User log-in is processed across a full page request everything else is SPA. Additionally, here is something to read more about using MVC/WebAPI and Angular together.
If you are trying to use AngularJS instead of something like BackboneJS then you should be using AngularJS Light which was started for this exact purpose. I haven't used this library specifically, but as it is suppose to be an alternative to libraries I do use. The suggestion, if you want Angular and not BackboneJS or KnockoutJS use AngularJS Light

Ajax With JQuery MVC Form Posting

I'm a complete Ajax noob and I'm finding myself a little lost in how to best approach things, I've been looking over SO and found a post about Ajax that included this JavaScript:
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$('#result').html(result);
}
});
}
return false;
});
});
I incorporated this into my scripts file for a form on one of the pages in my site and quickly realised this actually attaches to ALL the forms, site wide including the login form.
I want to treat the login form as a special case and actually perform a redirect rather than simply insert the returned HTML fragment into an element on the page.
I'm presuming that replacing 'form' with the ID of the login form will differentiate the login form from 'general' form handling processes and was wondering what are the accepted best practices for this.
Do you have a 'general' Ajax hander like the one above or is it better to have specific JavaScript functions for each form depending on what they need to do with the response?
It sounds to me like you included something generic for a situation which should have been localized. In my opinion blanket approaches like this are not really desirable, especially not with something which is going to affect every form in your application.
The real meat of this to take away is the $.ajax code. Use ajax when you want, and use formal posting otherwise (which is default).
Using an exact reference will of course differentiate certain forms in your application, but this is something which should be done in a view's script, and not in one blanket script which is included application wide.
What I tend to do is use ajax when I want to provide a preview, or if I want to post without the user navigating away from the page.
Sometimes in rare occasions I will have a page which is replaced with a few sliding windows via ajax and then at the end of the series I will want to redirect. When that is the case, I will have my controller return a string which allows the view to redirect to that string in the success function of the ajax call.
I tend to keep mine separate, though others may do differently. They post to different URLs and do different things with the data.
I refer to the form by id:
$('#myformid")
You may wish to use the not equal selector if you just want to apply your function to all forms except your login form.
$('form[id!="loginform"]')...

Mobile website and desktop website : do I have to duplicate my code?

I have a website built with Symfony2 and I would like to create a mobile version of it using jQuery mobile (I'm currently using Twitter bootstrap for the desktop version).
I'm really confused about the solution I have to choose :
create a subdomain, for example : http://mobile.mywebsite.com and create another Symfony project which would be very similar but with just other html.twig files (to integrate jQuery mobile), but that means that I would duplicate a big part of my code ?
media queries inside my symfony project in html files to use jquery mobile elements according to screen size. Thus, I would have only one project to maintain.
Any advice would be great ! Thanks !
You can do routing based on the domain name:
http://symfony.com/doc/master/components/routing/hostname_pattern.html
With this you can have different controllers for the mobile site but still re-use your model and (at least some of) your view. You could even re-use some of the controllers by setting a parameter (based on the host) in the routing and then using that in the controllers:
# routing.yml
mobile_homepage:
path: /
host: m.{domain}
defaults: { _controller: AcmeDemoBundle:Main:homepage, mobileVersion: true }
requirements:
domain: %domain%
homepage:
path: /
defaults: { _controller: AcmeDemoBundle:Main:homepage }
Then in your controller:
// Acme/DemoBundle/Controller/MainController.php
public function homepageAction($mobileVersion = false) {
...
if ($mobileVersion) {
// do some mobile things, perhaps return a specific template
} else {
// do some non-mobile things, perhaps return a specific template
}
...
}
Best practice is to create one page with a responsive design (media queries are part of that) that can adapt to any screen size.
You definitely should not be making a second Symfony project. You are essentially trying to make 2 different layouts for your site, so they should only affect your view. If you don't want to use a responsive design, you can create twig templates for your mobile pages and load those from the same controllers you use for the desktop site.
I know it's a bit late but it still may be usefull for others.
I had the same question this morning and Google gave me MobileDetectBundle https://github.com/suncat2000/MobileDetectBundle/ which seems to solve that kind of problem. Not tryed yet but I will for sure !

Refresh the browser once on load or clear DOM

I have a dynamic MVC4, jQuery Mobile application that works for the most part quite well. I have an auto posting dropdown list that selects a list from the database via the following code.
<script type="text/javascript">
$(function () {
$("#TownID").live('change', function () {
//$("#TownID").change(function () {
var actionUrl = $('#TheForm1').attr('action') + '/' + $('#TownID').val();
$('#TheForm1').attr('action', actionUrl);
$('#TheForm1').submit();
});
});
</script>
<p>
#using (Html.BeginForm("SearchTown", "Home", FormMethod.Post, new { id = "TheForm1" }))
{
#Html.DropDownList("TownID", (SelectList)ViewBag.TownId, "Select a Town")
}
</p>
The problem is it only works properly the first time a search is performed unless I click refresh. I don’t think this has anything to do with MVC, I think the problem is with AJAX and jQuery Mobile.
Edit:
The first time I search www.mysite.com/Home/Search/2 yields a result and woks fine, but the second time something seems to be left behind in the DOM??? and it looks for:
www.mysite.com/Home/Search/2/2 also
I get 404 errors in my log and “Error Loading Page” but it still finds the results and displays the page correctly!
Then with a third search I get the error 404’s in my log and “Error Loading Page” but it has grown and now looks for:
www.mysite.com/Home/Search/2/2
www.mysite.com/Home/Search/2/2/2 also
This then continues to grow after every search until at some seemingly random point on each test, it seems to give up and I get error 505
Additional Edit:
The code works perfectly if I take jQuery Mobile out of the question
Can anyone tell me what might be going on here?
Get rid of: $(function () {
And replace it with: $(document).delegate('[data-role="page"]', 'pageinit', function () {
Please read the big yellow sections at the top of this page: http://jquerymobile.com/demos/1.1.0/docs/api/events.html
You can't rely on document.ready or any other event that only fires once per page. Instead you have to get used to using jQuery Mobile's custom page events like pageinit so your code will work no-matter when the page is added to the DOM (which you don't know when this will happen in a jQuery Mobile website). There are a ton of events, so again, please read the documentation I linked-to above.
Firstly, dynamically generated html using a server side templating engine blows. I really don't understand what value people see in it.
My guess is that it used to make sense 10 years ago before AJAX became popular, and has just hung in there ever since because people have this feeling that it is "the right way to do it". It isn't. ESPECIALLY for mobile web apps.
Secondly, it looks like you are trying to do pretty simple search. All this MVC4 garbage makes it difficult for you to see what is really happening though. You don't need to append parameters to your URL for a simple form submission like this. In fact your TownId should already be part of the POST data when you submit, so you can just remove the URL modification bit.
Alternatively, don't use a form submission, but just a GET and AJAX. I don't know what your app is doing here, but I imagine you want to display the results on the page dynamically somehow, so a GET is more than enough.
Use your developer browser tools (F12) to see what exactly is getting submitted when you do the submit - it really helps. And for your next project, abandon MVC4! "Well established design patterns" my foot.
I have been bothered by this problem for a long time
There are same select element in the DOM I think so...
and I used $('.SelectCSS:last').val()
It seen work well.
I come from China , English is poor...
I guess this is one for the future, MVC and jQuery Mobile don't seem to blend completely right now. Maybe MS's response to the issue is Single Page Applications!
SPA may satisfy Danial also?

ASP.NET MVC editor template javascript location

We have an editor template that contains approx 40 lines of jquery. I tried dropping this script into a <asp:Content> block to keep all of the javascript in one location within the page. However, I get the following error message content controls have to be top-level controls in a content page.
Is there any way to get this working so we don't have script dotted around our final output pages or could someone recommend the best practice for storing javascript used within ASP.NET MVC templates? At the moment I'm thinking of pulling the code into a separate file and referencing it within the master page but this means it gets pulled into every page which isn't really ideal.
Thanks in advance.
It would be easier for later maintenance, if you keep the javascript into a separate file and reference it where ever it is needed. Also, if you feel that placing all script into a single file will increase unnecessary loading of scripts, where not needed, then break the scripts into separate files based on functionality/modules in which it is useful etc. strategy and then reference them instead.
Also, it was said that always, keep/reference the scripts at the bottom of the page so that page loading will be faster.
as siva says, bottom of the page is the 'ideal'. however, as for seperate files. this is only going to be practical as long as you don't reference asp.net elements from the page - ie:
<asp:Content ContentPlaceHolderID="jsCode" ID="jsCode1" runat="server">
<script type="text/javascript">
$(document).ready(function() {
getPoundsData();
});
function getPoundsData() {
var id = $("#ID").val();
var URL = '<%=Url.Action("GetPounds", "FundShareholder")%>';
var params = { id: id };
if (id != null)
SendAjaxCache("#" + $("#ShareholderID option:selected").text() + " RSP#", URL, params, null, ListDataShareholderPounds);
}
function ListDataShareholderPounds(data) {
if (data.length != 0) {
$('#shareholderPounds').html("");
$('#shareholderPounds').show();
$('#shareholderPounds').html(data);
}
};
</script>
</asp:Content>
notice the:
var URL = '<%=Url.Action("GetPounds", "FundShareholder")%>';
part in the js. what 'we' do is to add a content section to the master page at the very bottom to hold our js stuff. however, this only works inside the ViewPage (aspx) object. the ascx pages are 'ignorant' of any master page content sections.
We are currently working on systemizing the process whereby we save 'partial' js files with asp.net references inside them and then inject them into the page-flow via a filterattribute. we're at an early stage with this but the nice thing about this approach is that the partial js is treated as a file and is therefore cached for future visits to that page.
anyway, that's our current approach, would be interested to discover if peeps are using any similar mechanisms to inject js that contains asp.net object references.
cheers...
[edit] - here's a heads up on the approach i'm talking about (this wasn't our original inspiration, but is quite similar, tho is webforms, rather than mvc) - http://www.west-wind.com/WebLog/posts/252178.aspx or this one which IS mvc: http://poundingcode.blogspot.com/2009/12/injecting-javasript-into-aspnetmvc-with.html.
Finally found the article that inspired our 'search' in this: ASP.NET MVC routing and paths is js files plus http://codepaste.net/p2s3po

Resources