您如何编写 Javascript 以使用 SignalR 显示 MVC 模型? (Javascript 在参数中接收属性)
How do you write Javascript to display MVC models with SignalR? (Javascript receives properties in parameter)
我正在努力将 SignalR 添加到工作的 MVC 应用程序中,但我不擅长 Javascript,而且我不知道如何编写它来显示我需要的内容。我看到了一堆关于聊天应用程序(发送消息)的教程,但这不是我需要的。
我想要的:
显示一个完整模型及其所有组件。
该程序收到一个文件,该文件在数据库中添加了一个新的 属性。 属性 必须显示其所有组件。
这是要显示的模型 (属性) 的示例
public int WaterLevel { get; set; }
public int WindSpeed { get; set; }
public int NumberOfMachine { get; set; }
public int FoodLevel { get; set; }
html 页面是使用 Visual studio: 创建的默认 html 页面
(这是网页上需要更新的javascript)
<tbody>
@foreach (var item in Model)
{
<tr id="properties_display">
<td id="waterLevel">
@Html.DisplayFor(modelItem => item.WaterLevel)
</td>
<td id="windSpeed">
@Html.DisplayFor(modelItem => item.WindSpeed)
</td>
<td id="numberMachine">
@Html.DisplayFor(modelItem => item.NumberOfMachine)
</td>
<td id="foodLevel">
@Html.DisplayFor(modelItem => item.FoodLevel)
</td>
<td id="number">
@Html.DisplayFor(modelItem => item.Number)
</td>
</tr>
}
</tbody>
观察者(On_Change):
private readonly IHubContext<PropertyHub> _hubContext;
public MyFileWatcher(IHubContext<PropertyHub> hubContext)
{
_hubContext = hubContext;
//watcher stuff
}
private void OnChanged(object sender, FileSystemEventArgs e)
{
DataAccess db = new DataAccess(MVCCoreAppDB);
string filePath = Path.GetFullPath(e.FullPath);
db.InsertData(filePath);
var propData = db.LoadData();
List<PropertyModel> properties = new List<PropertyModel>();
foreach (var row in propData)
{
properties.Add(new PropertyModel
{
WaterLevel = row.WaterLevel,
WindSpeed = row.WindSpeed,
NumberOfMachine = row.NumberOfMachine,
FoodLevel = row.FoodLevel
Number = row.Number
});
}
_hubContext.Clients.All.SendAsync("onFileChange", properties);
}
观察者 class 正在查找文件夹中的更改。创建或修改文件时,它会调用 3 个方法。首先,它将数据插入数据库,然后从数据库中加载数据并更改要使用的数据类型。
最后,它调用集线器让它知道有变化并且它需要更新网页。
这是我尝试过的,我得到了我需要的实时效果,但它没有显示正确的东西:
"use strict";
let connection = new signalR.HubConnectionBuilder()
.withUrl("/propertyHub")
.build();
connection.on("onFileChange", function (properties) {
let tr = document.createElement("tr");
document.getElementById("properties_display").appendChild(tr);
let tdWaterLevel = document.createElement("td");
tdWaterLevel.textContent = properties[0];
document.getElementById("waterLevel").appendChild(tdWaterLevel);
let tdWindSpeed = document.createElement("td");
tdWindSpeed.textContent = properties[1];
document.getElementById("windSpeed").appendChild(tdWindSpeed);
let tdNumberMachine = document.createElement("td");
tdNumberMachine.textContent = properties[2];
document.getElementById("numberMachine").appendChild(tdNumberMachine);
let tdFoodLevel = document.createElement("td");
tdFoodLevel.textContent = properties[3];
document.getElementById("foodLevel").appendChild(tdFoodLevel);
let tdNumber = document.createElement("td");
tdNumber.textContent = properties[4];
document.getElementById("number").appendChild(tdNumber);
});
connection.start().then(function () { TestConnection(); }).catch(function(err)
{
return console.error(err.toString());
});
function TestConnection() {
connection.invoke("GetConnectionId").catch(function (err) {
return console.error(err.toString());
});
}
就像现在一样,它在好的列中写入但不在 table 的末尾并且不写入值。它正在写对象 Object 而不是数字。
此外,当我写在 javascript;
properties.WaterLevel //For example
是放一个空白space而不是写东西
//It does the same thing if I write properties [0].WaterLevel
更新前的输出:
更新后的输出:
所需输出:
添加新文件后调试中的属性值:
知道如何在合适的地方获得正确的值吗?
我认为您对 table 和 table 行的工作方式有点困惑。首先 html 应该看起来更像这样:
<tbody id="properties_display">
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.WaterLevel)
</td>
<td>
@Html.DisplayFor(modelItem => item.WindSpeed)
</td>
<td>
@Html.DisplayFor(modelItem => item.NumberOfMachine)
</td>
<td>
@Html.DisplayFor(modelItem => item.FoodLevel)
</td>
<td>
@Html.DisplayFor(modelItem => item.Number)
</td>
</tr>
}
</tbody>
id
应该在整个页面中是唯一的,但您正在为模型中的每个项目创建一个具有所有相同 ID 的新行。这不是好的做法,并且可能导致混淆您要添加到的元素。但这也意味着 属性 中的每个项目都应该有自己的行 (tr),因此您需要插入新行而不是添加到任何当前行,这就是您通过引用中的 id 所做的javascript.
实际上,由于您似乎是在更改时将所有数据发送回客户端,因此最简单的刷新方法是删除所有当前行并添加所有新行,如下所示:
connection.on("onFileChange", function (properties) {
let table = document.getElementById("properties_display");
// Remove all existing rows
while (table.firstChild) {
table.removeChild(table.firstChild);
}
// Add a new row for each item in properties
for (let i = 0; i < properties.length; i++) {
let tr = document.createElement("tr");
let tdWaterLevel = document.createElement("td");
tdWaterLevel.textContent = properties[i].waterLevel;
tr.appendChild(tdWaterLevel);
let tdWindSpeed = document.createElement("td");
tdWindSpeed.textContent = properties[i].windSpeed;
tr.appendChild(tdWindSpeed);
let tdNumberMachine = document.createElement("td");
tdNumberMachine.textContent = properties[i].numberMachine;
tr.appendChild(tdNumberMachine);
let tdFoodLevel = document.createElement("td");
tdFoodLevel.textContent = properties[i].foodLevel;
tr.appendChild(tdFoodLevel);
let tdNumber = document.createElement("td");
tdNumber.textContent = properties[i].number;
tr.appendChild(tdNumber);
table.appendChild(tr);
}
});
我还没有测试过,但这可能更符合您的要求。您将所有元素传递给 JavaScript 因此它需要遍历所有项目并获取它们各自的属性。您可能需要确保您的集线器数据正确传递到 javascript。如果不是,您可能需要在传递之前在服务器上转换为 Json,但我认为 SignalR 会自动处理这些东西。
你提到 properties.WaterLevel
没有 return 任何有意义的东西,因为属性是一个数组,但如果 properties[0].WaterLevel
那么可能是其他问题,我会记录javascript 中的属性对象,以查看您的服务器实际传递给浏览器的内容。
作为最后的评论,删除和替换所有行可能不是最好的主意。您可能会更好地重用现有行并添加需要的行,或者只传递来自服务器的更改元素而不是所有元素。只是一些想法。希望这有帮助。
根据您的情况,查看代码段。
您的 JavaScript 对象是一个包含单个对象的数组。
var properties = [
{
"FoodLevel" : 48,
"Id":0,
"MachineID": "1234567",
"Number" : 0,
"NumberOfMachine" : 32,
"WaterLevel" : 54,
"WindSpeed" : 98
}
]
let tbody = document.getElementById("tableBody")
let tr = document.createElement("tr");
let tdWaterLevel = document.createElement("td");
tdWaterLevel.innerHTML = properties[0].WaterLevel;
tr.appendChild(tdWaterLevel);
let tdWindSpeed = document.createElement("td");
tdWindSpeed.innerHTML = properties[0].WindSpeed;
tr.appendChild(tdWindSpeed);
let tdNumberOfMachine = document.createElement("td");
tdNumberOfMachine.innerHTML = properties[0].NumberOfMachine;
tr.appendChild(tdNumberOfMachine);
let tdFoodLevel = document.createElement("td");
tdFoodLevel.innerHTML = properties[0].FoodLevel;
tr.appendChild(tdFoodLevel);
let tdNumber = document.createElement("td");
tdNumber.innerHTML = properties[0].Number;
tr.appendChild(tdNumber);
tbody.appendChild(tr);
<table>
<thead>
<th>Water Level</th>
<th>Wind Sepeed</th>
<th>Number Of Machines</th>
<th>Food Level</th>
<th>Number</th>
</thead>
<tbody id="tableBody">
<!-- other razor code : for each x in model bla bla -->
</tbody>
</table>
您正在向客户端传递一个列表,我认为您应该只传递一个列表类型的对象,这样您就不必通过索引访问对象,而是可以这样做:properties.WaterLevel
如果您打算一次更新多个对象(请记住,我指的不是此时已经呈现的视图模型),那么您可以循环数组并按顺序添加每一行。
所以生命周期是:
- 使用现有对象列表渲染视图(对于模型中的每个对象)
- 客户添加新项目
- 服务器向所有客户端广播单个新项目
- 客户端使用新的单个项目更新现有视图
我正在努力将 SignalR 添加到工作的 MVC 应用程序中,但我不擅长 Javascript,而且我不知道如何编写它来显示我需要的内容。我看到了一堆关于聊天应用程序(发送消息)的教程,但这不是我需要的。
我想要的: 显示一个完整模型及其所有组件。 该程序收到一个文件,该文件在数据库中添加了一个新的 属性。 属性 必须显示其所有组件。
这是要显示的模型 (属性) 的示例
public int WaterLevel { get; set; }
public int WindSpeed { get; set; }
public int NumberOfMachine { get; set; }
public int FoodLevel { get; set; }
html 页面是使用 Visual studio: 创建的默认 html 页面 (这是网页上需要更新的javascript)
<tbody>
@foreach (var item in Model)
{
<tr id="properties_display">
<td id="waterLevel">
@Html.DisplayFor(modelItem => item.WaterLevel)
</td>
<td id="windSpeed">
@Html.DisplayFor(modelItem => item.WindSpeed)
</td>
<td id="numberMachine">
@Html.DisplayFor(modelItem => item.NumberOfMachine)
</td>
<td id="foodLevel">
@Html.DisplayFor(modelItem => item.FoodLevel)
</td>
<td id="number">
@Html.DisplayFor(modelItem => item.Number)
</td>
</tr>
}
</tbody>
观察者(On_Change):
private readonly IHubContext<PropertyHub> _hubContext;
public MyFileWatcher(IHubContext<PropertyHub> hubContext)
{
_hubContext = hubContext;
//watcher stuff
}
private void OnChanged(object sender, FileSystemEventArgs e)
{
DataAccess db = new DataAccess(MVCCoreAppDB);
string filePath = Path.GetFullPath(e.FullPath);
db.InsertData(filePath);
var propData = db.LoadData();
List<PropertyModel> properties = new List<PropertyModel>();
foreach (var row in propData)
{
properties.Add(new PropertyModel
{
WaterLevel = row.WaterLevel,
WindSpeed = row.WindSpeed,
NumberOfMachine = row.NumberOfMachine,
FoodLevel = row.FoodLevel
Number = row.Number
});
}
_hubContext.Clients.All.SendAsync("onFileChange", properties);
}
观察者 class 正在查找文件夹中的更改。创建或修改文件时,它会调用 3 个方法。首先,它将数据插入数据库,然后从数据库中加载数据并更改要使用的数据类型。
最后,它调用集线器让它知道有变化并且它需要更新网页。
这是我尝试过的,我得到了我需要的实时效果,但它没有显示正确的东西:
"use strict";
let connection = new signalR.HubConnectionBuilder()
.withUrl("/propertyHub")
.build();
connection.on("onFileChange", function (properties) {
let tr = document.createElement("tr");
document.getElementById("properties_display").appendChild(tr);
let tdWaterLevel = document.createElement("td");
tdWaterLevel.textContent = properties[0];
document.getElementById("waterLevel").appendChild(tdWaterLevel);
let tdWindSpeed = document.createElement("td");
tdWindSpeed.textContent = properties[1];
document.getElementById("windSpeed").appendChild(tdWindSpeed);
let tdNumberMachine = document.createElement("td");
tdNumberMachine.textContent = properties[2];
document.getElementById("numberMachine").appendChild(tdNumberMachine);
let tdFoodLevel = document.createElement("td");
tdFoodLevel.textContent = properties[3];
document.getElementById("foodLevel").appendChild(tdFoodLevel);
let tdNumber = document.createElement("td");
tdNumber.textContent = properties[4];
document.getElementById("number").appendChild(tdNumber);
});
connection.start().then(function () { TestConnection(); }).catch(function(err)
{
return console.error(err.toString());
});
function TestConnection() {
connection.invoke("GetConnectionId").catch(function (err) {
return console.error(err.toString());
});
}
就像现在一样,它在好的列中写入但不在 table 的末尾并且不写入值。它正在写对象 Object 而不是数字。 此外,当我写在 javascript;
properties.WaterLevel //For example
是放一个空白space而不是写东西
//It does the same thing if I write properties [0].WaterLevel
更新前的输出:
知道如何在合适的地方获得正确的值吗?
我认为您对 table 和 table 行的工作方式有点困惑。首先 html 应该看起来更像这样:
<tbody id="properties_display">
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.WaterLevel)
</td>
<td>
@Html.DisplayFor(modelItem => item.WindSpeed)
</td>
<td>
@Html.DisplayFor(modelItem => item.NumberOfMachine)
</td>
<td>
@Html.DisplayFor(modelItem => item.FoodLevel)
</td>
<td>
@Html.DisplayFor(modelItem => item.Number)
</td>
</tr>
}
</tbody>
id
应该在整个页面中是唯一的,但您正在为模型中的每个项目创建一个具有所有相同 ID 的新行。这不是好的做法,并且可能导致混淆您要添加到的元素。但这也意味着 属性 中的每个项目都应该有自己的行 (tr),因此您需要插入新行而不是添加到任何当前行,这就是您通过引用中的 id 所做的javascript.
实际上,由于您似乎是在更改时将所有数据发送回客户端,因此最简单的刷新方法是删除所有当前行并添加所有新行,如下所示:
connection.on("onFileChange", function (properties) {
let table = document.getElementById("properties_display");
// Remove all existing rows
while (table.firstChild) {
table.removeChild(table.firstChild);
}
// Add a new row for each item in properties
for (let i = 0; i < properties.length; i++) {
let tr = document.createElement("tr");
let tdWaterLevel = document.createElement("td");
tdWaterLevel.textContent = properties[i].waterLevel;
tr.appendChild(tdWaterLevel);
let tdWindSpeed = document.createElement("td");
tdWindSpeed.textContent = properties[i].windSpeed;
tr.appendChild(tdWindSpeed);
let tdNumberMachine = document.createElement("td");
tdNumberMachine.textContent = properties[i].numberMachine;
tr.appendChild(tdNumberMachine);
let tdFoodLevel = document.createElement("td");
tdFoodLevel.textContent = properties[i].foodLevel;
tr.appendChild(tdFoodLevel);
let tdNumber = document.createElement("td");
tdNumber.textContent = properties[i].number;
tr.appendChild(tdNumber);
table.appendChild(tr);
}
});
我还没有测试过,但这可能更符合您的要求。您将所有元素传递给 JavaScript 因此它需要遍历所有项目并获取它们各自的属性。您可能需要确保您的集线器数据正确传递到 javascript。如果不是,您可能需要在传递之前在服务器上转换为 Json,但我认为 SignalR 会自动处理这些东西。
你提到 properties.WaterLevel
没有 return 任何有意义的东西,因为属性是一个数组,但如果 properties[0].WaterLevel
那么可能是其他问题,我会记录javascript 中的属性对象,以查看您的服务器实际传递给浏览器的内容。
作为最后的评论,删除和替换所有行可能不是最好的主意。您可能会更好地重用现有行并添加需要的行,或者只传递来自服务器的更改元素而不是所有元素。只是一些想法。希望这有帮助。
根据您的情况,查看代码段。 您的 JavaScript 对象是一个包含单个对象的数组。
var properties = [
{
"FoodLevel" : 48,
"Id":0,
"MachineID": "1234567",
"Number" : 0,
"NumberOfMachine" : 32,
"WaterLevel" : 54,
"WindSpeed" : 98
}
]
let tbody = document.getElementById("tableBody")
let tr = document.createElement("tr");
let tdWaterLevel = document.createElement("td");
tdWaterLevel.innerHTML = properties[0].WaterLevel;
tr.appendChild(tdWaterLevel);
let tdWindSpeed = document.createElement("td");
tdWindSpeed.innerHTML = properties[0].WindSpeed;
tr.appendChild(tdWindSpeed);
let tdNumberOfMachine = document.createElement("td");
tdNumberOfMachine.innerHTML = properties[0].NumberOfMachine;
tr.appendChild(tdNumberOfMachine);
let tdFoodLevel = document.createElement("td");
tdFoodLevel.innerHTML = properties[0].FoodLevel;
tr.appendChild(tdFoodLevel);
let tdNumber = document.createElement("td");
tdNumber.innerHTML = properties[0].Number;
tr.appendChild(tdNumber);
tbody.appendChild(tr);
<table>
<thead>
<th>Water Level</th>
<th>Wind Sepeed</th>
<th>Number Of Machines</th>
<th>Food Level</th>
<th>Number</th>
</thead>
<tbody id="tableBody">
<!-- other razor code : for each x in model bla bla -->
</tbody>
</table>
您正在向客户端传递一个列表,我认为您应该只传递一个列表类型的对象,这样您就不必通过索引访问对象,而是可以这样做:properties.WaterLevel
如果您打算一次更新多个对象(请记住,我指的不是此时已经呈现的视图模型),那么您可以循环数组并按顺序添加每一行。
所以生命周期是:
- 使用现有对象列表渲染视图(对于模型中的每个对象)
- 客户添加新项目
- 服务器向所有客户端广播单个新项目
- 客户端使用新的单个项目更新现有视图