ict.ken.be

 

Posts in Category: Javascript

Introduction to TypeScript - Notes 

Categories: Javascript Notes
Introduction to TypeScript
Anders Hejlsberg
There is a lot of fuss about 'the language' TypeScript. I personally think it is because most people did not have a clear look at it for themselves and already decided that just because it is from Microsoft it must not be good. Anyway, since I strongly believe that more and more enterprises are going to use javascript. Especially for mobile applications and response designs with html5 and websockets, it will lead to enormous code bases. Dynamic languages are great, but I do believe in typing models where it will benefit long run maintenance. Since TypeScript does exactly this (it is not a language but a tool for writing more robust javascript code) and for early adopters it will allow them to simple use the generated javascript files in case the tool is discontinued by Microsoft. I also think it makes more sense to use classes and interfaces and other structures like namespaces and modules, before compilation. This way the client browser is not bothered with handling those things (like would be the case when using for example backbone.js). There are other tools like Coffeescript, Dart, ... but I think TypeScript will allow plenty of people to transit from ASP.Net with c# development to more SPA development with structured javascript. Give it a try, at least until ECMAScript 6 will be supported by all major browsers :)
 
 
  • Javascript is missing Namespaces, Classes, Modules, Interfaces
  • TypeScript is a language for application scale JavaScript development.
  • Compiles to plain javascript.
  • Allows renaming/refactoring by type over your whole code base.
  • Intellisense and find reference.
  • Google closure is more like a type system in comments, while TypeScript incorporates anotations in the syntax.
  • TypeScript is open source written in TypeScript.
 
function process(x: string) {}
function process(x: number) {}
function process(x: bool) {}
function process(x: string[]) {}
function process(x: () => string) {} //x is a function that returns a string
function process(x: { a:number; b:string; }) {} 
 
