如何基于json数据在视图中动态加载自定义客户端控件/窗口小部件?

时间:2014-01-14 12:44:37

标签: javascript knockout.js durandal single-page-application durandal-2.0

要求:我将从服务器使用REST服务获取json数据。该json数据将告诉我向用户呈现的控件类型(ControlID)以及需要在这些控件中插入的数据是什么。因此,基本需要是有多个客户端控件,当视图加载时,它应该根据控件和需要显示的数据进行布局。它是一种仪表板概念,其中所有的瓷砖都是一种自定义控件,例如。的iGoogle。

我将从服务器获取的json数据将包含controlId属性的集合和其他属性,其中包含用于自定义它的值以及需要进入控件的数据。例如:

{
  controlID: 'xxx',
  fontSize: 2
}

因此,我必须使用多个控件ID,我想我必须管理一个配置文件,该文件可以在文件夹中具有id和相应的模块/窗口小部件/控件的名称或路径以便加载。

我研究和实施的内容:我已经开始研究单页应用程序,现在我正在使用带有asp.net mvc5的Durandaljs。下面是我用它创建了两个小部件的代码 - “标签”和“文本框”。这两个没有任何花哨的定制,但我只是想看看我怎样才能做到这一点。 我开始研究小部件,因为我认为它是一个很好的选择 - 一个单独的视图和处理逻辑 - 每个小部件的视图和视图模型。如果我的要求有其他选择,我可能会错。

现在,我只是在我的示例视图中使用这两个小部件,并从其视图模型中传递一些属性。

我的问题 - 请尽可能使用样本/ gist / jsfiddle回答。

  • 但是,我确实想要加载一个小部件说“A”,然后这个小部件也应该使用小部件标签和文本框。从逻辑上讲,它将成为由其他组件组成的自定义组件。我们的想法是将一个大的小部件分成不同的小部件,以便它们可以独立工作,具有独特的自定义和视图,并且可以将它们组合在一起以呈现给用户。

  • 我应该研究durandaljs小部件的其他替代品吗?如果可能请举例。

  • 正如我上面提到的,我需要从服务器读取json数据以生成每个视图并识别要加载的控件。到目前为止,我只使用静态HTML,但这对我来说有点不同。任何人都可以帮我找一种根据服务器的json数据按需加载这些自定义小部件的方法。

应用中的小部件代码和主视图:

小工具 - 标签

HTML:

<div data-bind="foreach: { data: settings.items }">
<br />
<label data-bind="text: $data.TextValue, style: { color: $data.TextColour }"></label>

的javascript:

"use strict";

define(['durandal/composition', 'jquery'], function(composition, $) {
    var ctor = function () { };

    ctor.prototype.activate = function(settings) {
        this.settings = settings;
    };

    //ctor.prototype.getLabelText = function(item) {
    //    if (this.settings.textValue) {
    //        return item[this.settings.textValue];
    //    }

    //    return item.toString();
    //};

    return ctor;
});

小工具 - 文本框

HTML:

<div data-bind="foreach: { data: settings.items }">
    <br />
    <input type="text" data-bind="value: $data.TextValue, style: { color: $data.TextColour }" />
</div>

的javascript:

"use strict";

define(['durandal/composition', 'jquery'], function(composition, $) {

    var ctor = function() {};

    ctor.prototype.activate = function(settings) {
        this.settings = settings;
    };

    return ctor;
});

我正在使用上述小部件的主html页面:

HTML:

    <div class="well">
        <h2>SimpleLabel</h2>
        <div data-bind="simplelabel: {items:labels}"></div>
    </div>
    <hr />
    <div class="well">
        <h2>SingleLine Textbox</h2>
        <div data-bind="singlelinetextbox: {items:boxes}"></div>
    </div>

1 个答案:

答案 0 :(得分:0)

简单解决方案

如果它不符合您的要求/其他人的答案,我现在可以让它变得更短,但是我可以在以后(大约4-5小时)为您提供更复杂的解决方案。

多个小部件数据共享

在窗口小部件之间共享数据非常简单 - 您只需在包含窗口小部件的视图模型中声明knockout observable属性,并将其作为设置传递给两个窗口小部件。

动态窗口小部件呈现

为此,我将更改您的小部件绑定语法。

<div id="widget1placeholder">
        <h2 data-bind="Text: widget1.text"></h2>
        <div data-bind="widget: {kind:widget1.type, items:widget1.data}">/div>
    </div>
    <hr />
    <div id="widget2placeholder">
        <h2 data-bind="Text: widget2.text"></h2>
        <div data-bind="widget: {kind:widget2.type, items:widget2.data}">/div>
    </div>

其中widget1和widget2是viewmodel中的可观察对象。可以从服务器加载的JSON数据轻松设置它们。虽然您可能需要制定一些关于窗口小部件输入数据的约定。

编辑+复杂解决方案

  1. 您需要某种方法将控件ID映射到窗口小部件类型。由于我不知道您的应用程序逻辑(您可以在客户端编码映射信息或使用其他一些REST服务),我会假设函数mapIdToType(controlId)将控件ID作为输入并返回窗口小部件类型的名称。
  2. 根据您的要求,我会从经典的Durandal小部件转移到子视图(它的工作方式与小部件完全相同),因为您可以使用它们构成动态工作的绑定。
  3. 我们假设您有模块负责加载具有函数widgetLoader
  4. 的呈现的JSON数据(我们称之为loadWidgets()
  5. 我们假设您已经掌握了Durandal项目结构:

  6. /app
       /viewmodels
            /dashboard.js
            /widgetA.js
            /widgetB.js
            /widgetC.js
       /views
            /dashboard.html
            /widgetA.html
            /widgetB.html
            /widgetC.html 
    

    1. 让我们创建包含小部件的视图的视图模型

    2. define(['knockout', 'service/widgetLoader', 'viewmodels/widgetA', 'viewmodels/widgetB', 'viewmodels/widgetC'], 
      function (ko, widgetLoader, WidgetA, WidgetB, WidgetC) {  
          var widget1 = ko.observable();
          var widget2 = ko.observable();
      
          function getWidget(item)
          {
              if (mapIdToControll(item.controlId) === "WidgetA")
                  return new WidgetA(item));
              else if (mapIdToControll(item.controlId) === "WidgetB")
                  return new WidgetB(item));
              else if (mapIdToControll(item.controlId) === "WidgetC")
                  return new WidgetC(item));
              else
                  throw new Error("Unknown widget type");
          }
      
          return {
              widget1: widget1,
              widget2: widget2,
              activate: function() {
                  widgetLoader.loadWidgets().then(function(result) {
                      widget1 = getWidget(result[0]);
                      widget2 = gwtWidget(result[1]);
      
                      });
                  });
              }
          }
      });
      

      查看:

      <div>
      <div data-bind="compose: widget1"></div>
          <div data-bind="compose: widget2"></div>
      </div>
      

      希望它能满足您的要求。