用于显示异步请求状态的GUI组件

时间:2013-06-11 16:36:10

标签: javascript jquery backbone.js user-interface

您建议使用哪个接口或组件来显示并行异步调用的状态? (语言对我来说不是那么重要,只是模式,我可以在javascript中重写相同的类/接口......)

我从REST服务加载模型数据,我想在真实内容之前显示待定标签,如果出现问题则显示错误消息...我认为这是一个常见问题,并且必须有一个已编写的组件,或最佳实践,或此模式。你知道这样的事吗?

这是一个意大利面条代码 - Backbone.syncParallel还不是一个现有的函数 - 它有两个主要状态:updateForm,更新。在每个主要状态之前,页面显示“请稍候!”标签,并且错误页面显示错误消息。我认为这种代码是高度可重用的,所以我认为我可以创建一个自动显示当前状态的容器,但我无法确定该组件应该具有哪种接口......

var content = new Backbone.View({
    appendTo: "body"
});
content.render();

var role = new Role({id: id});
var userSet = new UserSet();

Backbone.syncParallel({
    models: [role, userSet],
    run: function (){
        role.fetch();  
        userSet.fetch();
    },
    listeners: {
        request: function (){
            content.$el.html("Please wait!");
        },
        error: function (){
            content.$el.html("Sorry, we could not reach the data on the server!");
        },
        sync: function (){
            var form = new RoleUpdateForm({  
                model: role,  
                userSet: userSet  
            });  
            form.on("submit", function (){
                content.$el.html("Please wait!");
                role.save({
                    error: function (){
                        content.$el.html("Sorry, we could not save your modifications, please try again!");
                        content.$el.append(new Backbone.UI.Button({
                            content: "Back to the form.",
                            onClick: function (){
                                content.$el.html(form.$el);
                            }
                        }));
                    },
                    success: function (){
                        content.$el.html("You data is saved successfully! Please wait until we redirect you to the page of the saved role!");
                        setTimeout(function (){
                            controller.read(role.id); 
                        }, 2000);
                    }
                });
            }, this);  
            form.render();
            content.$el.html(form.$el);
        }
    }
});

1 个答案:

答案 0 :(得分:0)

我创建了一个自定义视图来解决此问题。 (现在是测试版。)

用法:(表格是理论表格生成器)

var content = new SyncLabelDecorator({
    appendTo: "body",
});

content.load(function (){
    this.$el.append("normal html without asnyc calls");
});

var User = Backbone.Model.extend({
    urlRoot: "/users"
});
var UserSet = Backbone.Collection.extend({
    url: "/users",
    model: User
});
var Role = Backbone.RelationalModel.extend({
    relations: [{
        type: Backbone.HasMany,
        key: 'members',
        relatedModel: User
    }]
});

var administrator = new Role({id :1});
var users = new UserSet();

content.load({
    fetch: [role, users],
    sync: function (){
        var form = new Form({
            title: "Update role",
            model: role,
            fields: {
                id: {
                    type: "HiddenInput"
                },
                name: {
                    type: "TextInput"
                },
                members: {
                    type: "TwoListSelection",
                    alternatives: users
                }
            },
            submit: function (){
                content.load({
                    tasks: {
                        save: role
                    },
                    sync: function (){
                        this.$el.html("Role is successfully saved.");
                    }
                });
            }
        });
        this.$el.append(form.render().$el);
    }
});

代码:

var SyncLabelDecorator = Backbone.View.extend({
    options: {
        pendingMessage: "Sending request. Please wait ...",
        errorMessage: "An unexpected error occured, we could not process your request!",
        load: null
    },
    supported: ["fetch", "save", "destroy"],
    render: function () {
        if (this.options.load)
            this.load();
    },
    load: function (load) {
        if (load)
            this.options.load = load;
        this._reset();
        if (_.isFunction(this.options.load)) {
            this.$el.html("");
            this.options.load.call(this);
            return;
        }
        _(this.options.load.tasks).each(function (models, method) {
            if (_.isArray(models))
                _(models).each(function (model) {
                    this._addTask(model, method);
                }, this);
            else
                this._addTask(models, method);
        }, this);
        this._onRun();
        _(this.tasks).each(function (task) {
            var model = task.model;
            var method = task.method;
            var options = {
                beforeSend: function (xhr, options) {
                    this._onRequest(task, xhr);
                }.bind(this),
                error: function (xhr, statusText, error) {
                    this._onError(task, xhr);
                }.bind(this),
                success: function (data, statusText, xhr) {
                    this._onSync(task, xhr);
                }.bind(this)
            };
            if (model instanceof Backbone.Model) {
                if (method == "save")
                    model[method](null, options);
                else
                    model[method](options);
            }
            else {
                if (method in model)
                    model[method](options);
                else
                    model.sync(method == "fetch" ? "read" : (method == "save" ? "update" : "delete"), model, options);
            }
        }, this);
    },
    _addTask: function (model, method) {
        if (!_(this.supported).contains(method))
            throw new Error("Method " + method + " is not supported!");
        this.tasks.push({
            method: method,
            model: model
        });
    },
    _onRun: function () {
        this.$el.html(this.options.pendingMessage);
        if (this.options.load.request)
            this.options.load.request.call(this);
    },
    _onRequest: function (task, xhr) {
        task.abort = function () {
            xhr.abort();
        };
    },
    _onError: function (task, xhr) {
        this._abort();
        this.$el.html(this.options.errorMessage);
        if (this.options.load.error)
            this.options.load.error.call(this);
    },
    _onSync: function (task, xhr) {
        ++this.complete;
        if (this.complete == this.tasks.length)
            this._onEnd();
    },
    _onEnd: function () {
        this.$el.html("");
        if (this.options.load.sync)
            this.options.load.sync.call(this);
    },
    _reset: function () {
        this._abort();
        this.tasks = [];
        this.complete = 0;
    },
    _abort: function () {
        _(this.tasks).each(function (task) {
            if (task.abort)
                task.abort();
        });
    }
});