interface IThing { 
a:number; 
b:string; 
c?:bool; //c is an optional property
 
foo(s: string):string;
foo(s: number):number; //overloading is possible, but you need to check in your one foo to return the correct model
//or so it has two overloads and a data property
foo {
(s: string):string;
(s: number):number;
data:any; 
}
 
new (s: string): Element; //constructor
 
[index: number]: Date;
function process(x: IThing) {} 
 
function Accumulator() : IAccumulator {}
  • The 'lib.d.ts' file contains the definitions for all classical javascript functions. (so you can use F12)
  • You can write or import these delaration files for other javascript frameworks like node.js, jquery, ...
class Point {
x: number;
y: number;
private color: string;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
//or shortcut, so you do not have to declare and assign the properties
constructor(public x: number = 0, public y: number) {} 
 
//Point.prototype.dist = function () {};
dist() { return Math.sqrt(this.x * this.x + this.y * this.y); }
//Object.defineProperty(Point.prototype, "dist", { get ... });
get dist() { return Math.sqrt(this.x * this.x + this.y * this.y); }
 
static origin = new Point(0,0);
}
var p = new Point(); p.x = 10; p.y = 20;
var p = new Point(10,20);
 
//inheritance
class Point3D extends Point {
constructor(x: number, y: number, z:number) {
super(x, y); //calls the base class
}
dist() {
var d = super.dist();
return Math.sqrt(d * d + this.z * this.z);
}
}
 
//auto self-this reference, by using arrow/lambda functions
class Tracker {
count = 0;
start() {
window.onmousemove = e => {
console.log(this.count);
}
}
}
 
//internal modules (open ended, can be used as namespaces}
module MyCore.Utils {
export class Tracker { ... }
}
var t = new MyCore.Utils.Tracker(); t.start();
 
import mu = MyCore.Utils;
var t = new mu.Tracker();
 
//external modules (commonjs and emd)
 
///<reference path="node.d.ts"/>
import http = module("http");
 
export function simpleServer(port: number, message: string) {
http.createServer((req,res) => {
res.writeHead(200, { "Content-Type": "text/html" });
res.write("<h1>" + message + "</h1>");
res.end();
}).listen(port);
}
 
import server = module("./server");
server.simpleServer(1337, "Greetings Channel 9");
console.log("Listening...");
Examples
  • ImageBoard using TypeScript, node.js, express and mongoDB.
  • Warship using TypeScript and jQuery, jQueryUI

Backbone.js Fundamentals - Notes 

Categories: Javascript Notes

Backbone.js
Liam McLennan

I like the model inheritance and statics of backbone. It is also really nice that you can replace the server-side with a local storage implementation. I do prefer the model binding of knockout however. Just seems more clean then the makes.

1. Introduction
 
Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a REstful JSON interface.
 
It's not a framework and it is not MVC.
 
 
SPA 
Pros: Fast, Highly interactive, Scalable
Cons: Extra work for SEO, Difficult to test, Security issues
 
Server side pages -> Hybrid Ajax pages -> SPAs client side
 
Task board SPA - http://trello.com
 
Required Dependencies
  • Underscore.js
  • jQuery or zepto
  • Zepto lightweight jquery compatible api used for mobile devices. - http://zeptojs.com/  
var book = new Backbone.Model({title:'white tiger', author:'Aravid'});
book.get('title'); // "White tiger"
book.set('title', 'Funny tiger');
book.toJSON();
http://jsfiddle.net/ynkJE/ - preconfigured for backbone testing
(function() {
var Rectangle = Backbone.Model.extend({});
var RectangleView = Backbone.View.extend({
tagName: 'div',
className: 'rectangle',
events: {
'click': 'move'
}
render: function() {
this.setDimensions();
this.setPosition();
return this; //backbone convention
},
setDimensions: function() {
this.$el.css({
width: this.model.get('width') + 'px',
height: this.model.get('height') + 'px'
});
},
setPosition: function() {
var position = this.model.get('position');
this.$el.css({
left: position.x,
right: position.y
});
},
move: function() {
this.$el.css('left', this.$el.position().left + 10);
}
});
 
var myRectangle = new Rectangle({
width:100, height:60, position: { x:300, y:150 }
});
 
var myView = new RectangleView({ model: myRectangle });
$('div#canvas').append(myView.render().el);
 
//when creating an array
//_(models).each(function(model) {
//$('div#canvas').append(new RectangleView({model:model}).render().el);
//});
})(); 
2. Models
var Vehicle = Backbone.Model.extend(
{
defaults: {
'color': 'white',
'type': 'car'
}
initialize: function() {
console.log('this is a contructor');
}
},
{
summary: function() {return 'a static function';}
}
);
console.log(Vehicle.summary()):
var car = new Vehicle(); //will run constuctor
var SuperCar = Vehicle.extend({});
var superCar = new SuperCar();
console.log(superCar instanceof SuperCar); //true
console.log(superCar instanceof Vehicle); //true
console.log(superCar instanceof Backbone.Model); //true
 
ford.set({
'maximumSpeed': '99',
'color':'blue',
'description', '<script>injection</script>'
});
ford.escape('description');
ford.has('type'); //check if attribute defined 
Models raise events when their state changes, detect by listening to change event.
ford.on('change', function () {}); //listen to change of object
ford.on('change:color', function () {}); //listen to change of property
Custom model events are identified by string identifiers.
var volcano = _.extend({}, Backbone.Events);
volcano.on('disaster:eruption', function(options){
console.log('duck and cover - ' + options.plan);
}); //namespacing events
volcano.trigger('disaster:erruption', {plan:'run'});
volcano.off('disaster:erruption');
 
var ford = new Backbone.Model({});
console.log(ford.id) //undefined, because not saved to server yet
console.log(ford.cid) //c0, temp id
console.log(ford.isNew()); //true
Validation is called prior to set and save operations.
var Vehicle = Backbone.Model.extend(
{
validate: function (attrs) {
var validColors = ['white','red'];
var colorIsValid = function (attrs)
{
if (!attrs.color) return;
return _(validColors).include(attrs.color); 
}
if (!colorIsValid(attrs)) {
return "color must be one of: " + validColors.join(",");
}
}
});
var car = new Vehicle();
car.on('error', function (model,error){
console.log(error);
});
car.set({'color','green'}, {
error: function (error) {
alert(error);
}
});
var attrs = ford.toJSON(); //converts a model's attributes to a javascript object
console.log(JSON.stringify(attrs)); //string representation
3. Views
  • DOM - View - Model : glue between models and documents
var V = Backbone.View.extend({
tagName:'li',
id:'thing',
className:'active',
attributes: {
'data-value': 12345
}
});
var v = new V();
$('body').prepend(v.el);
var V = Backbone.View.extend({});
var v = new V({el: '#test'});
v.$el.css('background-color','Yellow');
 
var v = new Backbone.View({el:'body'});
v.el // <body></body> the dom element
v.$el // cached $(this.el) [<body></body>] the jquery wrapper of that element
this.$ // this.$('selector') = this.$el.find('selector') scoped to current view 
 
var RefreshingView = Backbone.View.extend({
initialize:function() {
this.model.on('change', function() { this.render(); }, this);
},
render: function() {
this.$el.html(this.model.get('text'));
}
});
var m = new Backbone.Model({text:new Date().toString()});
var v = new RefreshingView({model:m, el:'body'});
v.render();
setInterval(function() { m.set({text:new Date().toString());},1000);
 
//<h3 class="not-imortant">first version</h3>
var el = new Backbone.View().make('h3', { class: 'not-important' } , 'first version'); 
 
v.remove() //$el.remove();  to remove view from DOM
 
//this.$('.clickable').click(handleClick);
var FormView = Backbone.View.extend({
events: {
'click .clickable': 'handleClick'
},
handleClick: function() {}
});
Guidelines
  • Do not attach to existing elements
  • Do not access DOM elements the view does not own
  • Pass el to the constructor of self-updating view
 
4. Templating
 
Client-side Templating
 
Underscore.js
<% ... %> //exececute arbitrary code 
<%= ... %> //evaluate an expression and render the result inline
<%- ... %> //evaluate an expression and render the html escaped result inline
<script id="latlon-template" type="text/html">
<%=lat%> <%=lon%>
<% ([1,2,3]).each(function(number) {%>
<p><%=number%></p>
<% }); %>
</script>
var V = Backbone.View.Extend({
render: function() {
var data = { lat:-27, lon=123 };
var template = $('#latlon-template').html();
this.$el.html(_.template(template,data));
return this;
}
}):
var v = new V({el:'body'});
v.render();
 
var compiled = _.template(source);
 Handlebars is templating engine based on mustache. (no code philosophy)
{{lat}} {{lon}}
{{#each numbers}}
<p>{{this}}</p>
{{/each}}
var compiled = Handlebars.compile(source);
var rendered = compiled({lat:-27}); 
Server-side Templating
handlebars <input> -f <output> //pre-compile file based templates
var rendered = Handlebars.templates.list(data);
 
5. Routing 
  • Don't use routing like mvc controllers!
var DocumentRouter = Backbone.Router.extend({
routes: {
'contents' : 'contents'
'view/:title': 'viewDocument'
},
contents: function(){
$('body').html(new ContentsView({collection:documents}).render().el);
}
viewDocument: function (title) {
var selectedDocument = _(documents).find(function (document) {
return document.get('title') == title;
}
$('body').empty().append(new DocumentView({model: selectedDocument}).render().el);
}
});
var router = new DocumentRouter();
Backbone.history.start();
router.navigate('contents', {trigger:true});
eventAggregator.on('document:selected', function(document){
var urlPath = 'view/' + document.get('title');
router.navigate(urlPath, {trigger:true});
}
window.history.pushState(...); //html5 history api, else backbone will use hash fragments
http://localhost/search/cats vs http://localhost/#search/cats
 
SEO
  1. Render content on the server
  2. #! Urls 
 
Backbone.history.start({pushState:false});
router.navigate('!search/cats', {trigger:true});
// generated url -> /#!search/cats
// google converts to -> /_escaped_fragment_=search/cats which will need server rendered content 
https://developers.google.com/webmasters/ajax-crawling/docs/specification
 
6. Collections 
  • Container for multiple models of the same type
  • Retrieve models from the server
  • Create models and save them to the server
  • Group models by some attribute
  • Collection is an array-like object (can't use square brackets)
var c = new Backbone.Collection([
{name:'thing'},
{name:'other'}
]);
console.log(c.length); //2
console.log(c.at(0)); // thing
 
var Vehicles = Backbone.Collection.extend({
model:vehicle,
comparator: function(vehicle) {
return vehicle.get('sequence');
}
comparator: function(vehicle1, vehicle2) {
return vehicle1.get('sequence') < vehicle2.get('sequence') ? -1 : 1;
}
});
var vehicles = new Vehicles(...);
vehicles.add({name:'Fred', age:6}); //will convert to backbone model
vehicles.remove(vechicles.at(1));
vehicles.on('add', function (model, col, options) { ... options.index ... };
vehicles.add({name:'Fred2', age:6}, {at:2, silent:true}); 
var v1 = vehicles.get(1);
var v2 = vehicles.getByCid('c0');
 
//proxy from underscore.js
collection.forEach(function (item { print(item); });
collection.forEach(print);
collection.map(function(item) { return transform(item); });
collection.reduce(function(memo,item) { return memo + item.get('age'); }, 0 );
var dave = collection.find(function(model) { return model.get('name') == 'Dave'; });
collection.on('remove', function(model, collection) {});
collection.on('change:name', function(model, options) {});
7. Connecting to a Server
  • CORS - Cross-origin resource sharing 
  • Origin is application layer protocol + domain name + port number
  • Cors is an alternative to jsonp (jsonp only has GET verb)
  • Cors supports all verbs but needs modern browser (IE10)
var MyModel = Backbone.Model.extend({
url: 'http://tra.la/mymodel'
});
myModelInstance.save({}, {
success: function() {}, //response will give id
error: function() {}
});
https://github.com/liammclennan/backbone-server (with node.js)
people.create({name:"Tom", age:50}); //http post to insert a model
people.fetch() //get array of models that must have an id
var person = new Person({id:0});
people.add(person); //will get url from the collection
person.fetch({ success: function() {...} });
person.set('age',18);
person.save();
person.destroy();
Backbone.sync
  • A function that interfaces between backbone and the server
  • Implements crud behavior and can be overridden globally, per collection, or per model
 
Backbone.localStorage
  • Sets backbone.sync to local browser storage
 
8. Testing with Jasmine (instead of Mocha or qUnit)
  • Test model specifications
  • Test views (rendered elements, raised events)
  • Testing routes (difficult, so keep routing easy)
 
beforeEach(function(){
rectangle = new app.Rectangle();
});
 
describe('some context', function(){
it('should show some observable behavior', function() {
//assert expectations here
expect(rectangle.area()).toBe(28);
expect(mySetFunction).toThrow(); // will give error
expect(rectangleView.el.tagName).toBe('DIV');
expect(rectangleView.$el.hasClass('rectangle')).toBe(true);
});
});
Testing without a browser
 
  • For QUnit you can use http://phantomjs.org/ 
  • (you can also use this to take screenshots)
  • phantomjs.exe run-jasmine-fixed.js SpecRunner.html //0 success 1 failure

Javascript for C# developers - Notes 

Categories: Javascript Notes

Javascript for C# developers
Shawn Wildermuth

 
I am convinced that with the upcoming of all those hybrid application platforms, more and more developers will need to know more and more javascript and related framework. People want to have responsive designs with local storage and prefer to have one code base instead of one for each platform they deploy on. With the release of Node.js people even start to write javascript on server side. Not so sure if that is a good idea, but for sure over time people will prefer to be comfortable with one language instead of the slight speed difference. 
This course by Shawn is short and since you can speed up the video at least at 2x, it will take you less then an hour to watch. It will give you a quick look into the loose typed javascript. Something most of us back-end developers are not so comfy with. Feel free to start using typescript or coffeescript to ease the pain. 

1. Javascript basics

Engines (depends on brower)
  • V8 in Chrome
  • Chakra in IE
  • Monkey in Firefox
 
JIT'ing is based on what browser you're running in.
 
 
Types are typically defined by structure not by identity
var x = 0;
var isNumber = typeof x == "number";
x = new Object(); //no problem, redefines x with new type
http://www.jslint.com/ - can do some type checks for you
 
Classic duck typing
function Feed(ani) { ani.Feed(); } 
function Move(object) { object.Move(); }
Dynamic Typing
var x = {
name: "Shawn",
city: "Atlanta"
};
x.phone = "404-123-1234";
x.makeCall = function () {
callSomeone(this.phone);
};
Global Scope
  • Objects at root are 'global'
 
Closures
  • References outside of function are accessible in function, regardless of lifetime
 
Type Coalescing
"test " + "me" //test me
"test " + 1 //test 1
"test " + true //test true
"test " + (1==1) //test true
100 + "25" //10025
 
1 == "1"; //true
1 === "1"; //false - equality without coalescing
1 !== "1"; //true 
Value Types: boolean, string, number 
if ("hello") {} //true
if ("") {} //false
if (25) {} //true
if (0) {} //false
if (10/0) {} //false (NaN)
var a = null; if (a) {} //false
var u = "One" + "Two";
var single = u[4]; //'T'
log(u.length); //6
  • The number type holds IEEE-754 format which is prone to rounding errors.
var c = 080; //octal starting with zero
var d = 0xffff; //hex
var e = 1.34e6;
 
Number.MIN_VALUE;
Number.MAX_VALUE;
Number.POSITIVE_INFINITY;
Number.NEGATIVE_INFINITY;
 
var fail = 10/0; // NaN
var test1 = NaN == NaN; //false
var test2 = isNaN(NaN); //true
Reference Type: object 
var more = {
"moniker": 1,
height: 4.3,
subData {
option: true,
flavor: "vanilla"
}
}
var a = [ "hello", "goodbye" ];
a.length;
a.push({}); //add to end
var obj = a.pop(); //remove from end
a.shift(); //remove from beginning
a.unshift({}); //add to beginning
var where = c.indexOf(obj);
where = c.lastIndexOf(obj);
//etc. slice, splice, concat
Delegate Type: function
  • Data type much like Func<>
 
Special Type: undefined, null, NaN
var a = null; // "null"
var b = c; // "undefined" 
2. Functions
function foo(one, two, three) {
alert(one);
if (two) alert(two);
if (three) alert(three);
}
  • However there is no overloading, the last definition will override the previous.
  • The arguments.length is available inside function body.
  • Declared parameters do not matter. (arguments[x])
  • All functions return a value, it will return undefined if non existing.
var log = function(s) { alert("yes"); }
function log(s) { alert("yes"); }
var x = log.length; //1 paramter
var y = log.name; //"log"
var z = log.toString(); //"function log(s) { alert("yes"); }"
  • "this" applies to the owner of the function
var f = function() { alert(this); };
f(); // [Object Window]
  • bind() lets you change the owner (context)
var f = obj.myFunc.bind(this); 
f(); //this == global object
Functions define scope
var a = "Hello";
if (true) { var b = a; }
var c = b; //this works
var a = "Hello";
function () { var b = a; }
var c = b; //this doesn't work
  • Anonymous self-executing function protects the global namespace by function scope
  • Javascript lacks real namespaces!
(function(ns) {
var currentDate = new Date();
ns.currentTime = function (){
return currentDate;
};
})(window.MyNamespace = window.MyNamespace || {});
3. Object Oriented Javascript
var name = cust.name;
var nameAlso = cust["name"];
var company = cust."company name"; //does not work
var company = cust["company name"];
  • Dynamic objects work the same as c# dynamic foo = new ExpandoObject();
  • We can mimic c# class unit of work in javascript.
  • Use capital case for functions that are used as classes.
function Customer(name, company) {
//public
this.company = company;
//private
var mailServer = "mail.google.com";
var _name = name;
this.sendEmail = function (email) { sendMailViaServer(mailServer); };
//only use if really needed
Object.defineProperty(this, "name",{
get: function() { return _name; },
set: function(value) { _name = value; }
});
}
var cust = new Customer("Shawn", "Wilder Minds");
var name = cust.name;
cust.sendEmail("foo@test.foo");
 
//Static members
Customer.mailServerToo = "mail.google.com";
var svr = cust.mailServerToo; //doesn't work
var svr = Customer.mailServerToo;
 
//The prototype object
Customer.prototype.mailServer = "Tralala";
Customer.prototype.send = function (email) { 
var svr = this.maiLServer;
};
 
//Inheritance
var test = a instanceof Animal; //true
Cow.prototype = new Animal("Hay");
var c = new Cow("White");
c.feed(); //defined on a
var test2 = c instanceof Animal; //true
var test3 = c instanceof Cow; //true
  • Abstract class is possible, but better to avoid.
  • Protected is not possible.
  • Interfaces aren't necessary, use duck typing.
 
Enumerating Members (reflection)
for (var prop in cust) {
alert(prop); //name
alert(cust[prop]); //value
}
var has = cust.hasOwnProperty("name");
var isEnum = cust.propertyIsEnumerable("name");
  • Extension Methods: add to existing type's prototype.
4. Practical lessons
  • ECMAScript v5 allows more strictness. 
  • "use strict";
  • for..in returns the indexer not the object like in a c# foreach
svr.send({
to: "someone@somewhere.com",
from: "someone@somewhereelse.com",
body: "hello",
subject: "test",
complete: function(r) { alert("Success: " + r); },
error: function(e) { alert("Failed: " + e); }
});
 
SmtpClient.prototype.send = function(msg) {
var defaults = {
to: "bla@blo.bli"
};
$.extend(defaults, msg); //jQuery
var to = default.to;
};
Architecting large javascript codebases

Building a SPA with html5, web api, knockout and jquery - Notes 

Categories: CSS Javascript Notes WebApi

Building single page apps with html5, asp.net web api, knockout and jquery by JohnPapa.net and Pluralsight

 
Wonderful, just wonderful. This is one of the best and most complete tutorials I have ever seen. A must for every web developer. Non-developers should at least watch the application, to see how user friendly and responsive it is. The senior developer will get plenty of pointers to useful frameworks and libraries, while the beginner will get clear explanations and the why and how things are done. The included source code is very reusable and the only things that might need to be added to be able to use this in an enterprise environment are some secure token service and caching. (the use of breeze or upshot is recommended but not shown in the tutorial, probably because these frameworks are very new?) Enjoy. 
Why SPA?
  • As rich and responsive as a desktop app
  • Persisting important state on the client
  • Mostly loaded in the intitial page load
  • Progressively downloads features as required
  • 2-way data binding (MVVM)
  • Tombstoning / dropped connections 
1.CODE CAMPER
Technologies used:
  • CSS
  • LESS
  • Media Queries and responsive design
  • HTML5
  • Modernizr
  • HTML5 Boilerplate
  • AMD - Async Module Definition
  • Revealing Module Pattern
  • Prototypes
  • MVVM
  • jQuery - DOM/AJAX (+jquery.mockjson +jquery.activity)
  • knockout.js - Data Binding/MVVM (+Commanding +ChangeTracking +Templating +Validation)
  • amplify.js - Data Push/Pull, Client Storage, Messaging
  • Sammy.js - Nav/History
  • require.js - Dependency Resolution
  • underscore.js - Javascript Helpers
  • moment.js - Date parsing and formatting
  • toastr.js - UI Alerts
  • qunit - Testing
  • POCO Models
  • JSON and AJAX
  • Web API
  • Unit of Work Pattern
  • Repository Pattern
  • Single Responsibility Principle (SRP)
  • Entity Framework (EF Code First)
  • SQL Database
  • NuGet
  • JSON.NET
  • Ninject IoC
  • Web Optimization
2. TOOLS
  • JsFiddle.com / JsBin.com
  • Sublime / Notepad++
  • Mindscape - less in visual studio
  • Resharper
  • Responsinator.com - check in different sizes
  • ElectricPlum.com - iphone emulator
  • http://jpapa.me/responsivedesignbookmarklet
  • Web Standards Visual Studio Plugin
  • Web Essentials Visual Studio Plugin
3.DATA LAYER (EntityFramework 5.0)
SessionBrief (uses IDs)
Session: SessionBrief (gets full properties)
[Key] (uses Id as default)
DbSet<T>
EntityTypeConfiguration<T>
HasRequired( ).WithMany( ).HasForeignKey( ) 
override OnModelCreating : modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
CodeCamperDbContext : DbContext
{
   public CodeCamperDbContext() : base(nameOrConnectionString: "CodeCamper") { }
   public DbSet<Session> Sessions { get; set; }
}
Database.SetInitializer( );
CreateDatabaseIfNotExists<T>
DropCreateDatabaseIfModelChanges<T>
HasKey
EnityTypeConfiguration
HasRequired (s => s.Speaker).WithMany(p => p.SpeakerSessions).HasForeignKey(s => s.SpeakerId);
SRP - Single Responsibility Principle
public interface IRepository<T> where T : class
{
   IQueryable<T> GetAll();
   T GetById(int id);
   void Add(T entity);
   void Update(T entity);
   void Delete(T entity);
   void Delete(int id);
}
public class EFRepository<T> : IRepository<T> where T : class
{
   protected DbContext DbContext { get; set; }
   protected DbSet<T> DbSet { get; set; }
   public virtual T GetById(int id)
   {
      return DbSet.Find(id);
   }
  ...
}
Unit of Work Pattern decouples webapi controllers from the repositories and dbcontext
4. JSON with WebApi
Ninject IoC Container
kernel = new StandardKernel();
Kernel.Bind<ICodeCamperUow>().To<CodeCamperUow>();
..
config.DependencyResolver = new NinjectDependencyResolver(kernel);
 
[ActionName("rooms")]
GetRooms()
[ActionName("timeslots")]
GetTimeslots()
 
get throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
put return new HttpResponseMessage(HttpStatusCode.NoContent);
post 
response = request.createresponse(HttpStatusCode.Created);
response.Headers.Location = new Uri(Url.Link(RouteConfig.ControllerAndId, new { id = ...
testing : endpoints respond with success? return expected data? crud works?
config.Formatters.Remove(config.Formatters.XmlFormatter);
 
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
 
[Required(ErrorMessage = "Title is required")]
config.Filters.Add(new ValidationActionFilter());
//Puts the model errors in a JObject and returns them with
content.Response.CreateResponse<JObject>(HttpStatusCode.BadRequest, errors);
5. SPA Frame
 
Fallback for jQuery if CDN fails
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="script/lib/jquery-1.7.2.min.js"><\/script>')</script>
 
To activate bundles, the compilation debug has to be false.
bundles.UseCdn = true 
Modernizr goes separate since it loads first
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include("~/Scripts/lib/modernizr-{version}.js"));
@Scripts.Render("~/bundles/modernizr") 
bundles.Add(new ScriptBundle("~/bundles/jsmocks").IncludeDirectory("~/Scripts/app/", "*.js", searchSubdirectories:false);
 
bundles.Add(new StyleBundle("~/Content/css").Include(...);
bundles.Add(new StyleBundle("~/Content/less", new LessTransform(), new CssMinify()).Include("~/Content/styles.less"));
@Styles.Render("~/Content/css","~/Content/less")
6. Javascript Structure
  • Who is responsible for what?
  • Global scope pollution
  • High code re-use
  • Separation of concerns
  • Revealing Module Pattern
  • Bootstrapper.js will prime the data and setup the presentation
 
jQuery's deferreds, promises and $.when() at http://api.jquery.com/jquery.when/
 
ModularDemo to show js module dependencies: http://requirejs.org
define('alerter', ['dataservice'], function (dataservice) { ... });
requirejs.config({ baseUrl:'scripts', paths: { 'jquery' : 'jquery-1.7.2' } });
7. MVVM & KNOCKOUT
<input class="filter-box" type="text" 
data-bind="value: sessionFilter.searchText, 
valueUpdate: 'afterkeydown', 
escape: clearFilter"
placeholder="filter by model and brand"/>
 
_.reduce(timeslots, function (memo, slot) {
...
day = moment(slotStart).format('ddd MM DD');
...
}
 
configureExternalTemplates = function () {
infuser.defaults.templatePrefix = "_";
infuser.defaults.templateSuffix = ".tmpl.html";
infuser.defaults.templateUrl = "/Tmpl";
}
 
ko.bindingHandlers.escape example
 
self.searchText = ko.observable().extend({ throttle: config.throttle });
 
WebEssentials Plugin
ko.bindingHandlers example for favorites icon
 
sessions = ko.observableArray().trackReevaluations()
 
Debugging Tip
<div style="border: white dotted thin; clear: both">
<h4>Sessions JSON</h4>
<pre data-bind="text:ko.utils.debugInfo(speaker())"></pre>
</div>
ko.utils.debugInfo = function (items) {
return ko.computed(function() {
return ko.toJSON(items, null, 2); // JSON.stringify(ko.toJS(timeslots), null, 2);
}
}
8. Data Services on the Client
  • DataContext, DataService and Model Mappers
  • SRP - Single Responsibility Principle
  • datacontext.sessions.getData({data: sessions});
 
It's faster to create the raw array and then stick it into an ko.observableArray, so you get only one event notification !
 
  • $.ajax() vs $.getJSON()
  • http://amplifyjs.com : Wraps Ajax calls, Always Async, Mock Data, Caching
var init = function () {
amplify.request.define('session-briefs', 'ajax', {
url: 'api/sessionbriefs',
dataType: 'json',
type: 'GET'
//cache: true
//cache: 60000 // 1 minute
//cache: 'persist'
});
}
dataservice.session.js vs mock.dataservice.session.js
$.mockJSON.data.TITLE = [
'Pick and choose from these lines',
'Bla bla bla',
  'more bla bla' 
];
 
var data = $.mockJSON.generateFrom Template({
 'sessions|100-120' : [{
'id|+1': 1,
title: '@TITLE',
code: '@LOREM', //just one word
'speakerId|1-50':1,
description: '@LOREM_IPSUM' //just some text  
  }]
});
 
//some hardcoded to do some testing against
data.sessions[0].id = 1;
data.sessions[0].title = 'Single Page Apps';
 
if (_useMocks) {
dataserviceInit = mock.dataserviceInit;
}
dataserviceInit();
 
config.useMocks(true); //can be used in your tests
Role of the Data Context
  • Caches client data (Stores data in memory)
  • Facade for Data Services (Simplified API)
  • HandlesDataRequestLogic (Cached then return else get and cache) 
datacontext.sessions.getData(dataOptions(false))
sessions = new EntitySet(dataservice.session.getSessionBriefs, modelmapper.session, model.session.Nullo); 
EnititySet has mapDtoToContext, add, removeById, getLocalById, getAllLocal, getData, updateData
 
Prototypes in Javascript
 
Upshot.js 
  • Uses DataController that inherits from ApiController
  • Can get meta data from your WCF services
  • From the asp.net team
var dataSource = upshot.RemoteDataSource({
    providerParameters: { 
        url: constants.serviceUrl, 
        operationName: "GetProducts" 
    },
    provider: constants.provider,
    entityType: "Product:#Sample.Models"
}).refresh();
Breezejs.com
manager = new entityModel.EntityManager('api/todos');
var query = new entityModel.EntityQuery()
.from("Todos")
.orderBy("CreatedAt");
query = query.where("IsArchived", op.Equals, false);
  • Uses a Metadata action method on your controller (contextProvider.Metadata());
9. Navigation between views
sammy.get("#/sessions/:id', function (context) {
vm.callback(context.params);
presenter.transitionTo( $(viewName), path );
}
  •  jQuery event delegation to the container (performance)
  • bindEventToList(config.viewIds.sessions, sessionBriefSelector, callback, eventName)
  • resharper ctrl-shift-f7 and ctrl-alt-down will find the selected word in file.
$(rootSelector).on(eName, selector, function() {
var session = ko.dataFor(this);
callback(session);
return false;
});
Adding page transitions
  • css3-transitions: http://caniuse.com or http://realworldvalidator.com
  • jQuery transitions if you want to go cross-browser
  • presenter.transitionTo();
 
Browser history
<button class="button-back" data-bind="command: goBackCmd"></button>
 
goBackCmd = ko.asyncCommand({
execute: function (complete) { router.navigateBack(); complete() },
canExecute: function (isExecuting) { return !isExecuting && !isDirty(); }
});
ko.bindingHandlers.command = { ... };
 
Cancel & Recall using amplifyjs
  • module amplify.store for client storage on old and new browsers
  • messenger module amplify.publish and amplify.subscribe
 
If changes and user clicks back button, we use toastr to display a warning message (http://nuget.org/packages/toastr).
sammy.before(/*/, function () { ... response = routeMediator.canLeave(); ... }
 
store.save(config.stateKeys.lastView, context.path);
This module wraps amplify, so it can easily be replaced with localstorage on HTML5
expires: config.storeExpirationMs in milliseconds
 
Alternatives for sammy.js
10. Saving, Change Tracking and Validation
model.person.js
self.dirtyFlag = new ko.DirtyFlag([
self.firstName,
self.lastName,
self.email,
]);
isDirty = ko.computed(function() {
return canEdit() ? speaker().dirtyFlag().isDirty() : false;
});
 
Asynchronous Commands 
(eg. disable save button once it is clicked and put status indicator)
saveCmd = ko.asyncCommand({
execute: function(complete) {
$.when(datacontext.person.updateData(speaker()))
.always(complete);
},
canExecute: function(isExecuting){
return !isExecuting && isDirty() && isValid();
}
});
<button data-bind="command: saveCmd, activity: saveCmd.isExecuting'>Save</button>
Explicit vs Implicit Save?
saveFavorite = function (selectedSession) {
if (isBusy) { return; }
isBusy = true;
var cudMethod = selectedSession.isFavorite()
? datacontext.attendance.deleteData
: datacontect.attendance.addData;
cudMethod(selectedSession,
{
success: function() { isBusy = false; },
error: function() { isBusy = false; }
}
);
}
 
Use valueHasMutated() to tell and observable its value has updated.
 
Validate Data?
 
  • Using Knockout.Validation plugin.
self.firstName = ko.observable().extend({ required: true });
self.email = ko.observable().extend({ email: true });
self.blog = ko.observable().extend({
pattern: {
message: 'Not a valid url',
params: /[@]([A-Za-z0-9_]{1,15})/i
}
});
 
validationErrors = ko.validation.group(speaker());
isValid = ko.computed(function() {
return canEdit() ? validationErrors().length == 0 : true;
});
canLeave = function() {
return canEdit() ? !isDirty() && isValid() : true;
}
11. Mobility and Responsive Design with CSS and Less
  • Balsamiq to do mocks of gui
  • Web Essentials in Extensions and Updates of Visual Studio 2012
  • http://lesscss.org
  • Less: Variables, Mixins, Nested Rules, Functions and Operations
  • Media Queries
  • Testing for multiple devices: http://jpapa.me/electricplum 
@media only screen and (max-width: 1000px)
@media only screen and (max-width: 900px)
@media only screen and (max-width: 800px)
@media only screen and (max-width: 480px)
@media only screen and (max-width: 320px)
@media only screen and (max-width: 240px)
 
@media only screen and (max-height: 768px) {
.view-list { height: 330px; }
}

 

Knockout.js 

Categories: Javascript

http://knockoutjs.com/ documentation by Steve Sanderson

  • Automatic UI Refresh MVVM pattern for javascript
  • Automatic dependency tracking
  • Uses jQuery templates
  • Declarative bindings Integrated templating
  • Upgrade JSON to observable by using Knockout.mapping
  • DependentObservable => Computed

Overview

var myViewModel = { personName: ko.observable('Ken'), personAge:103 };
The name is <span data-bind="text: personName"></span>

ko.applyBindings(myViewModel);
ko.applyBindings(myViewModel, document.getElementById('someElementId'));

myViewModel.personName(); to get
myViewModel.personName('Patrycja'); to set
myViewModel.personName('Patrycja').personAge(50);

var subscription = myViewModel.personName.subscribe(function(newValue) { alert(newValue); });
subscription.dispose();

this.fullName = ko.computed(function() { return this.firstName() + " " + this.lastName(); }, this);
function AppViewModel() {
    var self = this;
    self.firstName = ko.observable('Bob');
    self.lastName = ko.observable('Smith');     
    self.fullName = ko.computed(function() {
        return self.firstName() + " " + self.lastName();
    });
}
this.fullName = ko.computed({
        read: function () {
            return this.firstName() + " " + this.lastName(); 
        },
        write: function (value) {
            var lastSpacePos = value.lastIndexOf(" ");
            if (lastSpacePos > 0) { // Ignore values with no space character
                this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
                this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
            }
        },
        owner: this
});
ko.applyBindings(new MyViewModel());
read, write, owner, deferEvaluation, disposeWhen, disposeWhenNodeIsRemoved

ko.isComputed, ko.isObservable, ko.isWriteableObservable

var myObservableArray = ko.observableArray([{ name: "Bungle", type: "Bear" }]);
myObservableArray.push('Some value'); // Adds the value and notifies observers
alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);
myObservableArray.indexOf('Blah'); //zero-based and -1 if not found
myObservableArray.slice(...);
myObservableArray.push('Some new value'); //adds a new item to the end of array
myObservableArray.pop(); //removes the last value from the array and returns it
myObservableArray.unshift('Some new value'); //inserts a new item at the beginning of the array
myObservableArray.shift(); //removes the first value from the array and returns it
myObservableArray.reverse(); //reverses the order of the array
myObservableArray.sort(); //sorts the array contents.
myObservableArray.sort(function(left, right) 
	{ return left.lastName == right.lastName ? 0 : 
	(left.lastName < right.lastName ? -1 : 1) });
myObservableArray.splice(1, 3);
myObservableArray.remove(someItem); 
myObservableArray.remove(function(item) { return item.age < 18 });
myObservableArray.removeAll(['Chad', 132, undefined]);
myObservableArray.removeAll();

visible, text, html, css, style, attr
<div data-bind="css: { profitWarning: currentProfit() < 0 }">...</div>
<div data-bind="style: { color: currentProfit() < 0 ? 'red' : 'black' }">...</div>
<a data-bind="attr: { href: url, title: details }">...</a>

foreach, if, ifnot, with
<div data-bind="if: displayMessage">...</div>

click, event, submit, enable, disable, value, hasfocus, checked, options, selectedOptions, uniqueName
<button data-bind="click: incrementClickCounter">Click me</button>
<div data-bind="event: { mouseover: enableDetails, mouseout: disableDetails }">...</div>
<input data-bind="hasfocus: isSelected" />

ko.bindingHandlers.yourBindingName = { ... };

<div data-bind="template: { name: 'person-template', data: buyer }"> </div>
<script type="text/html" id="person-template">
    <h3 data-bind="text: name"></h3>
    <p>Credits: <span data-bind="text: credits"></span></p>
</script>

.extend({ throttle: 500 }); //delayed binding

var viewModel = ko.mapping.fromJS(data);
ko.mapping.fromJS(data, viewModel);
var unmapped = ko.mapping.toJS(viewModel);

Example

var viewModel = {
	firstName: ko.observable("Ken"),
	lastName: ko.observable("Van Gilbergen"),
	friends: ko.observableArray([new friend("Steve"), new friend("Annie")]),
	addFriend: function() {
		this.friends.push(new friend("Tim"));
	},
	save: function() {
		$.ajax({
			url: "@Url.Action("Save")",
			type: "post",
			data: ko.toJSON(this),
			contentType: "application/json",
			success: function (result) { alert(result.message) }
		});
	}
};

viewModel.fullName = ko.computed(
function() {
	return this.firstName() + " " + this.lastName();
}, viewModel);

ko.applyBindings(viewModel);

<input data-bind="value: firstName" />

function friend(name)
{
	return {
		name: ko.observable(name),
		isOnTwitter: ko.observable(false),
		twitterName: ko.observable,
		remove: function ()
		{
			viewModel.friends.remove(this);
		}
	};
}

<div data-bind="template: 'friendsTemplate'"> </div>
<script id="friendsTemplate" type="text/html">
	<ul>
	{{each(index, friend) friends}}
		<ul><li>${ friend.name } - ${ new Date }</li></ul>
		<label><input type="checkbox" data-bind="checked: isOnTwitter" /> Is on twitter</label>
		<input data-bind="value: twitterName, visible: isOnTwitter" />
	{{/each}}	
	</ul>
</script>
<button data-bind="click: addFriend, enabled: friends().length < 5">Add Friend</button>

<script id="friendsTemplate2" type="text/html">
	<ul><ul><li>${ data.name } - ${ new Date }</li></ul>
	<button data-bind="click: remove">Remove
</script>

public JsonResult Save(Person person)
{
	...
	return Json(new { message });
}

public class Person
{
	public string FirstName { get; set;}
	public ICollection Friends { get; set; }     } 
Page 3 of 4 << < 1 2 3 4 > >>