Django - Angularjs COMBINING Frameworks

 



Created by:


Yftah Peled

Full stack developer @ www.roojoom.com

Check out my Roojoom profile 

Angularjs INTRODUCTION


Client side JS based Framework

MVC (two way binding between view and controller)

Directives

topics:

  • What gets rendered when? -  TEMPLATES / UX / SEO dilemmas
  • Forms validation
  • Uploading: form Vs. API
  • Passing data / variables / Settings - Player 
  • Django  - Angular 0.1

REndering


Test case:

How should we render Django models related data?

Just Angularjs?

Just Django?

Combo

PERCEIVED PERFORMANCE

OR WHAT AND WHEN DOES THE USER SEE ON SCREEN?


Just AngularJS - async database requests (but - AngularJS 1.0.8 is 80KB)

Just Django - database requests

Combo - Just like it sounds

SEO


Django

AngularJS  (PhantomJS)

Combo

Templates issues


Tags clashes - {{ }}

Directives

ng-cloak / ng-hide

Filters

Loops

D.R.Y issue on combo

Tags Clash

Django solution

{% verbtim %} 

Angularjs solution:
angular.module('trekpage_editor', ['ui.bootstrap', 'filters', 'ngResource', 'ui.sortable','RjResources','ValidationModule','AddPage'])
.config(function($interpolateProvider,$httpProvider) {
    $interpolateProvider.startSymbol('{[{');
    $interpolateProvider.endSymbol('}]}'); 



Directives

"Directives are a way to teach HTML new tricks. During DOM compilation directives are matched against the HTML and executed. This allows directives to register behavior, or transform the DOM.
Angular comes with a built in set of directives which are useful for building web applications but can be extended such that HTML can be turned into a declarative domain specific language (DSL)."

Directives - example

my_app.directive('limitinput', function () {    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            scope.inputchanged = function () {
                setTimeout(function () {
                    var limit = element.attr('limitinput');
                    var val = ctrl.$viewValue
                    if (val == undefined) return
                    var char_count = val.length;
                    if (char_count >= limit) {
                        attrs.$set('value', val.slice(0,limit))
                        scope.$apply(function() {
                          ctrl.$setViewValue(val.slice(0,limit));
                        });
                    }
                },100)
            }
            element.keypress(scope.inputchanged)
                .focus(scope.inputchanged)
                .live('input paste', scope.inputchanged)
                .on('ngChange', scope.inputchanged);
        }
    };
});

Ng-Cloak / NG-hide

AngularJS provides ng-cloak tag hiding 
elements using CSS until scope is populated but...
What if there is only a promise? you get ugly looking page full with tags.
In that case: control menualy it with ng-hide

Template Filters

Example: add 'http://' to url
Django: 
@register.filter(name='roojoom_legit_url')
def roojoom_legit_url(text):
    if not value.startswith("http"):
        value = 'http://' + text
    else:
        return text 
AngularJS:
angular.module('filters', []).
    filter('legit_url', function () {
        return function (text) {
            if (text != undefined) {
                if (text.indexOf('http') != 0) {
                    text = 'http://' + text
                }
                return text
            }
        };
    }) 

Loops

Django
{% for maintrek in not_featured.objects %}
   <div class="roojoom_box item">
       <div class="roojoom_category">
           <a href="/webtreks/category={{ maintrek.category.id }}">
               <img class="category_icon" src="{{maintrek.category.icon }}" alt="category" />{{  maintrek.data.category.data.name }}
            </a>
        </div>     ...     </div>{% endfor %}
AngularJS
<div class="roojoom_box item" ng-repeat="maintrek in webtreks.objects">                
    <div class="roojoom_inner_box">
        <div class="roojoom_category">
            <a ng-href="/webtreks/category={[{ maintrek.category.id }]}">
               <img class="category_icon" ng-src="{[{  maintrek.category.icon }]}" alt="category" />{[{ maintrek.category.name }]}
            </a>
         </div>     ...     </div></div>


Interaction


Dealing with dynamic pages can easily done using directives.

ng-bind

or 

Template:

ng-bind

<div ng-controller="Ctrl">   Enter name: <input type="text" ng-model="name">   <br>Hello <span ng-bind="name"></span>!</div>


   Demo

directives with templates


 <div ng-controller="Ctrl">   <div show-input> </div>

my_app.directive('showInput', function() {
  return {
    restrict: 'A',
    template: 'Enter name: <input type="text" ng-model="name"><br>Hello <span ng-bind="name"></span>!'  }
});

Forms


auto validation

custom validation

Angular Vs. Django (Regular / Ajax)

Auto VALIDATION

<div ng-controller="Controller">
   <form name="form" class="css-form" novalidate>
      Name:<input type="text" ng-model="user.name" name="uName" required />      <br />
      E-mail:<input type="email" ng-model="user.email" name="uEmail" required />      <br />
      <div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
        <span ng-show="form.uEmail.$error.required">Tell us your email.</span>
        <span ng-show="form.uEmail.$error.email">This is not a valid email</span>
      </div>       <button ng-click="update(user)" ng-disabled="form.$invalid ||                         isUnchanged(user)">SAVE</button>
   </form></div>

Demo

Custom Validation

Django:
def clean(self, value):
    value = super(SetPasswordField, self).clean(value)
    min_length = app_settings.PASSWORD_MIN_LENGTH
    if len(value) < min_length:
        raise forms.ValidationError(_("Password must be a minimum of {0} " "characters.").format(min_length))    return value 
Angular (Using directive)
var MIN_LENGTH = 6;
app.directive('validatePassword', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (viewValue > MIN_LENGTH) {
          ctrl.$setValidity('MIN_LENGTH', true);
          return viewValue;
        } else {
          ctrl.$setValidity('MIN_LENGTH', false);
          return undefined;
        }
      });
    }
  };
});

Uploading


Use plug-ins

Upload using forms 

Upload using API

Passing DATA & Variables


Creating angular resource

Passing data using template

Interacting with API resource / AJAX


Angular service


myModule.factory('serviceId', function() {
  var shinyNewServiceInstance;
  return shinyNewServiceInstance;
});

Using Template


   <script type="text/javascript">
        myModule.factory('serviceId', function(){
            var shinyNewServiceInstance = {{django_data_json|safe}} ;
            return shinyNewServiceInstance;
        })
    </script>
<script> angular.module('serviceId').constant('user', { user: "{{ request.user.id }}", ... }).constant('urls', { this_view_url: "{% url this_view %}", that_view_url: "{% url that_view %}", ... }); </script>

interacting with http requests and api





ng-resource ($http, $resource)

Restangular

Django - angular 0.1



Ajax requests from the controller

Form integration






Django - Angularjs

By Yftah Peled