如何将 ajax 结果传递给同一类中的可验证公共



我有一个jQuery文件,它也使用unserscore.js。它控制日期和不同地点的选择。对于其中一个页面,它还根据场地类型控制显示哪些视觉效果。我可以使用 ajax 成功获取页面类型,但我无法将该值传递给脚本中的公共变量。它基于数据来自哪个WiFi点。如果数据来自本地点,则页面应显示 d3 气泡图。如果它来自偏远的地方,它应该显示场地的地图。目前,我具有基于场地ID进行硬编码的功能,这远非理想。为了根据场地使用哪个位置做出决定,我创建了一个获取"位置"的 ajax 调用。使用控制台.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;
});

确定要显示的视觉对象靠近顶部的函数:_getChartDisplayDiv:

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.spotName"变量,但结果为空。我也尝试简单地返回 ajax 调用的结果,但我收到"不是函数"错误。如何使 ajax 调用的结果可用于我的"_getChartDisplayDiv"函数?

您的问题是您正在尝试在分配之前读取_this.spotName的值。让我们走过发生的步骤。

当你调用_getChartDisplayDiv(value)时,_getChartDisplayDiv函数首先调用_this.Load(venueId)。 反过来,Load提交带有success回调的 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回调,而回调又将调用_this.Update,这将设置您所追求的变量。您用于此目的的语法是正确的。然而!

"当响应到达时"恰好是未来不可预测的事件。可能是在 10 毫秒后,可能需要 2 秒,或者请求可能会完全超时。与浏览器在脚本中执行所有其他代码所需的时间相比,即使是 10 毫秒也已经是永恒的。您可以非常确定,当$.ajax返回时,success回调尚未运行。

当您将回调(success)传递给函数($.ajax)并且回调在函数返回之前未运行时,这称为异步回调,简称"异步"。当回调可能异步调用时,函数保证它始终异步运行非常重要,因为这种类型的情况需要以与同步调用回调时完全不同的方式处理(即,在函数返回之前)。您可以在此博客文章中阅读有关技术细节的更多信息。所以这正是$.ajax所保证的:它永远不会在返回之前调用success(或error)回调,即使在假设响应足够快的情况下也是如此

$.ajax返回后,您的Load函数将立即返回,此时您的_getChartDisplayDiv函数将继续执行。几乎在那之后,您打算立即阅读_this.spotName.$.ajax已经返回,因此您可能希望此时已经调用了success回调。

不幸的是,异步回调比这更顽固。异步回调不仅在您传递给它的函数返回之前不会运行;它不会运行,直到任何当前正在执行的函数返回。除了$.ajax之外,Load需要返回,_getChartDisplayDiv需要返回,任何调用_getChartDisplayDiv的函数都需要返回,等等。整个调用堆栈需要展开。只有这样(当响应实际到达时,很可能是几毫秒后),才会调用success回调。这个游戏规则在 JavaScript 中称为事件循环。

解决方案比您预期的要简单:您只需要颠倒控制顺序。当您想要更新图表时,您可以更新图表,而不是尝试在请求中强制数据,而不是尝试直接更新图表,您可以直接触发请求。具体来说,在您的情况下,您只需要进行三项更改:

  1. 在您当前呼叫_getChartDisplayDiv的地方,改为呼叫_this.Load
  2. 删除在_getChartDisplayDiv函数内调用_this.Load的行。
  3. success处理程序的末尾,添加一行调用_getChartDisplayDiv

顺便说一下,使用适当的应用程序框架将使管理此类事情变得更加容易。在您的情况下,我建议您尝试骨干网;它建立在Underscore和jQuery之上,并且没有主见,因此您可以逐步采用它,而不必从根本上改变您的工作方式。

我不熟悉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 ));

最新更新