第六天(Controllers)

原文地址:http://docs.sencha.com/touch/2.2.0/#!/guide/controllers


Controllers
控制器

Controllers are responsible for responding to events that occur within your app. If your app contains a Logout button that your user can tap on, a Controller listens to the Button's tap event and takes the appropriate action. While View classes handles the display of data and Model classes handle the loading and saving of data, Controller classes are the glue that binds Views and Models together.

控制器的责任是对app中触发的事件进行响应。如果你的app包含一个用户可点击的Logout的按钮,一个控制器监听该按钮的点击事件并采取适当的行为。当视图进行数据展现、模型处理加载和保存数据(这里所谓的加载和保存其实是store-数据存储的实体类)时,控制器是将视图和模型绑定在一起的纽带。

Relation to Ext.app.Application

与Ext.app.Application的关系

Controllers exist within the context of an Application. An Application usually consists of a number of Controllers, each of which handle a specific part of the app. For example, an Application that handles the orders for an online shopping site might have controllers for Orders, Customers, and Products.

控制器存在于Application的上下文中。一个应用程序通常包含一系列的控制器,每一个控制器处理应用中具体的一部分。例如,一个处理网上购物的应用可能包含订单控制器、客户控制器及产品控制器。

All Controllers used by an Application are specified in the Application's Ext.app.Application.controllers config. The Application automatically instantiates each Controller and keeps references to each of them, so in most cases you do not need to instantiate Controllers directly. By convention each Controller is named after the enitity (usually the Model) that it manages, usually in the plural - for example, if your app is called 'MyApp' and you have a Controller that manages Products, the convention is to create a MyApp.controller.Products class in the file app/controller/Products.js.

一个应用中的所有控制器都会在应用的Ext.app.Application.controllers配置中指定。应用自动实例化每一个控制器,并保持对他们的引用,因此大部分情况下,你不需要直接实例化它们。按照惯例,每一个控制器是以它所控制的实体(通常是模型)的复数形式命名-例如,如果你的app叫MyApp,并且你有一个控制产品的控制器,按照惯例,需要在app/controller/Products.js文件中创建一个叫MyApp.controller.Products的控制器类。

Launching

启动

There are four main phases in your Application's launch process, two of which pertain to a Controller. First, each Controller is able to define an init function, which is called before the Application launch function. Second, after the Application and Profile launch functions have been called, as the last phase of the process the Controller's launch function is called, as follows:

应用启动过程中有四个主要阶段,其中两个隶属于控制器。首先,每一个控制器可以定义一个init函数,该函数在Application的launch函数调用之前调用。其次,在Application和Profile的launch函数调用后,控制器的launch方法被调用,这是最后一阶段,如下:

  1. Controller#init functions called  控制器的init方法被调用
  2. Profile#launch function called   设备配置的launch方法被调用
  3. Application#launch function called   Application的launch方法被调用
  4. Controller#launch functions called   控制器的launch方法被调用

Most of the time your Controller-specific launch logic should go into your Controller's launch function. Because the controller's launch function is called after the Application and Profile launch functions, your app's initial UI is expected to be in place at this point. If you need to do some Controller-specific processing before app launch, you can implement a Controller init function.

大部分情况下,控制器的详细启动逻辑应该放在控制器的launch方法里。因为控制器的launch方法是在Application、Profile(设备配置)的launch方法调用之后,此时,应用的初始化ui界面需要准备就绪。如果你想在app启动之前做一些控制逻辑,你可以实现控制器的init方法。

Refs and Control

引用和控制

The centerpiece of Controllers are the twin configurations refs and control. These are used to gain references to Components inside your app and to take action on them, based on the events that they fire. In the following sections we first look at the refs config.

控制器的核心是引用和控制这两项配置。它们是用来获取组件引用并对其触发的事件采取行动。下面的章节我们先看引用配置。

Refs

引用

Refs leverage the powerful ComponentQuery syntax to easily locate Components on your page. For each Controller we can define as many refs as required, for example in the following example we define a ref called 'nav' that finds on the page a Component with the 'mainNav' ID. We then use that refs in the subsequent addLogoutButton function, as shown in this sample:

