1

Requirement: I will be getting json data using REST service from a server. That json data will tell me the what type of controls (ControlID) to present to the user and what is that data that needs to be inserted in those controls. So, the basic need is to have multiple client-side controls and when a view will load, it should make a layout depending on the controls and the data that needs to be presented. It is sort of dashboard concept where all the tiles are kind of custom controls eg. iGoogle.

The json data that I will get from server will include collection of controlId property and other property with values to customise it and the data that needs to go into the control. eg:

{
  controlID: 'xxx',
  fontSize: 2
}

So, as such I have to work with multiple control IDs, I think I will have to manage a configuration file which can have id and respective module/widget/control's name or path in a folder so as to load.

What I have researched and implemented: I have started researching for Single Page App and for now I’m using Durandaljs with asp.net mvc5. Below is the code using which I have created two widgets - a “label” and a “textbox”. There is not any fancy customisation in these two but I just want to see how can I achieve this. I’ve started looking into widgets as I believe its a good option to go - a separate view and processing logic - view and viewmodel per widget. I may be wrong if there is any another option available for my requirement.

For now, I am just using those two widgets from my sample view and passing on some properties from its view model.

My questions - Please answer with samples/gist/jsfiddle if possible.

  • However, I do want to load a widget say “A” and this widget should also then use widgets label and textbox. Logically, its going to be a custom component made up of other components. The idea is to divide a big widget into different small widgets to that they can work as standalone with its unique customisation and a view and they can be grouped together to be presented to the user.

  • Is there any other alternative to durandaljs widgets that I should research ? please give examples if possible.

  • As I have mentioned above I need to read the json data from server to make each view and identify which controls to load. Until now, I have only worked with static html but this is kind of different and new to me. Could anyone please help me to find a way of loading these custom widgets on demand as per the json data from server.

Code for Widgets and main view in the app:

Widget - label

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;
});

Widget - textbox

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;
});

Main html page where I'm using the above widgets:

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>
KKS
  • 3,600
  • 1
  • 27
  • 54

1 Answers1

0

Simple solutions

I'll make it rather short now however I can provide You more complex solution later ( in about 4-5 hours) if it doesn't meet Your requirements / someone else answers.

Multiple widgets data sharing

Sharing data between widgets is rather simple - You just declare knockout observable property in view model of view containing widgets and pass it as setting to both widgets.

Dynamic widget rendering

For this I would change Your widget binding syntax.

<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>

Where widget1 and widget2 are observables in viewmodel. They can be easily set from JSON data loaded from server. Although You will probably need to make some convention about widget input data.

Edit + Complex Solution

  1. You need some way of mapping control Id into widget type. Since I don't know Your application logic ( You may encode mapping information in client or use some other REST service) I would assume that function mapIdToType(controlId) takes control Id as input and returns name of widget type.
  2. According to Your requirements I would move from classic Durandal widgets into subviews ( which would work exactly same way as widgets) as You can use with them compose binding which is made to work dynamically.
  3. Let's assume that You have module responsible for loading presented JSON data ( let's call it widgetLoader) which has function loadWidgets()
  4. Let's assume that You have follwing Durandal project structure:

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

  1. Let's create viewmodel of view which contains widgets

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]);

                });
            });
        }
    }
});

View:

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

Hope it will work for Your requirements.

Krzysztof Cieslak
  • 1,695
  • 13
  • 14
  • I have just added a sample json data that I am expecting from server. It doesn't give any info of the type instead it just tell me about the controlid and values to other properties that I should use to customise it. – KKS Jan 14 '14 at 13:46
  • I will be getting json data from server that will have collection of controlIDs. I will have to manage a config where to map those id with widget names or something. how can I achieve this to load those widgets ondemand? – KKS Jan 14 '14 at 15:23
  • I am working with your approach. There is much processing that I need to do before binding as even the containers of the widget is dynamic, i.e. in the json data from server. So, for now I am confused what bits to do at client/server!! As I have to make some api calls and process data which can be an performance issue on client. any guidance on dynamic containers and where to make api calls/process on client/server? – KKS Jan 15 '14 at 17:03
  • @Yoda, It really depends on Your requirements and architecture. For sure I would change primary data You get - If You include there also widget type. You will make one request less to server ( if mapping between controlId and widget type is done on server). About dynamic containers - again I don't know Your requirements - I would just go deep into Knockout - foreach and if bindings ( maybe some custom bindings if it's not enough). And I would not worry so much about performance without test - modern PCs and web browesers are really fast – Krzysztof Cieslak Jan 16 '14 at 13:19