jquery 如何将 AJAX 结果传递给同一个类中的Public veriable?

kcrjzv8t  于 2022-12-18  发布在  jQuery
关注(0)|答案(2)|浏览(115)

我有一个jQuery文件,它也使用unserscore.js。它控制日期和不同地点的选择。对于其中一个页面,它还控制根据地点类型显示哪些视觉效果。我可以成功地使用 AJAX ,获得页面类型,但是我一直无法将该值传递给脚本中的公共变量。它是基于数据来自哪个WiFi点的。如果数据来自本地地点,页面应该显示一个d3气泡图。如果数据来自远程地点,页面应该显示一个地点Map。目前,我的功能是基于地点ID的硬编码,这是非常不理想的。为了根据地点使用的地点做出决定,我创建了一个 AJAX 调用来获取“地点”。使用console.log,我可以看到我从 AJAX 调用中得到了正确的结果,但是在将信息传递给变量以便使用它方面,我遗漏了一些东西。
下面是完整的jQuery文件:

define([
    "ui/selects",
], function (SelectsUiClass) {
    var global = this;

    var MainControlsClass = function () {
        // Private vars
        var _this = this,
            _xhr = null,
            _selects = new SelectsUiClass(),
            _dateRangeSelect,
            _venueSelect,
            _floorSelect,
            _zoneSelect;

        // Public vars
        this.Selects = null;
        this.spotName = null;

        // Private Methods
        var _construct = function () {
            _dateRangeSelect = _selects.InitSelect('#mainControls-dateRange', _onSelectChange);
            _venueSelect = _selects.InitSelect('#mainControls-venue', _onSelectChange);
            _floorSelect = _selects.InitSelect('#mainControls-floor', _onSelectChange);
            _zoneSelect = _selects.InitSelect('#mainControls-zone', _onSelectChange);

            var value = _this.GetVenue();

            _getChartDisplayDiv(value);
        };

        var _getChartDisplayDiv = function (venueId) {
            var path        = window.location.pathname,
                pathArray   = path.split("/"),
                page        = pathArray[pathArray.length - 1];

            console.log('controlsjs 36, navigation page: ' , page);
            console.log('controlsjs 37, venue value: ' , venueId);

            _this.Load(venueId);
            console.log('Controls 40, sPot Name = ', _this.spotName);

            if (page === 'heatmap') {
                if (venueId === 8 || venueId === 354) {
                    //make the bubble div visible
                    $("#heatmap-bubble").show();
                    //make the map div invisible
                    $("#heatmap-map").hide();
                } else {
                    //make the map div visible
                    $("#heatmap-map").show();
                    //make the bubble div invisible
                    $("#heatmap-bubble").hide();
                }
            }
        }

        this.Load = function (venueId) {
            console.log("Controls 66, Venue Id sent = ", venueId);
            if (_xhr) {
                _xhr.abort();
                _xhr = null;
            }

            _this.SetLoading(true);
            _xhr = $.ajax({
                url: $("meta[name='root']").attr("content") + '/app/heatmap/spot',
                type: 'POST',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                },
                data: {
                    venue_id: venueId
                },
                dataType: 'JSON',
                async: true,
                cache: false,
                error: function (jqXHR, textStatus, errorThrown) {
                    _this.SetLoading(false);
                },
                success: function (response) {
                    _this.SetLoading(false);
                    console.log("Controls 90, Response of ajax call = ", response);
                    _this.Update(response);
                }
            });
        };

        // Public functions
        this.SetLoading = function (option) {
            if (_.isUndefined(option)) { option = false; }

            if (this.spotName) { this.spotName.SetLoading(option); }
        };

        this.Update = function (data) {
            if (_.isUndefined(data) || _.isNull(data)) {
                console.log('Controls 106: Spot Name: ', data)
                this.spotName = data;
            }
        };

        var _getVenueData = function (venueId) {
            for (var i = 0; i < venuesData.length; i++) {
                if (venuesData[i].id === venueId) {
                    if (!_.isUndefined(venuesData[i].spot_data)) {
                        return venuesData[i].spot_data;
                    }
                }
            }
        };

        var _onVenueChange = function () {
            var value = _this.GetVenue();

            if (_.isNull(value)) {
                return;
            }

            _getChartDisplayDiv(value);

            //_setSelectValue(_venueSelect, value);

            var venueData = _getVenueData(value);
            console.log('Venue data received: ', venueData);
            if (!_.isUndefined(venueData) && !_.isUndefined(venueData.floors)) {
                _selects.UpdateSelect(_floorSelect, venueData.floors);
                _onFloorChange();
            }
        };

        var _onFloorChange = function () {
            var value = _this.GetFloor(),
                zones = [];

            if (_.isNull(value)) {
                return;
            }

            //_setSelectValue(_floorSelect, value);
            if (_.isNumber(value)) {
                var venueData = _getVenueData(_this.GetVenue()),
                    floors = venueData.floors;

                for (var i = 0; i < floors.length; i++) {
                    if (floors[i].id === value) {
                        zones = floors[i].zones;
                    }
                }
            }

            _selects.UpdateSelect(_zoneSelect, zones);
        };

        var _onZoneChange = function () {
            var value = _this.GetZone();

            if (_.isNull(value)) {
                return;
            }

            //_setSelectValue(_zoneSelect, value);
        };

        var _onSelectChange = function (e) {
            var t = $(e.target),
                id = t.attr('id');

            if (_venueSelect && _venueSelect.attr('id') === id) {
                _onVenueChange();
            } else if (_floorSelect && _floorSelect.attr('id') === id) {
                _onFloorChange();
            } else if (_zoneSelect && _zoneSelect.attr('id') === id) {
                _onZoneChange();
            }

            EventDispatcher.Dispatch('Main.Controls.Change', _this, {
                caller: id
            });
        };

        // Public Methods
        this.GetDateRange = function () {
            return _selects.GetSelectValue(_dateRangeSelect);
        };

        this.GetDateRangeKey = function () {
            if (_dateRangeSelect) {
                var selected = _dateRangeSelect.find('option:selected');

                if (selected.length) {
                    return selected.attr("data-key") || "";
                }
            }
            return "";
        };

        this.GetVenue = function () {
            return _selects.GetSelectValue(_venueSelect);
        };

        this.SetVenue = function (value) {
            _selects.SetSelectValue(_venueSelect, value);
        }

        this.GetFloor = function () {
            return _selects.GetSelectValue(_floorSelect);
        };

        this.SetFloor = function (value) {
            _selects.SetSelectValue(_floorSelect, value);
        }

        this.GetZone = function () {
            return _selects.GetSelectValue(_zoneSelect);
        };

        this.SetZone = function (value) {
            _selects.SetSelectValue(_zoneSelect, value);
        }

        this.GetData = function () {
            return {
                dateRange: {
                    date: this.GetDateRange(),
                    key: this.GetDateRangeKey()
                },
                venue: this.GetVenue(),
                floor: this.GetFloor(),
                zone: this.GetZone()
            };
        };

        // Init
        _construct();
    };

    return MainControlsClass;
});