引用得力于功能强大的 ComponentQuery语法来定位页面上的组件。每一个控制器,我们可以根据需要定义任意数量的引用,例如在下面的例子中,我们定义了一个叫‘nav’的引用,它关联页面上ID为‘mainNav’的组件。然后我们在addLogouButton方法里面用到它,如下:

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            nav: '#mainNav'
        }
    },

    addLogoutButton: function() {
        this.getNav().add({
            text: 'Logout'
        });
    }
});

Usually, a ref is just a key/value pair - the key ('nav' in this case) is the name of the reference that is to be generated, while the value ('#mainNav' in this case) is the ComponentQuery selector used to find the Component.

通常,引用只是一个key/value的键值对--键(该例中是‘nav’)是生成的引用的名字,值(‘#mainNav’)是ComponentQuery 用来查找组件的选择器。

We have then created the addLogoutButton function which uses this ref via its generated 'getNav' function. This getter function is automatically generated based on the refs you define and always follows the same format - 'get', followed by the capitalized ref name. In this case we are treating the nav reference as a Toolbar, and we add a Logout button to it when our function is called. This ref would recognize a Toolbar such as the following:

然后我们创建了一个addLogountButton的函数,在该函数中,我们用到了该引用所生成的'getNav'函数。这个getter方法是基于你定义的引用自动生成的,并且遵从一样的格式--‘get’,紧跟首字母大写的引用名。这个例子中,nav引用的是一个Toolbar,当我们的函数调用时,向该toolbar中添加一个Logout按钮。这个引用会识别如下的Toolbar:

Ext.create('Ext.Toolbar', {
    id: 'mainNav',

    items: [
        {
            text: 'Some Button'
        }
    ]
});

Assuming that this Toolbar has already been created by the time we run the 'addLogoutButton' function (we will see later how that is invoked), it will get the second button added to it.

假设我们运行addlogoutButton方法的时候该Toolbar已经被创建(稍后我们可以看到是如何调用的),它会向其中添加第二个按钮。

Advanced Refs

引用进阶

Refs can also be passed additional options, beyond name and selector. These options are autoCreate and xtype, which are almost always used together:

引用也可以传入名字和选择器之外的功能。这些功能通常都是autoCreate和xtype同时使用的:

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            nav: '#mainNav',

            infoPanel: {
                selector: 'tabpanel panel[name=fish] infopanel',
                xtype: 'infopanel',
                autoCreate: true
            }
        }
    }
});

We have added a second ref to our Controller. Again the name is the key, 'infoPanel' in this case, but this time we have passed an object as the value. This time we have used a more complex selector query - imagine that your app contains a tab panel and that one of the items in the tab panel is named 'fish'. The previously specified selector matches any Component with the 'infopanel' xtype inside the tab panel item.

我们向控制器添加了第二个引用。同样,名字是键,该例中是‘infoPanel’,但是这一次,我们传入了一个对象作为值。这一次我们使用了一个更复杂的选择器 -- 设想你的应用包含一个选项卡面板,并且选项卡面板的一项叫做‘fish’。前面指定的选择器匹配任何选项卡面板子项的xtype为‘infopanel’的组件。

The difference here is that, if infopanel does not exist already inside the 'fish' panel, it will be automatically created when you run this.getInfoPanel inside your Controller. The Controller is able to create the infopanel Component because we provided the xtype for instantiating it, in case the selector did not return anything.

这里的差别是,如果在‘fish’面板中不存在infopanel,当你运行该控制器的getInfoPanel方法时,它会自动被创建。控制器之所以能够创建infopanel,是因为当该选择器不能返回任何组件时我们为实例化该组件提供了一个xtype值。

Control

控制

The related config to refs is controlControl is the means by which your Controller listens to events fired by app Components and reacts in some way. The Control config accepts both ComponentQuery selectors and refs as its keys, and listener objects as values, as shown in the following example:

与饮用关联的配置是控制。控制是指控制器监听你的app组件所触发的事件并以某种方式采取行动。控制配置接受 ComponentQuery选择器和引用作为键,监听器对象作为值,如下例子所示:

Ext.define('MyApp.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        control: {
            loginButton: {
                tap: 'doLogin'
            },
            'button[action=logout]': {
                tap: 'doLogout'
            }
        },

        refs: {
            loginButton: 'button[action=login]'
        }
    },

    doLogin: function() {
        // called whenever the Login button is tapped
    },

    doLogout: function() {
        // called whenever any Button with action=logout is tapped
    }
});

