Ext JS Data Package
2016-08-19 17:57
375 查看
Data package让我们可以在我们的代码或者application中加载和保存数据。最重要的一点是,data package可以让我们链接或者绑定数据到Ext JS组件。data apckage是由多个类组成,其中最重要的三个类为
Ext.data.Model
Store
Ext.data.proxy.Proxy
每个application差不多都会使用上面的三个类, 它们受到很多卫星类(围绕它们)的支持, 如下图所示
Ext JS提供了一个单例的对像Ext.Ajax, 我们可以通过它,向服务器发送请求
以上的请求是异步的,它不会阻止下一行代码的执行,你也可以运行同步的Ajax请求
更多Ajax的配置可以查看http://docs.sencha.com/extjs/6.0.2-classic/Ext.Ajax.html#cfg-async
在上面的代码中,我没有处理服务器响应,我们需要在ajax配置中,配置一个callback函数, 这个函数在服务器响应时执行,同时还有 success or failure。
output
success会在服务器响应状态为200-299时执行,表示请求成功。如果响应为403, 404, 500, 503, 则执行failure方法。
success or failure都会接收两个参数,第一个参数是服务器响应对像, 通过它可以获得响应文本和响应头。第二个参数是我们对发起的Ajax请求的配置。在我们的例子中,options为URL, success, failure三个属性组成。
callback 函数将会一直执行,而不管是failure或者success. 并且这个函数接受三个参数: options 是请求时的配置, success是一个布尔值,如果请求成功,则为true, 否则为false. response参数是一个XMLHttpRequest对像,包含了响应的信息。
假设我们获得的响应为
对于success 来说, 响应返回的是纯文本的数据,我们需要将它解码为JSON数据
或者
如果我们请求的是一个xml资源,而不是JSON
则代码为
现在我们知道如何获取数据,但我们还需要一种方法来处理数据。 Ext JS为我们提供了一个简单的方法来管理数据。
以下是构成模型的几个部分
Fields 数据的名称,类型和值
Proxies 用来persist data或pull data
Validations 检验数据的有效性
Associations 与其它model的关系
上面我们讲解了base model的重要性,接下来看看如何一步步的创建一个model
在第一步,我们让它继承于Ext.data.Model. 这类为所有的model提供了所有的功能。
第二步定义的属性每一条记录的ID, 在这里,我们使用的是clientId字段,如果我们在fields中没有定义clientId, model将会自动为我们默认产生一个id属性。
在第三步,我们为我们的模型定义了字段,这个属性是一个数组,数组中的每一个元素是一个对像,它包含了对每一个字段的配置。 在这里,我们只是设置了字段的名字和类型,在最后一个字段中,我们设置了一个dateFormat属性。
字段的更多配置可以查看Ext.data.field文档
数据的类型,包含以下几中
String
Integer
Float
Boolean
Date (使用此类型时,记得使用dateFormat属性,以确保解析正确的日期值)
Auto (表示对接受到的数据,不进行转换)
根据定义的model,我们创建一个数据
创建完一条数据记录后,我们可以使用get和set方法,读写这个记录每个字段的值
设置方法可以是单个的值,也是传递一个对像,同时设置多个值
其实所有的数据都是保存存在data属性上,我们应该使用get 和 set方法来读写数据,但因为某些原因,我们需要访问所有的数据时,我们可以使用这个data对像
虽然可以通过这种方法设置,但这不是最佳实践,因为能过setting方法设置时,会执行更多的任务,比如,标识我们的模型为脏数据,保存上一个值,在此之后,我们可以拒绝或者接收改变,还有一个其它的重要步骤。
比如上面有一个”x0001”的字段,可以它在client模型中的名称为contractFileName, 所以我们需要使用mapping属性。
Validators是一个对像,这个对像的每个键对应于要检验的字段名。每个字段的检验规则,可以为一个对像配置,或者这些配置组成的数组。在这个例子中,我们是验证name字段,它的长度为7个字符以上,它的值不能为 “Bender”.
有的验证规则,接收额外的配置 - 比如 length校验器,可以有min 和 max属性。 format 可以一个matcher等等。 以下是Ext JS内置的检验器,并且还可以自定义规则
Presence - 保证这个字段必须有值,空字符串无效
Length - 保证字符串的长度在min和max之前。min 和 max这两个约束条件是可选的
Format - 保证字符串匹配一个正则表达式format.
Inclusion - 保证一个值必须在指定的列表中,比如,gender只能为 male或者 female
Exclusion - 保证一个值不能出现在指定的列表中
更多的检验器,可以查看Ext.data.validator空间下的子类, 了解如何传递参数, 比如Presence, http://docs.sencha.com/extjs/6.0.2-classic/Ext.data.validator.Presence.html
接下来,让我们创建一个违返这些规则的一条记录
在上面的代码中,最关键的函数是getValidation(), 它运行所有的检验规则,但只返回每一个字段,第一条没有通过的规则。这些检验记录是被懒性创建,只在需要的时候才会更新。在这里,错误为Length must be greater than 7.
当我们提供的名字超过规定的长度时
这条记录符合所有的检验,它包含7个字符,同时也不跟列表中的Bender匹配。
newUser.isValid()将会返回true, 当我们调用getValidation(), 新的检验记录将被更新,数据不在是脏数据。它所有的字段都将设置为true.
以下是另一个例子
在第5步,我们使用了validate方法,它将返回一个验证失败的集合。并且输出字段以及错误信息. 这个集合的类型为Ext.data.ErrorCollection, 它扩展于Ext.util.MixedCollection. 因此我们可以使用each方法来遍历每一条错误记录.
继承于Ext.data.field.String
我们为这个字段类型,定义了一个别名。这个别名不能重复,或者说覆盖一个已经存在于Ext.data.field中的子类
为这个字段类型设置校验器
在这里,我们使用的是
Employees for contact(Client联系的员工, 包含name, title, gender, email, phone, cell phone, 等字段)
Services (service ID, service name, service price, branch where service provided)
在Ext JS中支持one-to-many, one-to-one, and many-to-many关联
关于更多Association Type,可以查看http://docs.sencha.com/extjs/6.2.0-classic/Ext.data.schema.Association.html
使用了hasManay属性,我们定义了一个one-to-many的关系。hasMany属性可以是一个对像数组,每一个对像都包含一个model属性. 表明一个Client实例,可以有多个 Myapp.model.Employee对像。
此外,我们可以在创建Client类时,定义获得与它相关联model数据的函数名称。在这里,我们使用employees; 如果我们没有定义任何的函数名称, Ext JS将使用子模型的名字,加上”s”, 作为函数名称
现在我们创建一个Employee类,它位于appcode/model/Employee.js
为了测试model之前的关系,我们创建以下代码
我们创建一个Client的记录
我们执行employees方法,这个方法是在定义关联类时,指定的name属性。这个方法返回一个Ext.data.Store实例。它是一个集合,用来管理相应的模型。所以我们可以通过它的add方法,添加两个Employee对像。
我们遍历myclient中的employees集合。每个记录表示一个Employee对像,所以我们可以使用get方法,获得它每个字段的值
如果你所看到的,这是一个正常的类。现在我们来定义Customer类
我们添加了一个字段,称为contractInfo, 但我们使用reference代替了type属性。它表示contractInfo的值为一个Contract类的实例。
我们在这里是直接为contractInfo赋值一个对像. 如下所示
你看到上图中contractInfo是一个对像,它的字段跟Contract模型中定义的字段相同。如果contractInfo对像中没有定义Contract中的字段,则整个contractInfo则为undefined.
通过以上面的方式,可以很容易的实现我们应用中不同Model之前的关系。每一个Model可以跟其它的Model产生关联。同时,模型定义的顺序可以是任意的。一旦,你创建了这个Model类型的记录, 通过这条记录,就可以遍历与之相关联的数据。如果你想要获得一个用户的所有post. 你可以通过以下的代码
每一个User model都有多个Posts, 它会被添加一个user.posts()函数,调用user.posts()返回一个Post model的Store集合.
不仅我们可以加载数据,还可以创建一条新的记录
上面的代码会创建一个Post实例,并且自动赋值userId字段为用户的id. 调用sync(),通过post的代理,保存这个新的Post(schema的代理配置决定). 这是一个异步操作,如果你需要获得操作完成后的通知,可以给它传递一个回调函数。
相返的,在Post model中,也会生成一个新的方法
在加载函数中, getUser()是一个异步操作,所以需要一个回调函数,来获得用户的实例。setUser()方法只是简单的更新userId(有时称为 “外键”)为100,并且保存Post model. 通常也可以传递一个回调函数,在保存操作完成后,触发回调函数,知道操作是否成功.
加载嵌套的数据
当定义了associations后,可以在单个请求中,加载相关联系的记录。比如, 一个服务器响应如下数据
框架可以自动解析这个当个响应中的嵌套数据。而不用创建两个请求。
一个store经常被添加到widgets/componens,用来显示数据。比如组件中的grid, tree, combo box, 或者data view,都会使用一个store来管理数据。当我们创建一个自定义 widget,我们也应该使用一个store来管理数据。
为了创建一个store, 我们需要使用Ext.data.Store类。代码如下
为了定义一个store, 我们需要让它继承于Ext.data.Store, 这个类负责处理models.
我们需要为创建的store,关联一个模型。它必须为一个有效的model类。
当我们创建了一个store类后,可以使用这个store来存取数据
也可以使用以下的方法
如果想一次添加多个,我们可以传递一个数组给 add方法。
store使用add方法,会将新的元素添加到集合的最后位置,如果我们想添加一个元素到第一的位置,或才其它位置,则可以使用
each方法接受一个函数。这个函数将被store中的每一条记录执行。这个匿名函数接收两个参数,每一次遍历时的record和index.
我们也可以给each方法传递第二个参数,表明这个匿名函数的作用域。
如果我们想获得指定位置的model, 我们可以使用getAt方法
First and last records
* By range*
By ID
在上面的代码中,remove传递的参数是一个model引用。
我们也可以一次性删除多个记录。我们只需要把要删除的model数组传递给它
有时,我们没有model的引用,在这种情况下,我们可以通过记录的位置进行删除
如果我们想要删除所有的记录, 我们仅需要调用removeAll方法.
Ext JS使用proxies发送和接收数据。proxies通常配置在store或者model。也可以像我们在最开始的base model代码,在schema中定义.
在Ext JS中Proxies负现处理model的数据,我们可以说proxy是一个处理和操作数据(parsing, organizing等等)的类. 所以store可以通过proxie来读取和保存,或者发送数据到服务器。
一个proxy使用一个reader来解码接收到的数据,使用一个writer将数据编码为正确的格式,并且发送到数据源。reader有Array, JSON, XML三种类型,而writer只有JSON, XML类型。
proxies主要分为两种类型, Client 和 Server,如果我们想要改变我们数据源,我们仅需要改变proxy类型,而其它的都不需要改变。比如,我们为store或model定义一个Ajax proxy, 然后我们可以将它设置为 local storage proxy.
Client Proxy
Memory
Local Storage
Server Proxy
AJAX
JSONP
REST
之后我们就可以创建这个store, 并且从远程加载数据
在我们执行例子之前,我们应该在服务器上创建一个serverside/customers.json
如我们上面的例子
type属性表示使用什么格式来解析。它可以为json, xml, array.
rootProperty允许我们定义一个在服务器响应中的属性名字,服务器返回的数据都是这个字段下。在JSON响应中,它可以为一个数组。 在我们的例子中,我们设为records, 因为我们的JSON使用的是这个名字。如果我们嵌套的是一个对像,可以使用如下的方式
rootProperty 定义了 XML文件中查找records的节点。
我们还添加了一个record属性,在XML中每一个记录的名称
totalProperty和successProperty, store有的功能需要使用到这个标签下的值
现在,让我们创建一个XML文件 serverside/customers.xml
设置了一个api 属性: 为CRUD分别设置一个url
设置一个writer属性
type: ‘json’: 以JSON格式发送数据
encode: 表示在传递数据到服务器前,是否经过Ext JS进行编码
rootProperty: 包信息的属性的名字
writeAllFields: 传递所有的记录到服务器,如果为false, 则只发送个改的字段。
对CRUD所对应的方法类型
当一个entity指定了一个schema, 相关联的类,就能够查找(or create)这个entity类的实例,然后继承这个实例。
Importnat: 所有相关的entities 必须属性单个schema实例,这样才能正确的连接它们的关联性。
通过使用base class, 你所有的model类在声明创建时,都已要有拥有schema中的默认配置。
entityName - “User”和”Group”为
associationName - 当我们谈论关系时,特别是many-to-many. 给它们命名就非常重要。Associations(关联)不被涉级的任何一个实体拥有, 所以这时,这个名称就类似于一个entityName. 默认的associationName为”GroupUsers”.
left and right - Association(关联)描述了两个实例之前的关系。在谈论具体的关系时,我们可以使用两个参与者的entityName(比如User or Group). 当谈论一个抽像的associations
比如”GroupUsers”这个关系,”User”称为 left, 而”Group”称为right. 在一个many-to-many的关联时, 我们可以任意的选择left和right. 当涉及到外键时,left 则是被包含的外键
类似于alias创建一个类,如下所示
Ext.data.Model
Store
Ext.data.proxy.Proxy
每个application差不多都会使用上面的三个类, 它们受到很多卫星类(围绕它们)的支持, 如下图所示
Ajax
在学习data package 之前,我们需要知道如何向服务器发送Ajax请求。Ext JS提供了一个单例的对像Ext.Ajax, 我们可以通过它,向服务器发送请求
Ext.Ajax.request({ url:"serverside/myfirstdata.json" }); console.log("Next lines of code...");
以上的请求是异步的,它不会阻止下一行代码的执行,你也可以运行同步的Ajax请求
Ext.Aajx.async = false
更多Ajax的配置可以查看http://docs.sencha.com/extjs/6.0.2-classic/Ext.Ajax.html#cfg-async
在上面的代码中,我没有处理服务器响应,我们需要在ajax配置中,配置一个callback函数, 这个函数在服务器响应时执行,同时还有 success or failure。
Ext.Ajax.request({ url:"serverside/myfirstdata.json", success: function(response,options){ console.log('success function executed, here we can do some stuff !'); }, failure: function(response,options){ Ext.Msg.alert("Message", 'server-side failure with status code ' + response.status); }, callback: function( options, success, response ){ console.log('Callback executed, we can do some stuff !'); } });
output
success function executed, here we can do some stuff ! Callback executed, we can do some stuff !
success会在服务器响应状态为200-299时执行,表示请求成功。如果响应为403, 404, 500, 503, 则执行failure方法。
success or failure都会接收两个参数,第一个参数是服务器响应对像, 通过它可以获得响应文本和响应头。第二个参数是我们对发起的Ajax请求的配置。在我们的例子中,options为URL, success, failure三个属性组成。
callback 函数将会一直执行,而不管是failure或者success. 并且这个函数接受三个参数: options 是请求时的配置, success是一个布尔值,如果请求成功,则为true, 否则为false. response参数是一个XMLHttpRequest对像,包含了响应的信息。
假设我们获得的响应为
{ "success": true, "msg": "This is a success message..!" }
对于success 来说, 响应返回的是纯文本的数据,我们需要将它解码为JSON数据
success: function(response,options){ var data = Ext.decode(response.responseText); Ext.Msg.alert("Message", data.msg); },
或者
callback: function( options, success, response ){ if(success){ var data= Ext.decode(response.responseText); Ext.Msg.alert("Message", data.msg); } }
如果我们请求的是一个xml资源,而不是JSON
<?xml version="1.0" encoding="UTF-8"?> <response success="true"> <msg>This is a success message in XML format</msg> </response>
则代码为
Ext.Ajax.request({ url:"myfirstdata.xml", success: function(response,options){ var data = response.responseXML; var node = data.getElementsByTagName("msg")[0]; Ext.Msg.alert("Message", node.firstChild.data); }, failure: function(response,options){ Ext.Msg.alert("Message", 'server-side failure with status code ' + response.status); }, callback: function( options, success, response ){ console.log('Callback executed, we can do some stuff !'); } });
Passing parameters to Ajax request
为了相应的信息,我们需要向Ajax请求中传递需要的参数,我们将使用以下的代码来传递参数//myfirstparams.php
<?php
header('Content-Type:application/json');
$output = array(
"msg"=> "Message response text using the following params:<br> x=" . $_POST["x"] . ", y=" . $_POST["y"]
);
echo json_encode($output);
//index.html
Ext.Ajax.request({
url:"myfirstparams.php",
method: "POST", //默认为GET
params: {
x: 200,
y: 300,
},
success: function(response,options){ var data = Ext.decode(response.responseText); Ext.Msg.alert("Message", data.msg); },
failure: function(response,options){
Ext.Msg.alert("Message", 'server-side failure with status code ' + response.status);
},
callback: function( options, success, response ){
console.log('Callback executed, we can do some stuff !');
}
});
Setting timeout to Ajax request calls
有时,服务器可能会长时间没有反应, 而Ext JS 默认等待响应的时间为30秒。根据需要,我们可以设置等待请求的时间Ext.Ajax.request({
url: "serverside/myfirstparams.php",
method: 'POST',
params: {x:200, y:300},
timeout: 50000,
success: function(response,options){ var data = Ext.decode(response.responseText); Ext.Msg.alert("Message", data.msg); },
failure: function(response,options){
Ext.Msg.alert("Message", 'server-side failure with status code
' + response.status);
Ext.Msg.alert("Message", 'server-side failure:' +
response.status);
}
});
现在我们知道如何获取数据,但我们还需要一种方法来处理数据。 Ext JS为我们提供了一个简单的方法来管理数据。
Models
Ext.data.Model是data package的核心。在application中, 模型表示一个实体。比如一个电子商务的app, 可能会有Users, Products, Orders等模型。简单的讲,一个模型就是定义了一组字段,以及相关的业务逻辑。以下是构成模型的几个部分
Fields 数据的名称,类型和值
Proxies 用来persist data或pull data
Validations 检验数据的有效性
Associations 与其它model的关系
Creating a Model
在创建一个model时,最好先创建一个公共的基础类。这个basic class允许我们配置所有model类的公共部分,比如它的id以及id字段的类型,同时,schema也在这个类中配置。schema用来管理所有的model, 相当于mysql中的schema数据库,而每个model相当于一张表。这个基础类如下所示Ext.Ajax.request({
url: "serverside/myfirstparams.php",
method: 'POST',
params: {x:200, y:300},
timeout: 50000,
success: function(response,options){ var data = Ext.decode(response.responseText); Ext.Msg.alert("Message", data.msg); },
failure: function(response,options){
Ext.Msg.alert("Message", 'server-side failure with status code
' + response.status);
Ext.Msg.alert("Message", 'server-side failure:' +
response.status);
}
});
上面我们讲解了base model的重要性,接下来看看如何一步步的创建一个model
Ext.define('Myapp.model.Client',{ extend:'Ext.data.Model', // step 1 idProperty:'clientId ', // step 2 fields:[// step 3 {name: 'clientId', type: 'int'}, {name: 'name' , type: 'string'}, {name: 'phone' , type: 'string'}, {name: 'website' , type: 'string'}, {name: 'status' , type: 'string'}, {name: 'clientSince', type: 'date', dateFormat:'Y-m-d H:i'} ] });
在第一步,我们让它继承于Ext.data.Model. 这类为所有的model提供了所有的功能。
第二步定义的属性每一条记录的ID, 在这里,我们使用的是clientId字段,如果我们在fields中没有定义clientId, model将会自动为我们默认产生一个id属性。
在第三步,我们为我们的模型定义了字段,这个属性是一个数组,数组中的每一个元素是一个对像,它包含了对每一个字段的配置。 在这里,我们只是设置了字段的名字和类型,在最后一个字段中,我们设置了一个dateFormat属性。
字段的更多配置可以查看Ext.data.field文档
数据的类型,包含以下几中
String
Integer
Float
Boolean
Date (使用此类型时,记得使用dateFormat属性,以确保解析正确的日期值)
Auto (表示对接受到的数据,不进行转换)
根据定义的model,我们创建一个数据
var myclient = Ext.create('Myapp.model.Client',{ clientId:10001, name:'Acme corp', phone:'+52-01-55-4444-3210', website:'www.acmecorp.com', status:'Active', clientSince:'2010-01-01 14:35' }); console.log(myclient); console.log("My client's name is = " + myclient.data.name); console.log("My client's website is = " + myclient.data.name);
创建完一条数据记录后,我们可以使用get和set方法,读写这个记录每个字段的值
// GET METHODS var nameClient = myclient.get('name'); var websiteClient = myclient.get('website'); console.log("My client's info= " + nameClient + " - " + websiteClient); // SET Methods myclient.set('phone','+52-01-55-0001-8888'); // single value console.log("My client's new phone is = " + myclient.get('phone')); myclient.set({ //Multiple values name: 'Acme Corp of AMERICA LTD.', website:'www.acmecorp.net' }); console.log("My client's name changed to = " + myclient.get("name")); console.log("My client's website changed to = " + myclient.get("website") );
设置方法可以是单个的值,也是传递一个对像,同时设置多个值
其实所有的数据都是保存存在data属性上,我们应该使用get 和 set方法来读写数据,但因为某些原因,我们需要访问所有的数据时,我们可以使用这个data对像
//READ console.log("My client's name:" + myclient.data.name); console.log("My client's website:" + myclient.data.website); // Write myclient.data.name = "Acme Corp ASIA LTD."; myclient.data.website = "www.acmecorp.biz";
虽然可以通过这种方法设置,但这不是最佳实践,因为能过setting方法设置时,会执行更多的任务,比如,标识我们的模型为脏数据,保存上一个值,在此之后,我们可以拒绝或者接收改变,还有一个其它的重要步骤。
Mappings
当在model中定义了一个字段,我们可以为这个字段定义一个mapping属性,告诉字个字段从哪获取数据。{ "success" :"true", "id":"id", "records":[ { "id": 10001, "name": "Acme corp2", "phone": "+52-01-55-4444-3210", "x0001":"acme_file.pdf" } ] }
比如上面有一个”x0001”的字段,可以它在client模型中的名称为contractFileName, 所以我们需要使用mapping属性。
Ext.define('Myapp.model.Client',{ extend: 'Ext.data.Model', idProperty: 'clientId ', fields:[ {name: 'clientId', type: 'int' }, {name: 'name' , type: 'string'}, {name: 'phone' , type: 'string'}, {name: 'contractFileName', type: 'string', mapping:'x0001'} ] });
Validations
从Ext JS 4开始,可以直接在model中对数据进行验证。Ext.define('MyApp.model.User', { extend: 'Ext.data.Model', fields: ..., validators: { name: [ //需要校验的字段 'presence', { type: 'length', min: 7 }, { type: 'exclusion', list: ['Bender'] } ] } });
Validators是一个对像,这个对像的每个键对应于要检验的字段名。每个字段的检验规则,可以为一个对像配置,或者这些配置组成的数组。在这个例子中,我们是验证name字段,它的长度为7个字符以上,它的值不能为 “Bender”.
有的验证规则,接收额外的配置 - 比如 length校验器,可以有min 和 max属性。 format 可以一个matcher等等。 以下是Ext JS内置的检验器,并且还可以自定义规则
Presence - 保证这个字段必须有值,空字符串无效
Length - 保证字符串的长度在min和max之前。min 和 max这两个约束条件是可选的
Format - 保证字符串匹配一个正则表达式format.
Inclusion - 保证一个值必须在指定的列表中,比如,gender只能为 male或者 female
Exclusion - 保证一个值不能出现在指定的列表中
更多的检验器,可以查看Ext.data.validator空间下的子类, 了解如何传递参数, 比如Presence, http://docs.sencha.com/extjs/6.0.2-classic/Ext.data.validator.Presence.html
接下来,让我们创建一个违返这些规则的一条记录
/ now lets try to create a new user with as many validation // errors as we can var newUser = new MyApp.model.User({ id: 10, name: 'Bender' }); // run some validation on the new user we just created console.log('Is User valid?', newUser.isValid()); //returns 'false' as there were validation errors var errors = newUser.getValidation(), error = errors.get('name'); console.log("Error is: " + error);
在上面的代码中,最关键的函数是getValidation(), 它运行所有的检验规则,但只返回每一个字段,第一条没有通过的规则。这些检验记录是被懒性创建,只在需要的时候才会更新。在这里,错误为Length must be greater than 7.
当我们提供的名字超过规定的长度时
newUser.set('name', 'Bender Bending Rodriguez'); errors = newUser.getValidation();
这条记录符合所有的检验,它包含7个字符,同时也不跟列表中的Bender匹配。
newUser.isValid()将会返回true, 当我们调用getValidation(), 新的检验记录将被更新,数据不在是脏数据。它所有的字段都将设置为true.
以下是另一个例子
Ext.define('Myapp.model.Client',{ extend:'Ext.data.Model', idProperty:'clientId ', fields:[ {name: 'clientId', type: 'int' }, {name: 'name' , type: 'string'}, {name: 'phone' , type: 'string'}, {name: 'website' , type: 'string'}, {name: 'status' , type: 'string'}, {name: 'clientSince' , type: 'date', dateFormat: 'Y-m-d H:i'} ], validators:{ name:[ { type:'presence'} ], website:[ { type:'presence', allowEmpty:true}, { type:'length', min: 5, max:250 } ] } }); //Step 1 var myclient = Ext.create('Myapp.model.Client',{ clientId : '10001', name : 'Acme corp', phone: '+52-01-55-4444-3210', website: 'www.acmecorp.com', status: 'Active', clientSince: '2010-01-01 14:35' }); if (myclient.isValid()){ //Step 2 console.log("myclient model is correct"); } console.log(myclient); console.log("My client's name is = " + myclient.data.name); console.log("My client's website is = " + myclient.data.website); // SET methods //Step 3 myclient.set('name',''); myclient.set('website',''); if (myclient.isValid()){//Step 4 console.log("myclient model is correct"); } else { //Step 5 console.log("myclient model has errors"); var errors = myclient.validate(); errors.each(function(error){ console.log(error.field,error.message); }); }
在第5步,我们使用了validate方法,它将返回一个验证失败的集合。并且输出字段以及错误信息. 这个集合的类型为Ext.data.ErrorCollection, 它扩展于Ext.util.MixedCollection. 因此我们可以使用each方法来遍历每一条错误记录.
Custom field types
在我们的application中不同的model之前,经常需要反复使用某个字段类型,在 Ext JS 4在,我们可以通过自动义检验器实现。 但在Ext JS 5开始,推荐使用自定义字段类型来代替自定义检验器。能过以下代码,我们将创建一个自定义字段Ext.define('Myapp.fields.Status',{ extend: 'Ext.data.field.String', //Step 1 alias: 'data.field.status',//Step 2 validators: {//Step 3 type: 'inclusion', list: [ 'Active', 'Inactive'], message: 'Is not a valid status value, please select the proper options[Active, Inactive]' } });
继承于Ext.data.field.String
我们为这个字段类型,定义了一个别名。这个别名不能重复,或者说覆盖一个已经存在于Ext.data.field中的子类
为这个字段类型设置校验器
Ext.define('Myapp.model.Client',{ extend:'Ext.data.Model', idProperty:'clientId ', fields:[ {name: 'clientId', type: 'int' }, {name: 'name' , type: 'string'}, {name: 'phone' , type: 'string'}, {name: 'website' , type: 'string'}, {name: 'status' , type: 'status'}, //Using custom field {name: 'clientSince' , type: 'date', dateFormat: 'Y-m-d H:i'} ], validators:{ ... } });
在这里,我们使用的是
{name: 'status', type: 'status'}, 这是因为我们给自定义类型,取了一个别名(alias: ‘data.field.status’). 下面让我们创建一段代码来测试
var myclient = Ext.create('Myapp.model.Client',{ clientId: '10001', name: 'Acme corp', phone: '+52-01-55-4444-3210', website: 'www.acmecorp.com', status: 'Active', clientSince: '2010-01-01 14:35' }); if(myclient.isValid()){ console.log("myclient model is correct"); } // SET methods myclient.set('status','No longer client'); if(myclient.isValid()){ console.log("myclient model is correct"); } else { console.log("myclient model has errors"); var errors = myclient.validate(); errors.each(function(error){ console.log(error.field,error.message); }); }
Relationships
我们可以创建模型之间的关系。比如,一个Client会接触多名employees, 享受多种Service.Employees for contact(Client联系的员工, 包含name, title, gender, email, phone, cell phone, 等字段)
Services (service ID, service name, service price, branch where service provided)
在Ext JS中支持one-to-many, one-to-one, and many-to-many关联
关于更多Association Type,可以查看http://docs.sencha.com/extjs/6.2.0-classic/Ext.data.schema.Association.html
One-to-many associations
Ext.define('Myapp.model.Client',{ extend:'Ext.data.Model', // step 1 requires: ['Myapp.model.Employee'], idProperty:'id ', fields:[.... ], hasMany:{ model:'Myapp.model.Employee', name:'employees', associationKey: 'employees' } });
使用了hasManay属性,我们定义了一个one-to-many的关系。hasMany属性可以是一个对像数组,每一个对像都包含一个model属性. 表明一个Client实例,可以有多个 Myapp.model.Employee对像。
此外,我们可以在创建Client类时,定义获得与它相关联model数据的函数名称。在这里,我们使用employees; 如果我们没有定义任何的函数名称, Ext JS将使用子模型的名字,加上”s”, 作为函数名称
现在我们创建一个Employee类,它位于appcode/model/Employee.js
Ext.define('Myapp.model.Employee',{ extend:'Ext.data.Model', idProperty:'id ', fields:[ {name: 'id', type: 'int' }, {name: 'clientid' , type: 'int'}, {name: 'name' , type: 'string'}, {name: 'phone' , type: 'string'}, {name: 'email' , type: 'string'}, {name: 'gender' , type: 'string'} ] });
为了测试model之前的关系,我们创建以下代码
var myclient = Ext.create('Myapp.model.ClientWithContacts',{ id: 10001, name: 'Acme corp', phone: '+52-01-55-4444-3210', website: 'www.acmecorp.com', status: 'Active', clientSince: '2010-01-01 14:35' }); //Step 2 myclient.employees().add( { id:101, clientId:10001, name:'Juan Perez', phone:'+52-05-2222-333', email:'juan@test.com', gender:'male'}, { id:102, clientId:10001, name:'Sonia Sanchez', phone: '+52-05-1111-444', email:'sonia@test.com',gender:'female'} ); //Step 3 myclient.employees().each(function(record){ console.log(record.get('name') + ' - ' + record.get('email') ); });
我们创建一个Client的记录
我们执行employees方法,这个方法是在定义关联类时,指定的name属性。这个方法返回一个Ext.data.Store实例。它是一个集合,用来管理相应的模型。所以我们可以通过它的add方法,添加两个Employee对像。
我们遍历myclient中的employees集合。每个记录表示一个Employee对像,所以我们可以使用get方法,获得它每个字段的值
One-to-one associations
为了描述这种关系,我们创建了一个合同类,来与 Client相关联Ext.define('Myapp.model.Contract',{ extend:'Ext.data.Model', idProperty:'id ', fields:[ {name: 'id', type: 'int' }, {name: 'contractId', type: 'string'}, {name: 'documentType', type: 'string'} ] });
如果你所看到的,这是一个正常的类。现在我们来定义Customer类
Ext.define('Myapp.model.Customer',{ extend:'Ext.data.Model', requires: ['Myapp.model.Contract'], idProperty:'id ', fields:[ {name: 'id', type: 'int'}, {name: 'name' , type: 'string'}, {name: 'phone' , type: 'string'}, {name: 'website' , type: 'string'}, {name: 'status' , type: 'string'}, {name: 'clientSince' , type: 'date', dateFormat: 'Y-m-d H:i'}, {name: 'contractInfo' , reference: 'Contract', unique:true} ] });
我们添加了一个字段,称为contractInfo, 但我们使用reference代替了type属性。它表示contractInfo的值为一个Contract类的实例。
var myclient = Ext.create('Myapp.model.Customer',{ id: 10001, name: 'Acme corp', phone: '+52-01-55-4444-3210', website: 'www.acmecorp.com', status: 'Active', clientSince: '2010-01-01 14:35', contractInfo:{ id:444, contractId:'ct-001-444', documentType:'PDF' } });
我们在这里是直接为contractInfo赋值一个对像. 如下所示
你看到上图中contractInfo是一个对像,它的字段跟Contract模型中定义的字段相同。如果contractInfo对像中没有定义Contract中的字段,则整个contractInfo则为undefined.
Exmaple
我们以一个博客应用为例,它有User和Post两个model. 每一个User都可以创建Post. 所以在这种情况下,一个user 可以有多个post. 但一个post只能属于创建它的用户. 这是一个ManyToOne的关系。我们可以通过下面的代码,表达它们的关系Ext.define('MyApp.model.User', { extend: 'MyApp.model.Base', fields: [{ name: 'name', type: 'string' }] }); Ext.define('MyApp.model.Post', { extend: 'MyApp.model.Base', fields: [{ name: 'userId', reference: 'User', // the entityName for MyApp.model.User type: 'int' }, { name: 'title', type: 'string' }] });
通过以上面的方式,可以很容易的实现我们应用中不同Model之前的关系。每一个Model可以跟其它的Model产生关联。同时,模型定义的顺序可以是任意的。一旦,你创建了这个Model类型的记录, 通过这条记录,就可以遍历与之相关联的数据。如果你想要获得一个用户的所有post. 你可以通过以下的代码
// Loads User with ID 1 and related posts and comments // using User's Proxy MyApp.model.User.load(1, { callback: function(user) { console.log('User: ' + user.get('name')); user.posts(function(posts){ posts.each(function(post) { console.log('Post: ' + post.get('title')); }); }); } });
每一个User model都有多个Posts, 它会被添加一个user.posts()函数,调用user.posts()返回一个Post model的Store集合.
不仅我们可以加载数据,还可以创建一条新的记录
user.posts().add({ userId: 1, title: 'Post 10' }); user.posts().sync();
上面的代码会创建一个Post实例,并且自动赋值userId字段为用户的id. 调用sync(),通过post的代理,保存这个新的Post(schema的代理配置决定). 这是一个异步操作,如果你需要获得操作完成后的通知,可以给它传递一个回调函数。
相返的,在Post model中,也会生成一个新的方法
MyApp.model.Post.load(1, { callback: function(post) { post.getUser(function(user) { console.log('Got user from post: ' + user.get('name')); }); } }); MyApp.model.Post.load(2, { callback: function(post) { post.setUser(100); } });
在加载函数中, getUser()是一个异步操作,所以需要一个回调函数,来获得用户的实例。setUser()方法只是简单的更新userId(有时称为 “外键”)为100,并且保存Post model. 通常也可以传递一个回调函数,在保存操作完成后,触发回调函数,知道操作是否成功.
加载嵌套的数据
当定义了associations后,可以在单个请求中,加载相关联系的记录。比如, 一个服务器响应如下数据
{ "success": true, "user": [{ "id": 1, "name": "Philip J. Fry", "posts": [{ "title": "Post 1" },{ "title": "Post 2" },{ "title": "Post 3" }] }] }
框架可以自动解析这个当个响应中的嵌套数据。而不用创建两个请求。
Stores
一个store是某一个模型对像的集合(比如多个users集合,invoices集合),作为一个客户端缓存来管理我们本地的数据。我们可以使用这个集合执行多种任务,比如排序,分组, filtering. 我们也可以使用一个有效的proxies从服务器上拉数据,和一个render解析服务器响应,并且填充这个集合.一个store经常被添加到widgets/componens,用来显示数据。比如组件中的grid, tree, combo box, 或者data view,都会使用一个store来管理数据。当我们创建一个自定义 widget,我们也应该使用一个store来管理数据。
为了创建一个store, 我们需要使用Ext.data.Store类。代码如下
Ext.define('MyApp.store.Customers',{ extend : 'Ext.data.Store', //Step 1 model : 'Myapp.model.Customer' //Step 2 }); //Ext JS 6的写法 http://docs.sencha.com/extjs/6.0.2-classic/guides/core_concepts/data_package.html var store = new Ext.data.Store ({ model: 'MyApp.model.User' }); store.load({ callback:function(){ var first_name = this.first().get('name'); console.log(first_name); } });
为了定义一个store, 我们需要让它继承于Ext.data.Store, 这个类负责处理models.
我们需要为创建的store,关联一个模型。它必须为一个有效的model类。
当我们创建了一个store类后,可以使用这个store来存取数据
var store = Ext.create("MyApp.store.Customers"); //counting the elements in the store console.log(store.count());
Adding new elements
我们可以创建一个Customer数据,并且通过add 或者insert方法,将这个新的元素添加到store.//Step 1 (define /create new model instance) var mynewcustomer = Ext.create('Myapp.model.Customer',{ id: 10001, name: 'Acme corp', phone: '+52-01-55-4444-3210', website : 'www.acmecorp.com', status: 'Active', clientSince: '2010-01-01 14:35', contractInfo:{ id:444, contractId:'ct-001-444', documentType:'PDF' } }); store.add(mynewcustomer); //Step 2 console.log("Records in store:" + store.getCount() );
也可以使用以下的方法
//Method 2 for add Records store.add({ id: 10002, name: 'Candy Store LTD', phone: '+52-01-66-3333-3895', website : 'www.candyworld.com', status: 'Active', clientSince: '2011-01-01 14:35', contractInfo:{ id:9998, contractId:'ct-001-9998', documentType:'DOCX' } }); console.log("Records in store:" + store.getCount());
如果想一次添加多个,我们可以传递一个数组给 add方法。
// Method 3 for add multiple records var mynewcustomer = Ext.create('Myapp.model.Customer', { ...}); var mynewcustomerb = Ext.create('Myapp.model.Customer', { ...}); store.add([mynewcustomer, mynewcustomerb]); console.log("Records in store:" + store.getCount());
store使用add方法,会将新的元素添加到集合的最后位置,如果我们想添加一个元素到第一的位置,或才其它位置,则可以使用
insert方法
Inline data
new Ext.data.Store({ model: 'MyApp.model.User', data: [{ id: 1, name: "Philip J. Fry" },{ id: 2, name: "Hubert Farnsworth" },{ id: 3, name: "Turanga Leela" },{ id: 4, name: "Amy Wong" }] });
遍历store中的记录
store.each(function(record, index){ console.log(index, record.get("name")); });
each方法接受一个函数。这个函数将被store中的每一条记录执行。这个匿名函数接收两个参数,每一次遍历时的record和index.
我们也可以给each方法传递第二个参数,表明这个匿名函数的作用域。
store中检索记录
通过位置索引如果我们想获得指定位置的model, 我们可以使用getAt方法
var modelTest = store.getAt(2); console.log(modelTest.get("name"));
First and last records
var first = store.first(); var last = store.last(); console.log(first.get("name"), last.get("name"));
* By range*
var list = store.getRange(1,3); //获取位置1开始的,3条记录,包含位1, 2, 3的记录 Ext.each(list,function(record,index){ console.log(index,record.get("name")); });
By ID
var record = store.getById(10001); console.log(modelTest.get("name"));
Removing records
我们有三种方法删除记录store.remove(record); store.each(function(record,index){ console.log(index,record.get("name")); });
在上面的代码中,remove传递的参数是一个model引用。
我们也可以一次性删除多个记录。我们只需要把要删除的model数组传递给它
store.remove([first,last]); store.each(function(record,index){ console.log(record.get("name")); });
有时,我们没有model的引用,在这种情况下,我们可以通过记录的位置进行删除
store.removeAt(2); store.each(function(record,index){ console.log(index,record.get("name")); });
如果我们想要删除所有的记录, 我们仅需要调用removeAll方法.
store.removeAll(); console.log("Records:",store.count()); // Records: 0
Sorting and Grouping
new Ext.data.Store({ model: 'MyApp.model.User', sorters: ['name','id'], filters: { property: 'name', value : 'Philip J. Fry' } });
Retrieving remote data
到目前为此,我们使用的都是本地数据,但在真实的应用中,我们的数据是保存在数据库当中的,或者需要通过web服务才能获取到数据。Ext JS使用proxies发送和接收数据。proxies通常配置在store或者model。也可以像我们在最开始的base model代码,在schema中定义.
在Ext JS中Proxies负现处理model的数据,我们可以说proxy是一个处理和操作数据(parsing, organizing等等)的类. 所以store可以通过proxie来读取和保存,或者发送数据到服务器。
一个proxy使用一个reader来解码接收到的数据,使用一个writer将数据编码为正确的格式,并且发送到数据源。reader有Array, JSON, XML三种类型,而writer只有JSON, XML类型。
proxies主要分为两种类型, Client 和 Server,如果我们想要改变我们数据源,我们仅需要改变proxy类型,而其它的都不需要改变。比如,我们为store或model定义一个Ajax proxy, 然后我们可以将它设置为 local storage proxy.
Client Proxy
Memory
Local Storage
Server Proxy
AJAX
JSONP
REST
Ajax proxy
Ext.define('Myapp.store.customers.Customers',{ extend:'Ext.data.Store', model: 'Myapp.model.Customer', proxy:{ type:'ajax', url: 'serverside/customers.php', reader: { type:'json', rootProperty:'records' //json中的数据字段 } } });
之后我们就可以创建这个store, 并且从远程加载数据
//Step 1 var store = Ext.create("Myapp.store.customers.Customers"); //Step 2 store.load(function(records, operation, success) { console.log('loaded records');//Step 3 Ext.each(records, function(record, index, records){ console.log( record.get("name") + ' - ' + record.data.contractInfo.contractId ); }); });
在我们执行例子之前,我们应该在服务器上创建一个serverside/customers.json
{ "success":true, "id":"id", "records":[ { "id": 10001, "name": "Acme corp2", "phone": "+52-01-55-4444-3210", "website": "www.acmecorp.com", "status": "Active", "clientSince": "2010-01-01 14:35", "contractInfo":{ "id":444, "contractId":"ct-001-444", "documentType":"PDF" } },{ "id": 10002, "name": "Candy Store LTD", "phone": "+52-01-66-3333-3895", "website": "www.candyworld.com", "status": "Active", "clientSince": "2011-01-01 14:35", "contractInfo":{ "id":9998, "contractId":"ct-001-9998", "documentType":"DOCX" } } ] }
Readers
Readers让Ext JS知道如何处理响应.如我们上面的例子
reader: { type:'json', rootProperty:'records' }
type属性表示使用什么格式来解析。它可以为json, xml, array.
rootProperty允许我们定义一个在服务器响应中的属性名字,服务器返回的数据都是这个字段下。在JSON响应中,它可以为一个数组。 在我们的例子中,我们设为records, 因为我们的JSON使用的是这个名字。如果我们嵌套的是一个对像,可以使用如下的方式
{ "success" :"true", "id":"id", "output":{ "appRecords":[{ our data .... }], "customerRecords":[{ our data .... }] } }
reader: { type:'json', rootProperty:'output.customerRecords' }
XML reader
XML reader相对于JSON来说有些变化,因此,我们需要在这个render中,配置其它属性, 以确保XML可以正常解析proxy:{ type:'ajax', url: 'serverside/customers.xml', reader: { type: 'xml', rootProperty: 'data', record:'customer', totalProperty: 'total', successProperty: 'success' } }
rootProperty 定义了 XML文件中查找records的节点。
我们还添加了一个record属性,在XML中每一个记录的名称
totalProperty和successProperty, store有的功能需要使用到这个标签下的值
现在,让我们创建一个XML文件 serverside/customers.xml
<?xml version="1.0" encoding="UTF-8"?> <data> <success>true</success> <total>2</total> <customer> <id>10001</id> <name>Acme corp2</name> <phone>+52-01-55-4444-3210</phone> <website>www.acmecorp.com</website> <status>Active</status> <clientSince>2010-01-01 14:35</clientSince> <contractInfo> <id>444</id> <contractId>ct-001-444</contractId> <documentType>PDF</documentType> </contractInfo> </customer> <customer> <id>10002</id> <name>Candy Store LTD</name> <phone>+52-01-66-3333-3895</phone> <website>www.candyworld.com</website> <status>Active</status> <clientSince>2011-01-01 14:35</clientSince> <contractInfo> <id>9998</id> <contractId>ct-001-9998</contractId> <documentType>DOCX</documentType> </contractInfo> </customer> </data>
Sending data
Ext.define('Myapp.store.customers.CustomersSending',{
extend:'Ext.data.Store',
model: 'Myapp.model.Customer',
autoLoad:false,
autoSync:true,
proxy:{
type:'ajax',
url: 'serverside/customers.json',
api: {
read : 'serverside/customers.json',
create : 'serverside/process.php?action=new',
update : 'serverside/process.php?action=update',
destroy : 'serverside/process.php?action=destroy'
},
reader: { type:'json', rootProperty:'records' },
writer:{
type:'json',
encode:true,
rootProperty:'paramProcess',
allowSingle:false,
writeAllFields:true,
root:'records'
},
actionMethods:{
create: 'POST',
read: 'GET',
update: 'POST',
destroy: 'POST'
}
}
});
设置了一个api 属性: 为CRUD分别设置一个url
设置一个writer属性
type: ‘json’: 以JSON格式发送数据
encode: 表示在传递数据到服务器前,是否经过Ext JS进行编码
rootProperty: 包信息的属性的名字
writeAllFields: 传递所有的记录到服务器,如果为false, 则只发送个改的字段。
对CRUD所对应的方法类型
Ext.Loader.setConfig({ enabled: true, paths:{ Myapp:'appcode' } }); Ext.require([ 'Ext.data.*', 'Myapp.model.Contract', 'Myapp.model.Customer', 'Myapp.store.customers.CustomersSending' ]); Ext.onReady(function(){ var store = Ext.create("Myapp.store.customers.CustomersSending"); //Step 1 store.load({ // Step 2 load Store in order to get all records scope: this, callback: function(records, operation, success) { console.log('loaded records'); Ext.each(records, function(record, index, records){ console.log( record.get("name") + ' - ' + record.data.contractInfo.contractId ); }); var test=11; console.log('Start adding model / record...!'); // step 3 Add a record var mynewCustomer = Ext.create('Myapp.model.Customer',{ clientId : '10003', name: 'American Notebooks Corp', phone: '+52-01-55-3333-2200', website : 'www.notebooksdemo.com', status : 'Active', clientSince: '2015-06-01 10:35', contractInfo:{ "id":99990, "contractId":"ct-00301-99990", "documentType":"DOC" } }); //因为我们设置了autoSync属性为true,所以会发送数据到服务器 store.add(mynewCustomer); // step 4 update a record console.log('Updating model / record...!'); var updateCustomerModel = store.getAt(0); updateCustomerModel.beginEdit(); updateCustomerModel.set("website","www.acmecorpusa.com"); updateCustomerModel.set("phone","+52-01-33-9999-3000"); updateCustomerModel.endEdit(); // step 5 delete a record console.log('deleting a model / record ...!'); var deleteCustomerModel = store.getAt(1); store.remove(deleteCustomerModel); } }); }); });
Schema
介绍
一个Schema是相关Ext.data.Model和之前关系Ext.data.schema.Association的集合。Schema Instances
默认情况下这个类以单例创建,作为模式服务于所有的实体(model中没有明确指明schema配置, 也没有继承schema配置)当一个entity指定了一个schema, 相关联的类,就能够查找(or create)这个entity类的实例,然后继承这个实例。
Importnat: 所有相关的entities 必须属性单个schema实例,这样才能正确的连接它们的关联性。
Configuring Schemas
控制schema配置的最好方式是定义一个 base modelExt.define('MyApp.model.Base', { extend: 'Ext.data.Model', fields: [{ name: 'id', type: 'int' }], schema: { namespace: 'MyApp.model', // generate auto entityName proxy: { // Ext.util.ObjectTemplate type: 'ajax', url: '{entityName}.json', reader: { type: 'json', rootProperty: '{entityName:lowercase}' } } } });
通过使用base class, 你所有的model类在声明创建时,都已要有拥有schema中的默认配置。
Relative Naming
当描述两个model之前的关系时,需要使用简单的名称,它不包含共同的namespace部分. 它称为entityName, 而不是类名称。 默认情况下, entityName是整个类的名称。但是,如果使用了namespace属性时,共同的部分会被丢弃, 我们可以得到简单的名称。MyApp.model.Foo则
entityName为
Foo.
MyApp.model.foo.Thing为
foo.Thing
Association Naming
在描述关系时,有不同的术语。最能说明阐明这些术语的一个简单例子是,User与 Group这种多对多的关系entityName - “User”和”Group”为
entityName, 它们都是从完整的类名中简写获得(App.model.User and App.model.Group)
associationName - 当我们谈论关系时,特别是many-to-many. 给它们命名就非常重要。Associations(关联)不被涉级的任何一个实体拥有, 所以这时,这个名称就类似于一个entityName. 默认的associationName为”GroupUsers”.
left and right - Association(关联)描述了两个实例之前的关系。在谈论具体的关系时,我们可以使用两个参与者的entityName(比如User or Group). 当谈论一个抽像的associations
比如”GroupUsers”这个关系,”User”称为 left, 而”Group”称为right. 在一个many-to-many的关联时, 我们可以任意的选择left和right. 当涉及到外键时,left 则是被包含的外键
Custom Naming Conventions
Schema的一个工作就是管理名字的生成(比如entityName). 这个工作它是委派给namer类。 如果你需要以其它的方法生成名字,你可以为你的定提供自定义的namerExt.define('MyApp.model.Base', { extend: 'Ext.data.Model', schema: { namespace: 'MyApp.model', namer: 'custom' } });
类似于alias创建一个类,如下所示
Ext.define('MyApp.model.CustomNamer', { extend: 'Ext.data.schema.Namer', alias: 'namer.custom', ... });
相关文章推荐
- D3 & Data Visualization in Ext JS
- Extjs4.0数据包(Ext.data.package)以及数据模型(Ext.data.model介绍)详解
- ext-js 中 Ext.data.Model 的 phantom 属性的讨论
- [转载]Ext.data-Store
- Ext.data-Store
- 阅读Ext 学习Javascript(一)Core/Ext.js
- [转载]Ext.data-DataProxy/HttpProxy/MemoryProxy/ScriptTagProxy
- Ext.data-DataReader/ArrayReader/JsonReader/XmlReader
- JS的package/import机制的考察提纲(草稿)
- EXT核心API详解Ext.data(十)-DataReader/ArrayReader/JsonReader/XmlReader
- javascript:SwichData.js
- using JS to control two select(html),the data can be loaded from database and XML,and show in the select
- Ext.data-DataReader/ArrayReader/JsonReader/XmlReader
- 最好用的JS编辑器:JSEclipse及YUT-EXT库的智能代码完成功能
- IDEs, plugins and tools for Ext JS 2.0
- 阅读Ext 学习Javascript(一)Core/Ext.js
- sencha ext js 6 快速入门(必看)
- Ext.data-DataProxy/HttpProxy/MemoryProxy/ScriptTagProxy
- DTS Package Integrity violation; attempt to insert NULL data or data which violates constraints
- ext问题:ext-lang-zh_CN.js 未结束的字符串常量(解决)