用于确定要显示的视觉对象接近顶部的函数:_获取图表显示分区:

var _getChartDisplayDiv = function (venueId) {
            var path        = window.location.pathname,
                pathArray   = path.split("/"),
                page        = pathArray[pathArray.length - 1];

            _this.Load(venueId);
            console.log('Controls 40, sPot Name = ', _this.spotName);

            if (page === 'heatmap') {
                if (venueId === 8 || venueId === 354) {
                    //make the bubble div visible
                    $("#heatmap-bubble").show();
                    //make the map div invisible
                    $("#heatmap-map").hide();
                } else {
                    //make the map div visible
                    $("#heatmap-map").show();
                    //make the bubble div invisible
                    $("#heatmap-bubble").hide();
                }
            }
        }

当我能够将“spot”信息传递给它或它使用的变量时,它应该如下所示:

var _getChartDisplayDiv = function (venueId) {
            var path        = window.location.pathname,
                pathArray   = path.split("/"),
                page        = pathArray[pathArray.length - 1];

            _this.Load(venueId);
            console.log('Controls 40, sPot Name = ', _this.spotName);

            if (page === 'heatmap') {
                if (_this.spotName === 'local' ) {
                    //make the bubble div visible
                    $("#heatmap-bubble").show();
                    //make the map div invisible
                    $("#heatmap-map").hide();
                } else {
                    //make the map div visible
                    $("#heatmap-map").show();
                    //make the bubble div invisible
                    $("#heatmap-bubble").hide();
                }
            }
        }


我 AJAX 调用在这里:

this.Load = function (venueId) {
            console.log("Controls 66, Venue Id sent = ", venueId);
            if (_xhr) {
                _xhr.abort();
                _xhr = null;
            }

            _this.SetLoading(true);
            _xhr = $.ajax({
                url: $("meta[name='root']").attr("content") + '/app/heatmap/spot',
                type: 'POST',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                },
                data: {
                    venue_id: venueId
                },
                dataType: 'JSON',
                async: true,
                cache: false,
                error: function (jqXHR, textStatus, errorThrown) {
                    _this.SetLoading(false);
                },
                success: function (response) {
                    _this.SetLoading(false);
                    console.log("Controls 90, Response of ajax call = ", response);
                    _this.Update(response);
                }
            });
        };

这成功地获得了正确的位置,但是我一直无法将它传递给我可以使用的变量。我想我混淆了私有变量和公共变量。我尝试使用'this.update'函数将设置传递给公共变量'this.spotName',但是结果为null。我还尝试简单地返回 AJAX 调用的结果,但是我得到了一个“不是函数”的错误。2我怎样才能使 AJAX 调用的结果对我的'_getChartDisplayDiv'函数可用呢?

