Slide 1

Slide 1 text

DJANGO AND MVC JAVASCRIPT FRAMEWORKS

Slide 2

Slide 2 text

Sean Creeley @screeley [email protected]

Slide 3

Slide 3 text

WHAT WE BUILT

Slide 4

Slide 4 text

What is JavaScript MVC?

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Why Ember?

Slide 7

Slide 7 text

Why JavaScript MVC?

Slide 8

Slide 8 text

UP THE STACK

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

BROWSERS GOT BETTER

Slide 12

Slide 12 text

IE - 87.2% Firefox - 8.07% Safari: 3.2% Mozilla Suite - 0.52% Opera - 0.49% JULY 2005

Slide 13

Slide 13 text

Chrome: 32.3 % IE: 22.2% Firefox: 18.5% Safari: 14.7% Opera: 2.3% JULY 2013

Slide 14

Slide 14 text

XMLHttpRequest Web Standard jQuery Released Internet Explorer 7 2006 2007 iPhone Released 2008 Google Chrome

Slide 15

Slide 15 text

APIS ARE KING

Slide 16

Slide 16 text

USE THE GOOD PARTS

Slide 17

Slide 17 text

EMBER INTRO

Slide 18

Slide 18 text

APPLICATION window.Dolphin = Em.Application.create({ rootElement: $('#dolphin'), ready: function(){} });

Slide 19

Slide 19 text

ROUTER Dolphin.Router.map(function() { this.route('about'); this.resource('company', function(){ this.route('team'); }); }); / /about /company /company/team

Slide 20

Slide 20 text

TEMPLATES
{{outlet}}

Welcome to Dolphin!

Helping Dolphins become the smartest species since 2013

application.handlebars index.handlebars

Slide 21

Slide 21 text

MODELS Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return {name: 'sean'} } });

{{name}}

company/team.handlebars

Slide 22

Slide 22 text

MODELS Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return [ {name: 'sean'}, {name: 'kawandeep'}, {name: 'andy'}] } });
    {{#each controller}}
  • {{name}}
  • {{/each}}

Slide 23

Slide 23 text

MODELS Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return Dolphin.User.find({is_team: true}); } }); Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return $.getJSON(‘/api/users’, {is_team :true}); } });

Slide 24

Slide 24 text

MODELS Dolphin.User = DS.Model.extend({ profile: DS.belongsTo('Dolphin.Profile'), first_name: DS.attr('string'), last_name: DS.attr('string'), username: DS.attr('string'), email: DS.attr('string') });

Slide 25

Slide 25 text

CONTROLLERS Dolphin.CompanyTeamController = Em.ArrayController.extend({ sortAscending: ['years_coding'], totalYears: function(){ return this.reduce(function(total, user) { return total + user.get('years_coding'); }, 0); }.property('@each.years_coding') });

Slide 26

Slide 26 text

