ExtJs 4 и платформа Bars.B4

Радик Фаттахов

ведущий разработчик, БАРС Груп, БЦ ЖКХ

План

Система классов

JavaScript + OOP = ExtJs 4

Правила именования классов

Правила именования файлов

Система классов

Методы и переменные

CamelCase - наше все

Статические константы

Система классов

Объявление классов

Было:

Ext.ns('My.cool');
My.cool.Window = Ext.extend(Ext.Window, { ... });

Стало:

Ext.define(className, members, onClassCreated);

Система классов

Пример


Ext.define('My.sample.Person', {
    name: 'Unknown',

    constructor: function(name) {
        if (name) {
            this.name = name;
        }
    },

    eat: function(foodType) {
        alert(this.name + " is eating: " + foodType);
    }
});

var aaron = Ext.create('My.sample.Person', 'Aaron');
    aaron.eat("Salad"); // alert("Aaron is eating: Salad");

MVC Архитектура на клиенте

MVC Архитектура на клиенте

Структура проекта

<html>
<head>
    <title>Account Manager</title>

    <link rel="stylesheet" type="text/css" href="ext-4/resources/css/ext-all.css">

    <script type="text/javascript" src="ext-4/ext-debug.js"></script>

    <script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>

MVC Архитектура на клиенте

app.js

Ext.application({
    requires: ['Ext.container.Viewport'],
    name: 'AM',

    appFolder: 'app',

    launch: function() {
        Ext.create('Ext.container.Viewport', {
            layout: 'fit',
            items: [
                {
                    xtype: 'panel',
                    title: 'Users',
                    html : 'List of users will go here'
                }
            ]
        });
    }
});

MVC Архитектура на клиенте

Controller

Ext.define('AM.controller.Users', {
    extend: 'Ext.app.Controller',

    init: function() {
        this.control({
            'viewport > panel': {
                render: this.onPanelRendered
            }
        });
    },

    onPanelRendered: function() {
        console.log('The panel was rendered');
    }
});

MVC Архитектура на клиенте

View

Ext.define('AM.view.user.List' ,{
    extend: 'Ext.grid.Panel',
    alias: 'widget.userlist',

    title: 'All Users',

    initComponent: function() {
        this.store = {
            fields: ['name', 'email'],
            data  : [
                {name: 'Ed',    email: 'ed@sencha.com'},
                {name: 'Tommy', email: 'tommy@sencha.com'}
            ]
        };

        this.columns = [
            {header: 'Name',  dataIndex: 'name',  flex: 1},
            {header: 'Email', dataIndex: 'email', flex: 1}
        ];

        this.callParent(arguments);
    }
});

MVC Архитектура на клиенте

View

MVC Архитектура на клиенте

Model

Ext.define('AM.model.User', {
    extend: 'Ext.data.Model',
    fields: ['name', 'email']
});
Ext.define('AM.store.Users', {
    extend: 'Ext.data.Store',
    model: 'AM.model.User',
    autoLoad: true,

    proxy: {
        type: 'ajax',
        url: 'data/users.json',
        reader: {
            type: 'json',
            root: 'users',
            successProperty: 'success'
        }
    }
});

ExtJs 4 - Mixins

Ext.define('CanSing', {
     sing: function() {
         alert("I'm on the highway to hell...")
     }
});
Ext.define('Musician', {
     mixins: ['CanSing'],

    sing: function() {
        // делегируем операцию singing mixin-у
        this.mixins.canSing.sing.call(this);
    }
})

Bars.B4 - ExtJs

Overrides

В Bars.B4 - всего 3 override, 2 из них исправляют баги.

Bars.B4 - ExtJs4

Aspects

Ext.define('GridActs', {
   ...
   alias: 'widget.gridacts',
   gridSelector: null // селектор, который будет использоваться для получения grid'a
   ...
});

Bars.B4 - ExtJs4

Aspects

Ext.define('MyController', {
    extend: 'B4.base.Controller',
    aspects: [
       {
          xtype: 'gridacts',
          gridSelector: 'mygrid' // конкретный селектор
       }
    ]
});

Bars.B4 - ExtJs4

Routing

Создать контроллер унаследовав его от B4.base.Controller и добавить ему миксин

Ext.define('MyApp.MyController', {
    extend: 'B4.base.Controller',

    mixins: {
        context: 'B4.mixins.Context'
    }
});

Bars.B4 - ExtJs4

Routing

Определить конфигурацию для роутов:

Ext.define('MyApp.MyController', {
    extend: 'B4.base.Controller',

    mixins: {
        context: 'B4.mixins.Context'
    },

    config: {
        routes:[
            {
                route: 'contractor/{id}/', //регулярное выражение
                fn: 'index' // имя функции
            }
        ]
    }
});

Bars.B4 - ExtJs4

Routing

Определить функции, соответствующие роутам:

Ext.define('MyApp.MyController', {
    ... все то же самое, как выше
    index: function(id){ // функция в качестве параметра получит значение из url
                         // например, если открыли url http:///#contractor/1/ 
                         // то функция index при вызове получит в качестве параметра 1
        // Если нам нужно добавить компоненту в главный контейнер
        // 1. Получаем компоненту, например, так:
        var view = this.getMainPanel() || Ext.widget('contractor_nav_panel',
            {
                title: 'Контрагент ' + id
            });
        // ВАЖНО!
        this.bindContext(view); // 2. привязываем компоненту к текущему контексту
        this.application.deployView(view); // 3. деплоим ее через application (подробнее о деплое ниже)
    }
});

Bars.B4 - ExtJs4

Routing - Deploy

По умолчанию при вызове this.application.deployView(view) метод defaultDeploy из PortalController.

Если вы хотите, чтобы ваш контроллер имел возможность добавлять компоненту в пользовательский интерфейс и контекст приложения при этом нормально работал, надо сделать следующее:

Добавить в контроллер объект deployViewKeys:

Ext.define('MyApp.MyController', {
    ...
    deployViewKeys:{
        'deployViewItem': 'deployViewItemFn'
    }
});

Здесь 'deployViewItem' - ключ, который будет передаваться в качестве второго аргумента this.application.deployView(view, 'deployViewItem'), 'deployViewItemFn' - имя функции, которая будет ответственна за добавление компоненты. Ее тоже необходимо определить самому.

В качестве примера можно посмотреть на реализацию defaultDeploy из PortalController

Routing - IClientRouteMapRegistrar

namespace Bars.Gkh.Overhaul
{
    using Bars.B4;

    public class ClientRouteMapRegistrar : IClientRouteMapRegistrar
    {
        public void RegisterRoutes(ClientRouteMap map)
        {
            map.AddRoute(new ClientRoute("workprice/", "B4.controller.dict.WorkPrice"));
            map.AddRoute(new ClientRoute("commonestateobject_create/", "B4.controller.CommonEstateObject", "create"));
            map.AddRoute(new ClientRoute("commonestateobject_edit/{id}/", "B4.controller.CommonEstateObject", "edit"));
        }
    }
}

Production vs Development

В Development:
В Production:

Permissions

Реализовано в виде Aspect.

    {
        xtype: 'permissionaspect',
        applyBy: function (component, allowed) {
            if (allowed) {
                component.enable();
            } else {
                component.disable();
            }
        }
        applyOn: {  event: 'show', selector: '#meetingActionWindow' },
        permissions: [
            { name: 'TM.Protokol.View', applyTo: '#meetingActionWindow #btnProtocol' },
            { name: 'TM.Analytic.View', applyTo: '#meetingActionWindow #btnAnalytics' }
        ]
    }
    

/

#