qacovj5a

qacovj5a1#

您的问题是您试图在_this.spotName赋值之前读取它的值。让我们来看看发生的步骤。
当你调用_getChartDisplayDiv(value)时,_getChartDisplayDiv函数首先调用_this.Load(venueId),然后Loadsuccess回调提交一个 AJAX 请求,如下所示:

this.Load = function (venueId) {
            // ...

            _this.SetLoading(true);
            _xhr = $.ajax({
                ...
                success: function (response) {
                    _this.SetLoading(false);
                    console.log("Controls 90, Response of ajax call = ", response);
                    _this.Update(response);
                }
            });
        };

当响应到达时,success回调函数将被调用,然后success回调函数将调用_this.Update_this.Update将设置您要查找的变量。您用于此目的的语法是正确的。
“当响应到达”碰巧是一个不可预测的事件在未来。它可能是在10毫秒之后,可能需要2秒钟,或者请求可能完全超时。即使10毫秒已经是一个永恒的时间相比,你的浏览器执行所有其他代码在你的脚本。你可以相当肯定的是,当$.ajax返回的时候,success回调尚未运行。
当您传递回调时(success)到函数($.ajax),并且回调在函数返回之前没有运行,这被称为异步回调,简称“async”。当回调可能被异步调用时,对于函数来说保证它 * 总是 * 异步运行是很重要的,因为这种情况需要以与同步调用回调时完全不同的方式来处理(也就是在函数返回之前)。你可以阅读更多关于this blogpost的技术细节。这正是$.ajax所保证的:它在返回之前 * 决不 * 调用success(或error)回调,* 即使 * 在响应将足够快到达的假设情况下。
$.ajax返回后,Load函数立即返回,此时_getChartDisplayDiv函数继续执行。几乎在此之后,您打算立即读取_this.spotName$.ajax已经返回,因此您可能希望此时success回调已经被调用。
不幸的是,异步回调比这更顽固,不仅异步回调在传递给它的函数返回之前不会运行;它不会运行,直到 any 当前执行的函数返回。除了$.ajax之外,Load需要返回,_getChartDisplayDiv需要返回,任何调用_getChartDisplayDiv的函数需要返回,等等。整个调用堆栈需要展开。只有在那时(当响应真正到达时,可能要过几毫秒)success回调函数才会被调用,这个游戏规则在JavaScript中称为event loop
解决方案比您想象的要简单:您只需要颠倒控制的顺序。当您想要更新图表时,您可以在响应到达时更新图表,而不是试图强制数据离开请求。您也可以直接触发请求,而不是试图直接更新图表。具体到您的情况,您只需要进行三项更改:
1.在当前调用_getChartDisplayDiv的地方,改为调用_this.Load
1.删除_getChartDisplayDiv函数中调用_this.Load的行。
1.在success处理程序的末尾,添加调用_getChartDisplayDiv的行。
顺便说一句,使用一个合适的应用程序框架将使管理这类事情变得容易得多,在您的情况下,我建议尝试Backbone;它构建在Underscore和jQuery之上,并且不受任何影响,因此您可以逐步采用它,而不必从根本上改变您的工作方式。

7y4bm7vi

7y4bm7vi2#

我对underscore.js不熟悉。对于jQuery,您有两个选项,您可以使用它们作为案例的灵感。未经测试的代码:

1.回调函数

提供回调函数:

$('.mydiv').myPlugin({ // Pass options Object to plugin
  venuId:  '123',
  getType: function(type) {
    console.log(type); // Example accessing internal data
  }
});

您的插件代码:

(function( $ ) {
  $.fn.myPlugin = function(opt) {

    this.filter('div').each(function() {
      const settings = $.extend({
        namespace: 'myPlugin',
        type:      'local'
        getType: function() {},
        // otherSettings: 'as needed',
      }, opt);

      // plugin code here...

      if(typeof settings.getType === 'function') {
        settings.getType(settings.type);
      }

    });

    return this;
  };

}( jQuery ));

2.插件方式

您可以定义可以调用的插件方法:

$('#mydiv').myPlugin({ // Pass options Object to plugin
  venuId:  '123'
});
console.log($('#mydiv').myPlaugin('getType'));


您的插件代码:

(function( $ ) {
  $.fn.myPlugin = function(opt) {

    this.filter('div').each(function() {
      const settings = $.extend({
        namespace: 'myPlugin',
        type:      'local',
        // otherSettings: 'as needed',
      }, opt);

      this.getType = function() {
        return settings.type;
      }

      let firstArg = arguments[0];
      if(typeof firstArg === 'string') {
        let func = this[firstArg];
        if(typeof func === 'function') {
          var args = [];
          for(var i = 1; i < arguments.length; i++) {
            args.push(arguments[i]);
          }
          return func.apply(this, args);
        }
      } else {
        // plugin init code here...
      }

    });

    return this;
  };

}( jQuery ));

相关问题