VIEWS Dolphin.CompanyTeamView = Em.View.extend({ didInsertElement: function () { // fade images in. this.$().find('img').animate({ opacity: 1 }, 2000); } });

Slide 27

Slide 27 text

DJANGO DEVELOPMENT

Slide 28

Slide 28 text

#1 SEPARATE FRONTEND AND BACKEND DEVELOPMENT

Slide 29

Slide 29 text

USE GRUNT #2

Slide 30

Slide 30 text

#3 ALLOW XSS HEADERS

Slide 31

Slide 31 text

XSS_ORIGINS = 'http://localhost.com:3501' XSS_METHODS = ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE'] XSS_HEADERS = ['Content-Type', 'x-requested-with', '*'] XSS_CREDENTIALS = 'true' class XSSharing(object): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = XSS_ORIGINS response['Access-Control-Allow-Methods'] \ = ",".join(XSS_METHODS) response['Access-Control-Allow-Headers'] \ = ",".join(XSS_HEADERS) response['Access-Control-Allow-Credentials'] \ = XSS_CREDENTIALS return response https://gist.github.com/426829

Slide 32

Slide 32 text

#4 WRITE AN API YOURSELF

Slide 33

Slide 33 text

urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), url(r'^api/users(\/(?P\d+))?', UserView.as_view()), ) urls.py

Slide 34

Slide 34 text

from django.views.generic.base import View from django.http import HttpResponse from django.contrib.auth.models import User from django.forms.models import model_to_dict class UserView(View): def serialize(self, obj): return model_to_dict(obj, fields=['id', 'first_name', 'last_name', 'email']) def get(self, request, pk=None): if pk is not None: data = {'user': self.serialize(User.objects.get(pk=pk))} elif request.GET: data = {'users' : map(self.serialize, User.objects.filter(**request.GET))} else: data = {'users' : map(self.serialize, User.objects.all())} return HttpResponse(json.dumps(data), mimetype="application/json") views.py

Slide 35

Slide 35 text

users.js Dolphin.User = DS.Model.extend({ first_name: DS.attr('string'), last_name: DS.attr('string'), email: DS.attr('string') }); >> var user = Dolphin.User.find(1); >> user.get(‘first_name’) “sean”

Slide 36

Slide 36 text

#5 MAKE THE FRONTEND DUMB

Slide 37

Slide 37 text

#6 AUTHENTICATE

Slide 38

Slide 38 text

class UserAuth(auth.Auth): def can_access(self, request, obj): if request.user.id == obj.id: return True if request.user.is_superuser: return True return False class UserView(ember.View): ... def get(self, request, pk, **kwargs): obj = self.model.objects.get(pk=pk) if not self.auth.can_access(request, obj): raise ValueError('no auth specified') return self.serializer.one(obj) ember.register(User, UserSerializer, UserView, auth=UserAuth())

Slide 39

Slide 39 text

#7 MIX AND MATCH

Slide 40

Slide 40 text

#8 USE RELATIONS

Slide 41

Slide 41 text

class Author(models.Model): first_name = models.CharField() last_name = models.CharField() class Book(models.Model): author = models.ForeignKey(Author) title = models.CharField() models.py

Slide 42

Slide 42 text

Dolphin.Author = DS.Model.extend({ books: DS.hasMany('Dolphin.Book'), first_name: DS.attr('string'), last_name: DS.attr('string') }); Dolphin.Book = DS.Model.extend({ author: DS.belongsTo('Dolphin.Author'), title: DS.attr('string'), }); models.js

Slide 43

Slide 43 text

#9 DON’T PASS BACK ALL FIELDS

Slide 44

Slide 44 text

#10 JQUERY IS NOT THE ENEMY.

Slide 45

Slide 45 text

App.CommentView = Em.View.extend({ template: Em.Handlebars.compile( '{{name}}

{{{body}}}

'), didInsertElement: function(){ this.$().find('p').animate({ opacity: 1 }, 1000); } });

Slide 46

Slide 46 text

#11 WORRY

Slide 47

Slide 47 text

{ "blogs": [ { "id": 1, "name": "Embedly Blog" }, { "id": 1, "name": "Embedly Blog" }, .... ] } /api/blogs

Slide 48

Slide 48 text

{"users": [ { "id": 1, "name": "Sean", "email": "[email protected]" }, { "id": 2, "name": "Kawandeep", "email": "[email protected]" }, .... ]} /api/users

Slide 49

Slide 49 text

#12 DEPLOY STATIC

Slide 50

Slide 50 text

#13 deferReadiness IS YOUR FRIEND

Slide 51

Slide 51 text

App.deferReadiness(); // Wait for all the javascript files to load. $(document).ready(function(){ App.User.current(function(user, profile, organizations){ // Set everything else up. App.set('user', user); // Will start everything up. App.advanceReadiness(); }); });

Slide 52

Slide 52 text

#11 IT’S PROBABLY NOT EMBER’S FAULT.

Slide 53

Slide 53 text

QUESTIONS? @screeley @embedly