2

我正在使用 ExtJS+mongoDB 来实现一个应用程序。我使用 node.js 作为服务器和 express 模块来创建 REST api。具体来说,我使用的是:ExtJS 4.1.3 + mongoose 1.2.17 + express 1.2.17 + mongodb 2.4,运行在 Node.js v0.10.3 上。

代码 ExtJS 部分(在 MVC 中组织):

Model 部分包含两个模型,PvdcPrice 和 PvdcPriceDetails,它们具有“hasMany”关系。

PvdcPrice.js:

Ext.define('App.model.PvdcPrice', {
        extend : 'Ext.data.Model',
        fields : [{
                    name : '_id',
                    type : 'Number'
                }, {
                    name : 'Type',
                    type : 'string'
                }, {
                    name : 'MaxDiscount',
                    type : 'Number'
                }],
        hasMany : [{
                    name : 'prices',
                    model : 'App.model.PvdcPriceDetail',
                    associationKey : 'prices'
                }],
        proxy : {
            type : 'rest',
            url : '/pvdcprices',
            reader : {
                type : 'json',
                root : 'data',
                successProperty : 'success'
            }
        }
    });

PvdcPriceDetail.js:

    Ext.define('App.model.PvdcPriceDetail', {
        extend : 'Ext.data.Model',
        fields : [{
                    name : 'ID',
                    type : 'Number'
                }, {
                    name : 'Location',
                    type : 'string'
                }, {
                    name : 'Edition',
                    type : 'string'
                }, {
                    name : 'MonthlyPrice',
                    type : 'Number'
                }, {
                    name : 'OneTimePrice',
                    type : 'Number'
                }
        ]
    });

控制器部分,因为太长了,我只放了商店创建部分:

    var pvdcPrice = Ext.create('Ext.data.Store', {
                model : "App.model.PvdcPrice",
                data : [{
                            "_id" : 1,
                            "Type" : "pvdc",
                            "MaxDiscount" : "0"
                        }]
            });
    var priceInfo = pvdcPrice.first();
    var pvdcPriceDetails = priceInfo.prices();
    pvdcPriceDetails.add({
                'ID' : 1,
                'Location' : 'SNJ',
                'Edition' : 'Basic',
                'MonthlyPrice' : 906,
                'OneTimePrice' : 777
            });
    pvdcPriceDetails.add({
                'ID' : 2,
                'Location' : 'ATL',
                'Edition' : 'Standard',
                'MonthlyPrice' : 906,
                'OneTimePrice' : 777
            });
    pvdcPriceDetails.add({
                'ID' : 3,
                'Location' : 'ATL',
                'Edition' : 'Standard',
                'MonthlyPrice' : 906,
                'OneTimePrice' : 777
            });
    pvdcPrice.sync();

    var record = pvdcPrice.first();
    console.log(record.get('Type'));
    record.prices().each(function(r) {
        console.log(r.get('Location'));
        console.log(r.get('Edition'));
    });

Node.js部分,服务器脚本是app.js:

var express = require('express'),
app = module.exports = express();
// MongoDB
var mongoose = require('mongoose'),
    db = mongoose.connect('mongodb://127.0.0.1/IaaSDB'),
    //create sub schema of pvdc price schema
    PriceDetailSchema = new mongoose.Schema({
        ID: Number,
        Location: String,
        Edition: String,
        MonthlyPrice: Number,
        OneTimePrice: Number
    }),
    //create the Pvdc price info Model using the 'pvdcPrice' collection as a data-source
    PvdcPrice = mongoose.model('pvdcPrice', new mongoose.Schema({
        Type: String,
        MaxDiscount: String,
        prices: [PriceDetailSchema]
    }));

// Configuration
app.configure(function () {
    //app.set('views', __dirname + '/views');
    //app.set('view engine', 'jade');
    app.use(express.bodyParser());//parse JSON into objects
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(__dirname + '/IaaSPriceTool'));
});

app.configure('development', function () {
    app.use(express.errorHandler({
        dumpExceptions: true,
        showStack: true
    }));
});

app.configure('production', function () {
    app.use(express.errorHandler());
});

// Routes
app.get('/', function (req, res) {
    res.redirect('/index.html');
});


/*
Pvdc Price Information CRUD web service
*/
app.get('/pvdcprices', function (req, res) {
    PvdcPrice.find({}, function (err, pvdcprices) {
        res.contentType('json');
        res.json({
            success: true,
            data: pvdcprices
        });
    });
});
app.get('/pvdcprices/:id', function(req, res){
    PvdcPrice.find({_id: req.params.id}, function (err, pvdcPrices) {
        res.contentType('json');
        res.json({
            success: true,
            data: pvdcPrices
        });
    });
});
app.post('/pvdcprices', function (req, res) {
    console.log("[200] " + req.method + " to " + req.url);
    console.log(req.body);

    var newPriceInfo = new PvdcPrice();
    var newPriceInfoData = req.body;
    //remove the id which the client sends since it is a new pvdc price
    delete newPriceInfo['_id'];
    newPriceInfo.set(newPriceInfoData);
    newPriceInfo.save(function (err, pvdcPrice) {
        res.contentType('json');
        res.json({
            success: !err,
            data: pvdcPrice
        });
    });
});

