Я новичок в JS и делаю веб-приложения, и я столкнулся с этой проблемой: У меня есть код JS, который создает мне 6 датчиков и функцию, которая обновляет значение датчика 1:
<!-- Include the Gauge.js library -->
<!-- Raphael must be included before justgage -->
<script type = "text/javascript" src = "https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
<script type = "text/javascript" src = "https://cdnjs.cloudflare.com/ajax/libs/justgage/1.2.9/justgage.min.js"></script>
<!-- Initialize the gauge on page load -->
<script>
var gauge1;
var gauge2;
var gauge3;
var gauge4;
var gauge5;
var gauge6;
function updateGaugeValue(newValue, event) {
event.preventDefault();
gauge1.refresh(newValue);
}
window.onload = function () {
gauge1 = new JustGage({
id: "gauge1",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Value"
});
gauge2 = new JustGage({
id: "gauge2",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Value"
});
gauge3 = new JustGage({
id: "gauge3",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Value"
});
gauge4 = new JustGage({
id: "gauge4",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Value"
});
gauge5 = new JustGage({
id: "gauge5",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Value"
});
gauge6 = new JustGage({
id: "gauge6",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Value"
});
};
</script>
Когда я вызываю обновление с такой кнопки
<button onclick = "updateGaugeValue(5, event)">Click me</button>
функция нормально работает и измените значение датчика 1 на 5. Но когда я пытаюсь вызвать эту функцию из С# (используя ASP.NET) через ClientScript, например:
Page.ClientScript.RegisterStartupScript(this.GetType(), "CallMyFunction", "updateGaugeValue(5, event)", true);
Ничего не произошло. Но если я попытаюсь проверить это и предупредить привет, мир, как
Page.ClientScript.RegisterStartupScript(this.GetType(), "CallMyFunction", "alert('Hello World')", true);
он нормально работает и предупреждает. Какие-либо предложения?
Я ожидаю, что смогу вызвать эту функцию JS из кода C#.
Просто проверьте, существует ли event
перед вызовом preventDefault()
(т. е. if (event) { event.preventDefault(); }
). Затем в вашем вызове RegisterStartupScript
просто вызовите функцию без event
(т. е. "updateGaugeValue(5)"
).
Пробовал, с кнопки работает нормально, а с c# опять нет
Что говорит консоль? В инструментах разработчика браузера. Нажатие F12 в вашем браузере обычно открывает инструменты разработчика.
Homepage.aspx:96 Uncaught ReferenceError: updateGaugeValue не определено на Homepage.aspx:96:1
Затем переместите свои сценарии в тег HEAD. Я думаю, они у вас в конце BODY, верно?
Да, ты прав. Все еще не работает, но я получаю другую ошибку: Uncaught TypeError: Не удается прочитать свойства неопределенного (чтение «обновить») в updateGaugeValue (Homepage.aspx:25:20) в Homepage.aspx:176:1
Подождите, я напишу правильный ответ...
Причина, по которой вы получали Cannot read properties of undefined (reading 'refresh')
, заключается в том, что у вас есть неопределенная переменная только во время выполнения JS, пришедшего из C#. Переменная window.gauge1
определяется в событии «загрузка» (поскольку вы определяете обратный вызов onload
, назначая window.onload = ...
Помимо перемещения ваших сценариев в тег HEAD вместо того, чтобы помещать их в конец BODY, вы можете захотеть отложить выполнение кода, поступающего из C#, после завершения загрузки.
Что-то вроде:
window.onload = () => { const event = new Event("dummy"); setTimeout(() => { updateGaugeValue(5, event) }, 0) };
Поместите это в вызов Page.ClientScript.RegisterStartupScript(...
.
Что оно делает:
// appending to the queue of onload functions ensures any other variables and functions, that were declared in main template's scripts, will be defined already.
window.onload = () => {
const event = new Event("dummy"); // We want some event so that we can call .preventDefault() upon something.
// There are better ways around it though.
setTimeout(() => {
updateGaugeValue(5, event)
}, 0); // setting timeout to 0 effectively means, execute immediately after next round of UI updates
};
Я немного смущен сейчас. Весь этот блок кода я должен поместить в функцию RegisterStartupScript
в качестве параметра скрипта?
Да. Метод RegisterStartupScript создает тег SCRIPT и вставляет содержимое в параметр внутри. Так что в нем можно писать довольно длинные скрипты.
Итак, если я назову это: Page.ClientScript.RegisterStartupScript(this.GetType(), "CallMyFunction", "window.onload = () => { const event = new Event(\"dummy\"); setTimeout(() => { updateGaugeValue(5, event) }, 0) };", true);
есть 2 возможности: 1. иметь код JS внизу - нет никакой ошибки, но ничего не меняется 2. поместить код JS в тег заголовка - когда эта функция вызывается, я получаю снова ошибка не может прочитать свойства, и все мои датчики исчезают с моей веб-страницы
И функция по-прежнему выглядит так: function updateGaugeValue(newValue, event) { event.preventDefault(); gauge1.refresh(newValue); }
Удалите загрузку и просто вызовите ее снизу тела
Таким образом, проблема заключается в том, что мы используем страницу onload/onready для настройки, создания и отображения датчика.
Но нам нужна простая кнопка asp.net, скажем, текстовое поле, а затем некоторый код для установки значения.
Проблема в загрузке окна. Если мы «внедряем» вызов этой процедуры, мы не можем (очень хорошо) контролировать, когда запускается сценарий запуска нашего реестра, в отличие от существующего кода для настройки + создания датчиков.
Итак, что нам нужно сделать, так это «заставить» наш сценарий запуска ждать. Одним из отличных способов является использование setTimeout, и это фактически «подтолкнет» наш код к подпрограмме в «стеке выполнения» движка JavaScript, чтобы дождаться, отобразить страницу, запустить этот код настройки датчика, а ЗАТЕМ запустить желаемый код.
Еще хуже? Немного протестировав это, event.prevent, кажется, все испортил.
Итак, вот рабочее доказательство концепции:
Сверху размещаем код библиотеки скриптов, например:
<title></title>
<script src = "../Scripts/jquery-1.12.4.js"></script>
<link href = "../Content/bootstrap.css" rel = "stylesheet" />
<script src = "../Scripts/bootstrap.js"></script>
<script type = "text/javascript" src = "https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.4/raphael-min.js"></script>
<script type = "text/javascript" src = "https://cdnjs.cloudflare.com/ajax/libs/justgage/1.2.9/justgage.min.js"></script>
но для НАШЕГО кода мы хотим, чтобы этот материал был размещен внизу - после того, как DOM будет создан, настроен и отрендерен.
Итак, теперь этот код:
<div style = "padding:35px">
<div style = "border: solid 1px; float: left; padding: 8px">
<legend>Client side buttons</legend>
<button style = "float: left" onclick = "updateGaugeValue(4, event)">Click me</button>
<button style = "float: left; margin-left: 25px" onclick = "updateGaugeValue2(6, event)">Click me</button>
</div>
<div style = "float: left; margin-left: 30px; padding: 8px; border: solid 1px">
<legend>Server side buttons</legend>
<div style = "float:left">
Enter value 1-10 for gauage1:
<br />
<asp:TextBox ID = "TextBox1" runat = "server" Width = "50px">
</asp:TextBox>
<asp:Button ID = "cmdGauge1" runat = "server"
Text = "set guage 1"
CssClass = "btn"
OnClick = "cmdGauge1_Click" />
</div>
<div style = "float: left; margin-left: 30px">
Enter value 1-10 for gauage2:
<br />
<asp:TextBox ID = "TextBox2" runat = "server" Width = "50px">
</asp:TextBox>
<asp:Button ID = "cmdGauge2" runat = "server"
Text = "set guage 2"
CssClass = "btn"
OnClick = "cmdGauge2_Click" />
</div>
</div>
<div style = "clear: both; height: 50px">
</div>
<div id = "gauge1" style = "float:left;width:250px;"></div>
<div id = "gauge2" style = "width:200px;float:left; margin-left:40px; width: 250px"></div>
</div>
<script>
var gauge1;
var gauge2;
window.onload = function () {
gauge1 = new JustGage({
id: "gauge1",
value: 0,
min: 0,
max: 10,
title: "Files Uploaded",
label: "Zip Files Uploaded",
showInnerShadow: true,
shadowOpacity: 2,
shadowSize: 5,
textRenderer: function () {
return "144 / 400"
}
});
gauge2 = new JustGage({
id: "gauge2",
value: 0,
min: 0,
max: 10,
title: "My Gauge",
label: "Pdf Files uploaded",
showInnerShadow: true,
shadowOpacity: 2,
shadowSize: 5,
});
};
function updateGaugeValue(newValue, event) {
// event.preventDefault();
// alert('START')
gauge1.refresh(newValue);
}
function updateGaugeValue2(newValue, event) {
// event.preventDefault();
gauge2.refresh(newValue);
}
</script>
Итак, мы завернули наш скрипт запуска в функцию тайм-аута. Даже с задержкой «1» это работает, поскольку тайм-аут поместил наш код в программный стек движка Java, и, таким образом, он запускается после всего остального на этой странице.
код позади:
protected void cmdGauge1_Click(object sender, EventArgs e)
{
string jscode =
$@"updateGaugeValue({TextBox1.Text},event)";
jscode = "setTimeout(function () {" + jscode + "},1)";
ClientScript.RegisterStartupScript(Page.GetType(), "myfunc", jscode, true);
}
protected void cmdGauge2_Click(object sender, EventArgs e)
{
}
и результат:
Однако на самом деле нет особой необходимости «вызывать» ту существующую JS-подпрограмму, которая у вас есть, поскольку JS-объект существует (или мы предполагаем, что он будет).
Итак, тогда этот код:
protected void cmdGauge1_Click(object sender, EventArgs e)
{
string jscode =
$@"gauge1.refresh({TextBox1.Text})";
jscode = "setTimeout(function () {" + jscode + "},1)";
ClientScript.RegisterStartupScript(Page.GetType(), "myfunc", jscode, true);
}
Итак, теперь мы даже не вызываем эту JS-процедуру, а используем метод обновления графического объекта.
Итак, теперь мы можем вернуть (раскомментировать) event.preventDefault()
, и, таким образом, обе кнопки на стороне клиента и на стороне сервера в приведенном выше примере теперь будут работать.
Я также должен указать, что я использовал задержку 1 (1 миллисекунда). Эта задержка (количество) на самом деле не требуется, но СИЛА подпрограммы, которая должна быть помещена в стек выполнения механизма Java, является важной частью здесь.
У меня есть еще один вопрос, связанный с проблемой ... когда я вызываю ClientScript из функции, которая занимает 6 секунд, значение датчика изменяется на моей веб-странице после того, как вся функция выполнена, вместо этого я вызываю его при запуске своей функции, есть предложения?
Порядок кода позади, как правило, не имеет значения. При обратной передаче ВСЯ веб-страница находится на сервере. Вы можете в коде изменить элементы управления или что-то еще, но пока ПОЛНАЯ обратная передача не будет завершена на 100%, веб-страница не вернется на сторону клиента. Итак, если что-то занимает 6 секунд, то вся страница все еще находится на сервере. Код позади, чтобы изменить элемент управления, зарегистрировать скрипт или что-то еще? Вы не увидите ни одного из этих кумулятивных изменений до тех пор, пока обратная передача не будет завершена на 100%, поскольку веб-страница находится на сервере, измененном кодом позади - только после того, как все изменения передаются клиенту.
Неясно, можно ли разбить части этого кода, но если этот код занимает 6 секунд, тогда вы будете ждать 6 секунд, верно? Не совсем уверен на 100%, что вы спрашиваете здесь? Возможно, вы могли бы отобразить страницу и «затем» вызвать код для установки/загрузки значения датчика. В этом случае, вероятно, можно было бы рассмотреть ajax-вызов веб-метода.
Я делаю веб-страницы, которые проверяют скорость запросов к базам данных... Поэтому, когда я нажимаю кнопку и запускаю функцию, появляется цикл for, который выполняет некоторое количество запросов, эта функция включает таймер, и я хочу обновить значение датчика после каждый запрос выполняется, это как-то возможно?
Конечно, вы можете, но для этого потребуется некоторый тип таймера на стороне клиента и подход к обновлению. У вас не может быть «временной задержки» на стороне сервера для таких обновлений, поскольку, как уже отмечалось, вы ДОЛЖНЫ понять концепцию кругового пути. Таким образом, это можно сделать либо с помощью элемента управления таймером, либо даже с помощью кода JavaScript на стороне клиента + вызовов веб-методов. Чтобы понять, «почему» вы не можете использовать задержку на стороне сервера, прочитайте мой недавний ответ: stackoverflow.com/questions/75878021/…
Этот ответ содержит ссылки на «движок Java», но мне интересно, имелся ли в виду «движок JavaScript» (то есть интерпретатор JS в браузере).
Нет
event
, когда вы вызываете"updateGaugeValue(5, event)"
, регистрируя его как сценарий запуска.