我有django模板,该模板呈现一种形式:2个datetimepickers,2个multiselect下拉列表和1个select下拉列表。 下拉菜单就像一个超级按钮,但datetimepicker的行为不正确。
案例:我想更改日期。所以: -单击日期时间选择器,然后显示日历(无时间) -点击日期: -发送新请求 -单击其他位置或其他字段: -发送与以前相同的请求
恐怕这最终可能会使服务器超载。 我如何避免最后一个额外的请求?
代码:
表格:
class UsageByTypeForm(forms.Form):
def __init__(self, data=None, user=None, *args, **kwargs):
super().__init__(data=data, *args, **kwargs)
if user and user.is_authenticated():
self.setup_mcu_field(user)
self.setup_client_field(user)
def setup_mcu_field(self, user):
user_mcus = user.operated_mcus
if len(user_mcus) > 1:
mcu_field = self.fields['mcus']
mcu_field.queryset = user_mcus.order_by('name').exclude(mcu_unico_id__isnull=True)
mcu_field.initial = mcu_field.queryset
else:
del self.fields['mcus']
def setup_client_field(self, user):
user_clients = user.operated_client_ids
client_field = self.fields['clients']
client_field.queryset = Cliente.objects.filter(id__in=user_clients)
client_field.initial = client_field.queryset
frequency = forms.ChoiceField(
label=_('Frequency'),
choices=ReportFrequency.values.items(),
required=True,
widget=forms.Select(
attrs={
'data-placeholder': _('Frequency'),
'data-header': _('Select a frequency'),
}),
)
mcus = forms.ModelMultipleChoiceField(
queryset=MCU.objects.none(),
widget=forms.SelectMultiple(
attrs={
'data-actions-box': "true",
'data-header': _('Choose some MCUs'),
}),
)
clients = forms.ModelMultipleChoiceField(
queryset=Cliente.objects.none(),
required=False,
widget=forms.SelectMultiple(
attrs={
'data-header': _('Choose some clients'),
'data-actions-box': "true",
}),
)
begin = forms.DateField(
initial=get_last_month_range()[0],
widget=forms.TextInput(
attrs={
'class': 'form-control',
}),
)
end = forms.DateField(
initial=get_last_month_range()[0],
widget=forms.TextInput(
attrs={
'class': 'form-control',
}),
)
def get_interval(self):
time_in_advance = self.cleaned_data.get(
'time_in_advance'
) or LAST_MONTH
return get_time_interval(
time_in_advance
)
模板:
{% extends "reporting_base.html" %}
{% load static %}
{% load i18n %}
{% block container %}
<div class="container-fluid">
<div class="row">
<div id="chartdiv" style="width: 100%; height: 800px; background-color: #FFFFFF;" ></div>
</div>
<div id="tableContainer"
class="container-fluid"
style="margin-top: 2%;"
data-ng-app="videoconferenceRoomsUsage"
ng-controller="videoconferenceRoomsUsageController">
<form style="text-align: center" id="UsageByTypeForm" class="form-inline well">
<div id="#id_begin" class="form-group">
{{ form.begin }}
</div>
<div id="#id_end" class="form-group">
{{ form.end }}
</div>
<div id="#id_freq"class="form-group">
{{ form.frequency }}
</div>
<div id="#id_clients" style="display:inline-block" class="form-group">
{{ form.clients }}
</div>
<div id="#id_mcus" class="form-group">
{{ form.mcus }}
</div>
</form>
<table id="intervalDataTable" class="table table-responsive">
<thead>
<tr>
<th class="text text-center">{% trans 'Interval' %}</th>
<th class="text text-center">{% trans 'Max Licence Usage' %}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="data in chartData">
{% verbatim %}
<td class="text text-center">
{[{ data.dataContext.category }]}
</td>
<td class="text text-center">
{[{data.dataContext.capacity}]}
</td>
{% endverbatim %}
</tr>
</tbody>
</table>
</div>
</div>
{% endblock container %}
{% block override_js %}
<script type="text/javascript" src="{% static "amcharts/amcharts.js" %}"></script>
<script type="text/javascript" src="{% static "amcharts/serial.js" %}"></script>
<script type="text/javascript" src="{% static "amcharts/plugins/dataloader/dataloader.min.js" %}"></script>
<script src="{% static "vendor/bootstrap-select/js/bootstrap-select.min.js" %}"></script>
<script src="{% static "vendor/bootstrap-select/js/i18n/defaults-es_CL.min.js" %}"></script>
<script type="text/javascript" src="{% static "js/jquery.datetimepicker.min.js"%}"></script>
<script type="text/javascript">
Array.prototype.removeValue = function(val) {
for (var i = 0; i < this.length; i++) {
if (this[i] === val) {
this.splice(i, 1);
i--;
}
}
return this;
};
$.ajaxSetup({ traditional: true });
$(document).ready(
function(){
$('#UsageByTypeForm select').on(
'change',
function() {
var tableElement = document.getElementById("tableContainer");
var scope = angular.element(tableElement).scope();
scope.makeChart();
scope.$apply();
}
);
$('#id_begin').datetimepicker({
closeOnDateSelect: true,
format: 'Y-m-d',
lang: 'es',
timepicker: false,
onShow:function( ct ){
this.setOptions({
maxDate:$('#id_end').val()?$('#id_end').val():false
}
)
},
});
$('#id_end').datetimepicker({
closeOnDateSelect: true,
format: 'Y-m-d',
lang: 'es',
timepicker: false,
onShow:function( ct ){
this.setOptions({
minDate:$('#id_begin').val()?$('#id_begin').val():false
}
)
},
});
$('#id_begin').change(function(e){
var tableElement = document.getElementById("tableContainer");
var scope = angular.element(tableElement).scope();
scope.makeChart();
scope.$apply();
});
$('#id_end').change(function(e){
var tableElement = document.getElementById("tableContainer");
var scope = angular.element(tableElement).scope();
scope.makeChart();
scope.$apply();
});
function collectData(){
var result = {};
var reportData = $("#UsageByTypeForm").serializeArray();
for(var j=0; j<reportData.length; j++){
var key = reportData[j]['name'];
if(key === 'sites' || key === 'mcus' || key === 'clients'){
if(result[key] === undefined){
result[key] = [];
}
result[key].push(reportData[j]['value']);
}
else{
result[key] = reportData[j]['value'];
}
}
return result;
}
$("select").selectpicker(
{
size: 15,
lang: 'en-us',
deselectAllText: "{% trans 'Deselect all' %}",
selectAllText: "{% trans 'Select all' %}",
noneSelectedText: "{% trans 'Nothing selected' %}"
}
);
var hiddenSites = [];
$("select#id_clients").on(
'changed.bs.select',
function(){
var clientIds = $(this).val();
if(clientIds === null){
clientIds = [];
for(var k=0; k<hiddenSites.length; k++){
$(hiddenSites[k]).appendTo("#id_sites");
}
hiddenSites = [];
$("#id_sites").selectpicker('refresh');
return;
}
for(var i=0; i<clientIds.length; i++){
var clientId = clientIds[i];
var selectedItem = $("select#id_clients>option[value=" + clientId + "]");
var clientName = selectedItem.text();
var openBracketIndex = clientName.indexOf('(');
if(openBracketIndex !== -1){
clientName = clientName.slice(openBracketIndex+1, -1)
}
if(clientName !== undefined){
var foundIndex = null;
for(var j=0; j<hiddenSites.length; j++){
var currentSiteName = hiddenSites[j];
if(currentSiteName.attr('label') === clientName){
foundIndex = j;
var optGroups = $("select#id_sites").find(">optgroup");
if(optGroups.length === 0){
$(hiddenSites[j]).appendTo("#id_sites");
}
else{
var lastOptItem = optGroups.last();
var lastOptName = lastOptItem.attr('label');
var firstOptItem = optGroups.first();
var firstOptName = firstOptItem.attr('label');
if(clientName > lastOptName){
$(hiddenSites[j]).appendTo("#id_sites");
}
else if(clientName < firstOptName){
$(hiddenSites[j]).prependTo("#id_sites");
}
else{
var spliceIndex = foundIndex;
foundIndex = null;
$.each(
optGroups,
function(i, optItem){
var wrappedOptItem = $(optItem);
if(wrappedOptItem.attr('label') > clientName){
wrappedOptItem.before($(hiddenSites[j]));
hiddenSites.splice(spliceIndex, 1);
return false;
}
}
);
}
}
break;
}
}
if(foundIndex !== null){
hiddenSites.splice(foundIndex, 1)
}
}
}
var deselectedItems = $("select#id_sites>optgroup");
for(var l=0; l<deselectedItems.length; l++){
var notSelectedClientItem = deselectedItems[l];
var notSelectedClientName = notSelectedClientItem.label;
var notSelectedClientId = $("select#id_clients>option:contains('" + notSelectedClientName + "')").val();
if(notSelectedClientId !== null){
var found = false;
for(var m=0; m<clientIds.length; m++){
if(clientIds[m] === notSelectedClientId){
found = true;
break;
}
}
if(!found){
hiddenSites.push($(notSelectedClientItem).remove());
}
}
}
$("#id_sites").selectpicker('refresh');
});
}
);
function handleDataUpdated(event){
var tableElement = document.getElementById("tableContainer");
var scope = angular.element(tableElement).scope();
if(event.chart !== undefined && scope !== undefined){
var chartData = event.chart.chartData;
var invertedChartData = [];
for(var i=chartData.length - 1; i>=0; i--){
invertedChartData.push(chartData[i]);
}
scope.chartData = invertedChartData;
scope.$apply();
}
}
var app = angular.module(
'videoconferenceRoomsUsage',
[]
);
app.config(
function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
}
);
app.controller(
'videoconferenceRoomsUsageController',
[
'$scope',
function($scope) {
$scope.chartData = [];
$scope.getHours = function(d){
return (Math.round(d * 100 / 3600) / 100) + "h";
};
$scope.formatHours = function(d){
return Math.round(d*100)/100 + "h"
};
$scope.formatPercent = function(d){
return Math.round(d*100)/100 + "%"
};
$scope.getUrl = function(){
var queryString = "?" + $("form").serialize();
return "{% url 'usage_of_ports_reports_json' %}" + queryString;
};
$scope.makeChart = function(){
if($scope.chart !== undefined){
var chart = $scope.chart;
chart.dataLoader.url = $scope.getUrl();
chart.dataLoader.loadData();
}
else{
$scope.chart = AmCharts.makeChart(
"chartdiv",
{
"type": "serial",
"numberFormatter": {
"precision": -1,
"decimalSeparator": ",",
"thousandsSeparator": "",
},
"listeners": [
{
"event": "dataUpdated",
"method": handleDataUpdated
}
],
"categoryField": "category",
"startDuration": 1,
"categoryAxis": {
"gridPosition": "start"
},
"trendLines": [],
"graphs": [
{
"id": "capacity_unit_usage",
"title": "{% trans 'Max Licence Usage' %}",
"valueField": "capacity",
"bullet": "round",
"balloonText": "[[value]]",
},
],
"legend": {
"enabled": true
},
"guides": [],
"export" : exportConfiguration,
"valueAxes": [
{
"id": "Hours-Axis",
"title": "{% trans 'Number of Ports' %}",
},
],
"allLabels": [],
"titles": [
{
"id": "Room-Usage",
"size": 15,
"text": "{% trans 'Max Licence Usage' %}"
}
],
"dataLoader": {
"url": $scope.getUrl(),
"format": "json"
}
}
);
}
};
$scope.makeChart();
}
]
);
</script>
{% endblock %}
{% block extra_css %}
<link href="{% static "vendor/bootstrap-select/css/bootstrap-select.min.css" %}" rel="stylesheet"/>
<link rel="stylesheet" href="{% static "css/jquery.datetimepicker.min.css"%}">
<style>
#UsageByTypeForm .form-group {
margin-left: 20px;
}
#id_call_type, #id_videoconference_mode{
list-style: none;
text-align: left;
padding-left: 10px;
padding-top: 4px;
}
</style>
{% endblock extra_css %}
答案 0 :(得分:1)
您正在使用的库可能有一个日期更改事件,因此您不会在基础输入每次更新时都被触发。假设这是jquery bootstrap datetimepicker库,您可以执行以下操作。尚未亲自尝试过此操作,但来自文档“ dp.change-日期更改时触发。”
$('#id_begin').on('dp.change', function(e){
var tableElement = document.getElementById("tableContainer");
var scope = angular.element(tableElement).scope();
scope.makeChart();
scope.$apply();
})
这些事件可以在http://eonasdan.github.io/bootstrap-datetimepicker/Events/中找到。如果您使用其他库,则它们可能会发生类似的事件。
或者,您可以将日期存储在变量中,并在执行工作之前确保日期已更改。类似这样的事情。请记住,完全未经测试
var previousDate = $('#id_begin').val(); //or whatever your default is here
$('#id_begin').change(function(e){
var currentDate = $(this).val();
if(previousDate != currentDate) {
var tableElement = document.getElementById("tableContainer");
var scope = angular.element(tableElement).scope();
scope.makeChart();
scope.$apply();
previousDate = currentDate;
}
});