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:
Display categories on Roojoom

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

Demo
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




Documentation


Made with Slides.com