single page applications on javascript and asp.net mvc4
DESCRIPTION
Single page applications on JavaScript and asp.net mvc4 from Minsk SEC 2013 conference (Jan 2013)TRANSCRIPT
SINGLE PAGE APPLICATIONS WITH JAVASCRIPT AND ASP.NET MVC4
Author: Yurii [email protected]
TOPICS
Definition
SPA Architecture
Server Stack
Client Stack
JavaScript Patterns
Web Optimization
Examples of components
WHAT’S A SPA?
• is a web application that fits on a single web page, providing a fluid UX akin to a desktop application.
• Maintain navigation, history, page linking.
• Whole applications fits on a single web page.
• Persisting important state on the client
• Fully (or mostly) loaded in the initial page load
• Progressively downloads data as required
SPA HIGH LEVEL ARCHITECTURE
Server Client
Database
Data Services
ASP.NET + (JavaScript /
HTML / CSS)
Application(JavaScript)
Views (HTML / CSS)
Data Access(JavaScript)
Navigation
SERVER SIDE DESIGN
Database
Repository Pattern
Unit of Work Pattern
Entity Framework
Web API JSON
EF Code First
ASP.NET Web API
ASP.NET WEB API
• Simplifies web services
•Good for serving JSON
SQL Server Data LayerASP.NET Web
API
Models
JSON
CONTROLLERSpublic IEnumerable<Activity> GetAll() { return Uow.Activities.GetAll().OrderBy(p => p.Name);}
public Activity Get(int id) { var activity = Uow.Activities.GetById(id); if(activity != null) return activity;}
public HttpResponseMessage Put(Activity activity) { Uow.Activities.Update(activity); Uow.Commit(); return new HttpResponseMessage(HttpStatusCode.NoContent);}}
INDEX.HTML
•Using HTML5 Boilerplate• (http://html5boilerplate.com/)• Modernizr.js – checks supported features in client browser.
•@using System.Web.Optimization
• Configuring App_Start• BundleConfig
BUNDLING AND MINIFICATION
• To reduce size of resources which we transfer to client in initial page load, we are using bundling and minification.
• Bundling reduce server calls for resources.
• Minification reduce the size of file.
BUNDLING
• Instead of this:
<script type="text/javascript" src="jquery.easing.js"></script>
<script type="text/javascript" src="jquery-ui.js"></script>
<script type="text/javascript" src="inputlabel.js"></script>
<script type="text/javascript" src="jquery.treeview.js"></script>
<script type="text/javascript “src="quickpager.jquery.js"></script>
<script type="text/javascript" src="jquery.modalboxmin.js"></script>
<script type="text/javascript" src="jquery.activity-indi.js"></script>
<script type="text/javascript" src="/lib/js/tabs.js"></script>
<script type="text/javascript" src="/lib/js/socializ.js"></script>
<script type="text/javascript" src="/lib/js/muScript.js"></script>
<!-- ... -->
BUNDLING
… doing this for scripts:
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jsextlibs").Include(
"~/Scripts/lib/jquery.activity-indicator-{version}.js",
"~/Scripts/lib/knockout-{version}.js",
"~/Scripts/lib/underscore.js",
"~/Scripts/lib/moment.js",
"~/Scripts/lib/sammy.js",
"~/Scripts/lib/amplify.js"
// ...
));
BUNDLING
… and for other resources:bundles.Add(new ScriptBundle("~/bundles/jsapplibs").
IncludeDirectory("~/Scripts/app/", "*.js",
searchSubdirectories: false));
bundles.Add(new StyleBundle("~/Content/css").
Include(
"~/Content/normalize.css",
"~/Content/main.css", ));
bundles.Add(new Bundle("~/Content/Less",
new LessTransform(), new CssMinify())
.Include("~/Content/styles.less"));
BUNDLING
Add bundles on [email protected]("~/Content/css", "~/Content/less")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render(
"~/bundles/jquery",
"~/bundles/jsextlibs",
"~/Scripts/lib/require.js",
"~/bundles/jsapplibs",
"~/bundles/jsmocks",
"~/Scripts/main.js"
)
BUNDLING AND MINIFICATION
Using B/M Without B/MChange
File Requests
9 34 256%
KB Sent 3.26 11.92 266%
KB Received
388.51 530 36%
Load Time
510 MS 780 MS 53%
CSS AND LESS
Less – Dynamic Stylesheet language
.accent-top (@sizeMultiplier: 1, @lineHeightMultiplier: 1) {
.accent;
width: @base-accent-top-width * @sizeMultiplier;
height: @base-accent-top-height * @sizeMultiplier;
div {
text-transform: uppercase;
}}
LESS TO CSS
Creating custom BundleTransform using library dotLett.
public class LessTransform : IBundleTransform
{
public void Process(BundleContext c, BundleResponse resp)
{
resp.Content = dotless.Core.Less.Parse(resp.Content);
response.ContentType = "text/css";
}}
RENDER VIEWS
• Render all pages in main section.• All pages are stored in Views folder• General components like header, navigation and footer we are defining above or below of main section.
<section class="main">
@RenderPage("Views/workspace.cshtml")
@RenderPage("Views/statistics.cshtml")
@RenderPage("Views/settings.cshtml")
@* ... *@
</section>
VIEWS
• View is a simple html with data-bind.• Views might contain Knockout templates
<section id="workspace-view" class="view">
<h2 data-bind="text: context.date"></h2>
<ul data-bind="template: { name: 'activity-template',
foreach: activities }" class="activity"></ul>
<button data-bind="click: addActivity" class="add- activity">+</button>
<table data-bind="template: { name: timelogTemplate }">
</table>
</section>
CLIENT SIDE DESIGN
BootstrapperHTML ViewsHTML ViewsHTML Views
HTML / ViewsMVVM Binder Data Primer
Data Context
Data Services
Model MappersModel MappersModel MappersModel Mappers
SortingHTML / ViewsHTML / ViewsHTML / Views
ViewModels
HTML / ViewsHTML / ViewsHTML / ViewsModelsFiltering
JAVASCRIPT LIBRARIES
jQuery Working with DOM,
Knockout.js Data Binding and MVVM
Amplify.js Data Push/Pull, Caching, Client Storage
Breeze.js Querying with filters, ordering and paging
Sammy.js Navigation and History
require.js Dependency Resolution
underscore.js JavaScript helper
toastr.js UI Alerts
qunit Unit Testing
JAVASCRIPT PATTERNS
• AMD (Asynchronous Module Definition)• is a JavaScript API for defining modules and their dependencies in such a way that modules may be asynchronously loaded
• Revealing Module Pattern• we define all of our functions and variables in the private scope and return an anonymous object with pointers to the private functionality we wished to reveal as public.
• MVVM
AMD PATTERN
Application
Data Context
Controller
Data Services Model
Navigation
• Sequence of loading component might cause an issue.
DEFINING A MODULE IN REQUIRE.JS
define('model',
[‘settings', 'model.workspace'],
function (settings, workspace) {
var
model = {
Workspace: workspace
}; // ...
return model;
});
Module ID
Dependencies
Module Factory
STARTING THE SPA
Prime the Data
Models
Data Services
Data Context
Setup the presentation
Navigation Router
HTML Views
View Models
Bootstrapper
BOOTSTRAPPER
Bootstrapper responsible for initial application run.define('bootstrapper',['jquery', 'route-config', 'presenter', 'dataprimer', 'binder'], function ($, routeConfig, presenter, dataprimer, binder) { var run = function () { presenter.toggleActivity(true); $.when(dataprimer.fetch()) .done(binder.bind) .done(routeConfig.register) .always(function () { presenter.toggleActivity(false); }); } return { run : run }});
var run = function () {
presenter.toggleActivity(true);
$.when(dataprimer.fetch())
.done(binder.bind)
.done(routeConfig.register)
.always(function () {
presenter.toggleActivity(false);
}}
DATASERVICE var init = function () { amplify.request.define('activities', 'ajax', { url: '/api/activities', dataType: 'json', type: 'GET' }); },
getActivities = function (callbacks) { return amplify.request({ resourceId: 'activities', data: '', success: callbacks.success, error: callbacks.error });}; init();
return { getActivities: getActivities };
VIEWMODEL activityItemTemplate = 'common.activityitem', timelogTemplate = 'common.timelogtable'; activities = ko.observableArray();
activate = function (routerData, callback) { refresh(callback); } getWorkspace = function (callback) { datacontext.activities.getActivities(id); } return {
activate: activate activityItemTemplate: activityItemTemplate, timelogTemplate: timelogTemplate }});
AS RESULT WE HAVE
• Async services• Client side application & business logic• Long-lived client-side state• 2 way data binding• Tombstoning / dropped connection
PROS AND CONS
• Faster UI• More Interactive• Can work offline• UI is just another
Client• UI can have BI• Perfect for
HTML5 and Mobile apps
• Bad for SEO
• More complex to build
• Need to expose application logic to client
• Require strong JavaScript skills