+

From $()
to
angular.module()

Front-end developer @ DoYouBuzz
The Web is more than just my job, it's my passion @atomrc

What are we talking about?

The old codebase

: The codebase


$('a.widget.edit').click(function () {
        var $this = $(this);
        $('.random.selector').show();
        $('.other.random.selector').hide();
        $.get($this.attr('href'), function (response) {
            $this
                .parent()
                .parent()
                .find('.widget-content')
                .html(response.html);
        });
        return false;
    });
<a
        class="edit"
        href="/fr/ajax/skills/save/?id=11&cv_id=12">
    </a>

: The codebase

The front is really user friendly but not developer friendly at all !

: The codebase

How many skills on my resume ?

$('.widget.skills').length;
-> 4

: DoYouBuzz V1

: DoYouBuzz V1

: DoYouBuzz V1

We are simulating a front application


The adult age

V2


=


+

+



+

No country for old JS !

+

Why Angular ?
angular
        .module('doyoubuzz', ['ng-animate'])
        .controller('resumeController', function () {})
        .factory('resumeManager', function () {})
        .directive('editBox', function () {});
    
[
        'dependancy1',
        'dependancy2',
        function (dependancy1, dependancy2) {
            dependancy1.makeTheAudienceSmile();
            dependancy2.nextSlide();
        }
    ];

+

And more :

+

Directives

  • Everything about the DOM

Services

  • All the logic of the application
  • also the shared memory of the application

Controllers

  • The link between the DOM and the datas
  • responsible for the publication of the scope

Misc

  • global API
  • resources
  • animations
  • ...

+ : the scopes

 
 
 
{
        positions: Object
        resume: Object
        createElement: function (type) {}
        hasWidget: function (widgetName) {}
        isMovable: function (type) {}
        sortStop: function (event, data) {}
    }
    
{
        type: String,
        elements: Array,
        create: function () {},
        edit: function (element) {},
        isDeletable: function (element) {},
        isHome: function (element) {},
        isMovable: function (element) {},
        isNew: function (element) {},
        isNotNew: function (element) {},
        remove: function (element) {},
        sort: function () {}
    }
    

+ : the scopes

 
 
 

    <button data-ng-click="edit(element);"></button>

    

+ : the scopes

prototype inheritance

{
        //widgetController's prototype
        type: String,
        elements: Array,
        create: function () {},
        //[...]
        remove: function (element) {},
        sort: function () {}
    }
    
{
        //widgetController's prototype
        type: String,
        elements: Array,
        create: function () {},
        //[...]
        remove: function (element) {},
        sort: function () {},
        //inherited resumeController's prototype
        __proto__: {
            resume: Object
            createElement: function (type) {}
            hasWidget: function (widgetName) {}
            isMovable: function (type) {}
            sortStop: function (event, data) {}
        }
    }
    

+ : the services






    /* controller : resumeController */
        createElement: function (type) {
            widgetsManager.create(type);
        }

    

+ : the services


    /* service : widgetsManager */
        create: function (type) {
            if (!this.canCreate(type)) {
               return errorHandler.widgetLimitError();
            }

            Widget
                .create({ type: type })
                .then(function () {
                    widget.setEditMode(true);
                });

            this.widgets[type].unshift(widget);
        }

    

+ : the services

the unit test

    it('should not create if user exceeded limitation', inject(function (widgetsManager, offerManager) {
        offerManager.offer.nbSkills = 2;

        widgetsManager.create('skills');
        expect(widgetsManager.widgets.skills.length).toBe(1);

        widgetsManager.create('skills');
        expect(widgetsManager.widgets.skills.length).toBe(2);

        widgetsManager.create('skills');
        expect(widgetsManager.widgets.skills.length).toBe(2);
    }));
                

+ : the services

the widgetsManager

    {
        widgets: { skills: [], portfolios: [] },
        create: function (type) {},
        edit: function (element) {},
        remove: function (element) {},
        move: function (type) {},
        canCreate: function (type) {}
    }
                

+ : the services

/* dataManager */
    return {
        datas: [],
        add: function (element) {
            if (this.datas.length >= 3) {
                return window.alert('dataManager says : STOP I wont add anything more');
            }
            this.datas.push(element);
        }
    };
    

    /* elementsController */
    scope.addElement = function (element) {
        dataManager.add(element);
    };
    

    /* thingsController */
    scope.addThing = function (thing) {
        dataManager.add(thing);
    };
    

+ : the services

the unit test

    it('should not create if user exceeded limitation', inject(function ($window, dataManager) {

        spyOn($window, "alert").and.callFake(function() {
          return true;
        });

        datasManager.add({color: 'green'});
        expect(datasManager.datas.length).toBe(1);

        datasManager.add({color: 'red'});
        expect(datasManager.datas.length).toBe(2);

        datasManager.add({color: 'purple'});
        expect(datasManager.datas.length).toBe(3);

        datasManager.add({color: 'grey'});
        expect(datasManager.datas.length).toBe(3);
        expect($window.alert).toHaveBeenCalled();
    }));
                

+ : resources

The only bound client <-> server
var Widget = $resource(
        '/cv/:resumeId/:type/:id/:action',
        { type: type, resumeId: 12, id: '@id' },
        {
            get: { method: 'GET' },
            create: {
                method: 'GET',
                params: {action: 'new'}
            },
            save: { method: 'POST' },
            update: { method: 'PUT' }
        }
    );
    

+ : resources




    var w = new Widget({ type: ‘skills’, title: ‘felix’ });
    w.$save(); // POST -> /cv/12/skills
    w.title = ‘other title’;
    w.$update(); //PUT -> /cv/12/skills/1
                

The constraint

Resume in 'edit' mode
===
Resume in 'view' mode
without in view mode !!

+ : la contrainte

+ : edit mode

I think you forgot something?
Angular.js
jQuery.js
services.js
[...]
application.js
Can you send me the resume?
{
        widgets: [...],
        positions: [...],
        ...
    } 

+ : view mode

+ : the constraint

Each month :
300 000
1 200 000
Resumes edited Resumes viewed

+ : Problem

How to render views generated on server side and views generated on client side exactly the same ?

+ : Problem

Many possibilities

+ : Problem

 

+ : the constraint

Our solution :
 
How do you render that {...} ?
Here it is : { $view: '...' } !

+ : summary

Front-End V1Front-End V2
  • use interactions
  • AJAX
  • HTML injections
  • animations
  • user actions
  • animations
  • business logic
  • rendering logic
  • placement
  • navigation inside the resume
  • information tips
  • limitation tips

Thanks