app.listen(3000);
console.log("Express server listening on port %d in %s mode", 3000, app.settings.env);

然后,我使用firebug调试前端,我可以从浏览器控制台看到pvdc价格的竞争显示:

POST http://localhost:3000/pvdcprices?_dc=1369740182183 200 OK 27ms  
pvdc
Basic
ATL
Standard
ATL
Standard

这意味着 ExtJS 中的关联模型有效,我可以看到 pvdcPrice 的内容。ExtJS 中的 json 结构应该是:

{
"data" :{
            "_id" : 1,
            "Type" : "pvdc",
            "MaxDiscount" : "0",
            "prices" : [{
                'ID' : 1,
                'Location' : 'SNJ',
                'Edition' : 'Basic',
                'MonthlyPrice' : 906,
                'OneTimePrice' : 777
            },{
                'ID' : 2,
                'Location' : 'ATL',
                'Edition' : 'Standard',
                'MonthlyPrice' : 906,
                'OneTimePrice' : 777
            },{
                'ID' : 3,
                'Location' : 'ATL',
                'Edition' : 'Standard',
                'MonthlyPrice' : 906,
                'OneTimePrice' : 777
            }]
        }
}

但是来自 node.js 的响应是成功:假。

然后在node.js部分的控制台,打印出node.js中post的请求体,它是:

[200] POST to /pvdcprices?_dc=1369734975208
{ _id: 1, Type: 'pvdc', MaxDiscount: 0, id: null }

缺少“价格”的子内容,仅将 pvdcPrice 的主要部分发布到服务器。

有人可以指出在后期过程中导致丢失的原因吗?真的很感激帮助:)

4

2 回答 2

1

终于解决了这个问题,关键是ExtJS 4中的Ext.data.writer.Json对关联的支持不好。

http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store

该链接提供了一些解决方案。

我使用 Extjs 4.1.1 a,并将 DeepJson 添加到 Extjs 4.1 源文件夹中:

/**


* @class Ext.data.writer.DeepJson This class is used to write
 *        {@link Ext.data.Model} data to the server in a JSON format.
 * 
 * It overrides the original Ext.data.writer.Json since the Json can not handle
 * hasMany association, it can only transform the outside part into Json, the
 * inside data will be omiited.
 * 
 * @Yi Fang Reference:
 *     http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store/page3     23 Mar 2012 6:00 AM
 *     http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store/page5     13 Feb 2013 2:57 PM
 *     
 */
Ext.define('Ext.data.writer.DeepJson', {
        extend : 'Ext.data.writer.Json',
        getRecordData : function(record, operation) {
            // Setup variables
            var me = this, i, association, childStore, data;

            data = me.callParent(arguments);

            // Iterate over all the hasMany associations
            for (i = 0; i < record.associations.length; i++) {
                association = record.associations.get(i);
                if (association.type == 'hasMany') {
                    data[association.name] = null;
                    childStore = record[association.storeName];

                    // Iterate over all the children in the current
                    // association
                    childStore.each(function(childRecord) {

                                if (!data[association.name]) {
                                    data[association.name] = [];
                                }

                                // Recursively get the record data for
                                // children (depth first)
                                var childData = this.getRecordData.call(
                                        this, childRecord);

                                /*
                                 * If the child was marked dirty or phantom
                                 * it must be added. If there was data
                                 * returned that was neither dirty or
                                 * phantom, this means that the depth first
                                 * recursion has detected that it has a
                                 * child which is either dirty or phantom.
                                 * For this child to be put into the
                                 * prepared data, it's parents must be in
                                 * place whether they were modified or not.
                                 */
                                if (childRecord.dirty
                                        || childRecord.phantom
                                        || (childData != null)) {
                                    data[association.name].push(childData);
                                    record.setDirty();
                                }
                            }, me);

                    /*
                     * Iterate over all the removed records and add them to
                     * the preparedData. Set a flag on them to show that
                     * they are to be deleted
                     */
                    Ext.each(childStore.removed, function(
                                    removedChildRecord) {
                                // Set a flag here to identify removed
                                // records
                                removedChildRecord.set('forDeletion', true);
                                var removedChildData = this.getRecordData
                                        .call(this, removedChildRecord);
                                data[association.name]
                                        .push(removedChildData);
                                record.setDirty();
                            }, me);
                }
            }

            // Only return data if it was dirty, new or marked for deletion.
            if (record.dirty || record.phantom || record.get('forDeletion')) {
                return data;
            }
            return null;
        }
    });

在 PvdcPrice 模型中,将代理更新为

proxy : {
            type : 'rest',
            url : '/pvdcprices',
            reader : {
                type : 'json',
                root : 'data',
                successProperty : 'success'
            },
            writer : Ext.create('Ext.data.writer.DeepJson')
        }

然后嵌套数据的帖子有效。

于 2013-05-29T13:54:36.883 回答
0

belongsTo: 'App.model.PvdcPrice''App.model.PvdcPriceDetail'模型类 中缺少。它是您代码中的关联问题,您需要将父类键与子模型映射。请参阅以下链接(在协会会议中)。

http://docs.sencha.com/extjs/4.2.0/#!/guide/data

我希望在将您的 belongsTo 配置为 'App.model.PvdcPriceDetail' 类之后,它会按预期正常工作。

谢谢

于 2013-05-28T10:32:53.750 回答