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 で編成):

モデル パーツには、PvdcPrice と PvdcPriceDetails の 2 つのモデルが含まれており、"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 からの応答は success: false です。

次に、node.js 部分のコンソールで、node.js の投稿のリクエスト本文を出力します。それは次のとおりです。

[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 に答える