In the previous example we have set up two control declarations - one for the loginButton ref and the other for any Button that has been given the 'logout' action. For each declaration we passed in a single event handler - in each case listening for the 'tap' event - and specified the action that should be called when that Button fires the tap event. Note that we specified both the 'doLogin' and 'doLogout' methods as strings inside the control block - this is important.

在上年的例子中,我们创建了两个控制的声明 -- 一个是loginButton的引用,一个是赋予了logout行为的按钮。每一个声明,我们都传递了一个时间处理器 -- 每一个都是监听点击事件,并指定了当按钮触发点击事件时应该采取的行动。注意,我们在control块中是以字符串‘doLogin’、‘doLogout’指定方法的,这很重要。

In each control declaration, you can listen to as many events as you like, and mix and match ComponentQuery selectors and refs as the keys.

在每一个控制声明中,你可以根据需要监听多个事件,并且可以混用引用和ComponentQuery选择器作为键。

Routes

路由

As of Sencha Touch 2, Controllers can directly specify which routes they are interested in. This enables us to provide history support within our app, as well as the ability to deeply link to any part of the application that we provide a route for.

自st2起,控制器能够直接指定自己感兴趣的路由。这是我们能够为app提供历史功能,也能够深度连接到我们提供的路由所指的部分。

For example, let us assume that we have a Controller responsible for logging in and viewing user profiles, and want to make those screens accessible via urls. We could achieve that as follows:

例如,假设我们有一个负责登录和查看用户身份的控制器,我们想将那些界面做成可访问的url。我们能够像下面这样来实现:

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

    config: {
        routes: {
            'login': 'showLogin',
            'user/:id': 'showUserById'
        },

        refs: {
            main: '#mainTabPanel'
        }
    },

    // uses our 'main' ref above to add a loginpanel to our main TabPanel (note that
    // 'loginpanel' is a custom xtype created for this application)
    showLogin: function() {
        this.getMain().add({
            xtype: 'loginpanel'
        });
    },

    // Loads the User then adds a 'userprofile' view to the main TabPanel
    showUserById: function(id) {
        MyApp.model.User.load(id, {
            scope: this,
            success: function(user) {
                this.getMain().add({
                    xtype: 'userprofile',
                    user: user
                });
            }
        });
    }
});

The routes specified previously map the contents of the browser address bar to a Controller function that is called when the route is matched. The routes can be simple text like the login route, which matches against http://myapp.com/#login, or may contain wildcards such as the 'user/:id' route, which matches urls like http://myapp.com/#user/123. Whenever the address changes, the Controller automatically calls the specified function.

路由事先指定了浏览器地址栏中的地址与控制器一个方法间的映射关系,当浏览器地址匹配该路由时,相应的方法会被调用。路由可以是简单的文本类型,像登录路由样,与http://myapp.com/#login相匹配,或者与包含有通配符 'user/:id' 例如http://myapp.com/#user/123相匹配。当地址改变时,控制器会自动调用指定的方法。

Note that the showUserById function first has to load the User instance. Whenever you use a route, the function that is called by that route is completely responsible for loading its data and restoring the state. This is because a user could either send that url to another person or could simply refresh the page, which wipes any cached data that you had already loaded. In the application architecture guides you can find a more thorough discussion on restoring state with routes.

注意,showUserById 方法首先需要加载User实例。无论什么时候你使用路由,被该路由调用的方法都会加载数据并保存状态。因为一个用户既可以将该url发给另一个用户,也可以刷新当前页面,这都会导致重写已经载入的缓存数据。在应用程序结构向导中,你能够找到关于通过路由恢复状态的全面信息。

Before Filters

前置过滤器

An additional functionality that Controllers provide within the context of Routing is the ability to define filter functions that are run before the function specified in the route. These are an excellent place to authenticate or authorize users for specific actions, or to load classes that are not yet on the page. For example, let us say we want to authenticate a user before allowing him to edit a Product in an e-commerce backend application:

控制器通过路由提供的一个额外功能是定义过滤方法,过滤方法在路由指定的方法运行之前运行。这是对特定行为验证用户合法性或者加载页面上不存在的类的一个不错位置。例如,在一个电子商务后台程序中,一个用户修改产品之前我们要验证他的身份:

Ext.define('MyApp.controller.Products', {
    config: {
        before: {
            editProduct: 'authenticate'
        },

        routes: {
            'product/edit/:id': 'editProduct'
        }
    },

    // this is not directly because our before filter is called first
    editProduct: function() {
        //... performs the product editing logic
    },

    // this is run before editProduct
    authenticate: function(action) {
        MyApp.authenticate({
            success: function() {
                action.resume();
            },
            failure: function() {
                Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in");
            }
        });
    }
});

Whenever the user navigates to an URL such as http://myapp.com/#product/edit/123, the Controller's authenticate function is called and is passed theExt.app.Action that would have been executed if the before filter did not exist. An Action simply represents the Controller function (editProduct in this case) and other data, such as the ID parsed from the url.

一个用户无论在什么时候定位到url:http://myapp.com/#product/edit/123时,控制器的authenticate方法都会被调用,如果前置过滤器不存在,会马上调用Ext.app.Action(没大懂)。一个Action仅仅代表控制器的方法(这个例子中是editProduct)和其他数据,如从uril里传入的ID。

The filter can perform any kind of processing it needs to, either synchronously or asynchronously. In this case we are using our application's authenticatefunction to verify that the user is currently logged in. Since this could entail an AJAX request to check the user's credentials on the server, it runs asynchronously - if the authentication was successful, we continue the action by calling action.resume(), if not we tell the user that he needs to log in first.

过滤器可以根据需要进行任何处理,同步的或异步的。这个例子中,我们是用authenticate方法判断当前用户是否登录。这可以向服务器端发起一个异步的ajax请求,验证用户的凭证 --如果验证通过,我们通过调用action.resume()继续往下走,如果验证不通过,告诉用户他需要先登录。

Before filters can also be used to load additional classes before certain actions are performed. For example, if some actions are rarely used, you may wish to defer loading of their source code until they are needed, so that the application boots up faster. To achieve this you can simply set up a filter that usesExt.Loader to load code on demand.

前置过滤器也可以在采取行动之前加载额外的一些类。例如,如果一些actions很少用,你可能想在需要它们时再加载,这样能够使应用运行的更流畅。为了达到该目的,你可以用Ext.loader加载需要的代码来构建一个过滤器。

Any number of before filters can be specified for each action, to use more than one filter simply pass in an array:

每一个action可以配置任意数量的过滤器,通过传递一个数组来使用多个过滤器:

Ext.define('MyApp.controller.Products', {
    config: {
        before: {
            editProduct: ['authenticate', 'ensureLoaded']
        },

        routes: {
            'product/edit/:id': 'editProduct'
        }
    },

    // this is not directly because our before filter is called first
    editProduct: function() {
        //... performs the product editing logic
    },

    // this is the first filter that is called
    authenticate: function(action) {
        MyApp.authenticate({
            success: function() {
                action.resume();
            },
            failure: function() {
                Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in");
            }
        });
    },

    // this is the second filter that is called
    ensureLoaded: function(action) {
        Ext.require(['MyApp.custom.Class', 'MyApp.another.Class'], function() {
            action.resume();
        });
    }
});

The filters are called in order, and each must call action.resume() to continue the processing.

过滤器会按顺序执行,为了将处理进行下去,每一个都必须调用action.resume()方法。

Profile-Specific Controllers

特定设备类型的控制器

Superclass, shared stuff:

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

    config: {
        routes: {
            'login': 'showLogin'
        },

        refs: {
            loginPanel: {
                selector: 'loginpanel',
                xtype: 'loginpanel',
                autoCreate: true
            }
        },

        control: {
            'logoutbutton': {
                tap: 'logout'
            }
        }
    },

    logout: function() {
        // code to close the user's session
    }
});

Phone Controller:

手机版控制器:

Ext.define('MyApp.controller.phone.Users', {
    extend: 'MypApp.controller.Users',

    config: {
        refs: {
            nav: '#mainNav'
        }
    },

    showLogin: function() {
        this.getNav().setActiveItem(this.getLoginPanel());
    }
});

Tablet Controller:

Ext.define('MyApp.controller.tablet.Users', {
    extend: 'MyApp.controller.Users',

    showLogin: function() {
        this.getLoginPanel().show();
    }
});
晚上总是弄得太晚了,无奈,只得白天补上了!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值