【问题标题】:How do you write Javascript to display MVC models with SignalR? (Javascript receives properties in parameter)你如何编写 Javascript 来使用 SignalR 显示 MVC 模型? (Javascript 在参数中接收属性)
【发布时间】:2019-12-21 22:11:25
【问题描述】:

我正在努力将 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);
    }

观察者类正在寻找文件夹中的变化。创建或修改文件时,它会调用 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());
     });
}

就像现在一样,它写在好的列中,但不是在表的末尾,也不是写值。它正在写对象而不是数字。 此外,当我在 javascript 中编写时;

properties.WaterLevel  //For example

它是放置一个空格而不是写一些东西。

//It does the same thing if I write properties [0].WaterLevel

更新前的输出: 更新后的输出: 所需的输出: 添加新文件后调试中的属性值:

知道如何在一个好地方获得正确的值吗?

【问题讨论】:

  • 能否将“console.log(JSON.stringify(properties))”的输出粘贴到“onFileChange”函数中
  • 向我们展示一些输出,请它在浏览器中显示什么?

标签: javascript c# asp.net-mvc asp.net-core signalr


【解决方案1】:

我认为您对表格和表格行的工作方式有些困惑。首先,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),因此您需要插入新行而不是添加到任何当前行,这是您通过引用 javascript 中的 id 所做的事情。

实际上,由于您似乎是在更改时将所有数据发送回客户端,因此最简单的刷新方法是删除所有当前行并添加所有新行,如下所示:

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 没有返回任何有意义的东西,因为属性是一个数组,但是如果properties[0].WaterLevel 可能是其他问题,我会在 javascript 中记录属性对象以查看您的服务器是什么实际传递给浏览器。

作为最后的评论,删除和替换所有行可能不是最好的主意。您可能会更好地重用现有行并添加需要的行,或者仅从服务器传递更改的元素而不是全部传递。只是一些想法。希望这会有所帮助。

【讨论】:

  • 感谢您的回复,它非常清晰且很有帮助。我的代码有一些问题,你注意到了,所以我有时间改变一些东西。现在我的 InsertData 返回刚刚插入的数据,这样我就不必获取数组了。我可以传递一个项目,这样我就可以做 property.WaterLevel。
  • @Michaeldc 很高兴它有帮助。我不确定您是希望获得所有行还是只获得更新的行。
  • 您是否知道为什么网页上没有写入任何值?当我在调试中查找它时,我传递的属性具有良好的值。当我使用 innerHTML 时,它会写入“未定义”,而不是用 textContent 写入任何内容。我可以写 tdWaterLevel.textContent = property.WaterLevel 吗?还是我必须写点别的?
  • @Michaeldc 你的意思是当你在调试中查找它到达 JavaScript 代码后你可以看到好的值?如果您可以在到达 javascript 代码后看到这些值,那么应该找到正确的参考路径来获取值
  • 我在connection.on函数中做了一个console.log,它在控制台中显示了数据。所以它在 property.WaterLevel 调用中被破坏了?
【解决方案2】:

根据您所拥有的,请参阅 sn-p。 您的 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

如果您打算一次更新多个对象(请记住,我指的不是此时已经渲染的视图模型),那么您可以循环数组并按顺序添加每一行。

所以生命周期是:

  • 使用现有对象列表渲染视图(模型中的每个对象)
  • 客户端添加新项目
  • 服务器向所有客户端广播单个新项目
  • 客户端使用新的单个项目更新现有视图

【讨论】:

  • 感谢您的回答。我尝试了提供的代码,但它在网页上写入“未定义”而不是显示数字。你会碰巧知道是什么原因造成的吗?我稍微改变了我的观察者,所以我只得到了在 javascript 中添加的最后一个属性,当我调试时,我看到了好的值。但它只是写未定义。如果我使用 textContent 而不是 innerHTML,它不会写任何东西。
  • 问题解决了,它必须是 javascript 中的驼峰式大小写。类似的东西:properties.waterLevel。谢谢!
  • 没有问题,这是什么旅程!
猜你喜欢
  • 1970-01-01
  • 2013-07-12
  • 2020-05-10
  • 2013-03-23
  • 2016-01-01
  • 1970-01-01
  • 2019-05-17
  • 2011-04-16
  • 2014-07-31
相关资源
最近更新 更多