Open In App

Design a form component which takes input from its user and displays a form

Last Updated : 14 Feb, 2020
Improve
Improve
Like Article
Like
Save
Share
Report

Design: Here are the things that it should support:

  • Grouping of the fields of the form
  • Validations for fields
  • Submit button

For example, the user might want to validate if the input is a valid address and might want this component to have default validations for email, etc.
Let’s try to create a very basic data model for our component.

For our minimalistic implementation, we might want to use these fields.

  • Id: The id for the field
  • Type: text/dropdown etc
  • Subtype: For input type text we might want password etc
  • Values: for dropdown/Radio
  • Validator: the custom validator provided by the user. We can use the value of “email” etc to provide default validation.
  • Group: the group to which this field should belong
  • Placeholder: placeholder if needed for the field.
  • Handler: to provide a callback/option for the submit button click

For this, we would try to use the MVC pattern in JS. First, let’s develop our model class.

For our minimalistic implementation, we will keep the input for this component in our model class.
Our model will have input for the component which will be hardcoded now and an event that will notify that our input has changed.
Here is the minimalistic implementation




function ListModel() {
    this._formInput=[ {
        "id": "id1",
        "type":"text",
        "name":"name",
        "placeholder":"name",
        "values":"",
        "validator":function () {
            console.log("test1");
        }
        ,
        "group":"gr1"
    }
    ,
    {
        "id": "id2",
        "type":"dropdown",
        "name":"name",
        "placeholder":"name",
        "values":[1,
        2,
        3],
        "validator":function () {
            console.log("test2");
        }
        ,
        "group":"gr2"
    }
    ,
    {
        "id": "id3",
        "type":"radio",
        "name":"name",
        "placeholder":"name",
        "values":["male",
        "female"],
        "validator":function () {
            console.log("test");
        }
        ,
        "group":"gr3"
    }
    ,
    {
        "id":"id4",
        "type":"button",
        "name":"name",
        "placeholder":"name",
        "values":"",
        "handler": function () {
            console.log("test3");
        }
        ,
        "group":"gr3"
    }
    ];
    this.itemAdded=new Event(this);
}
  
ListModel.prototype= {
    addItem: function () {
        this.itemAdded.notify();
    }
    ,
    getFormInput: function () {
        return [].concat(this._formInput);
    }
    ,
}
  
;


Here the event class is basic pub/sub implementation where each module can react to an event. Here is the event class




function Event(sender) {
    this._sender = sender;
    this._listeners = [];
}
Event.prototype = {
    attach: function(listener) {
        this._listeners.push(listener);
    },
    notify: function(args) {
        var index;
        for (index = 0; index < this._listeners.length; index += 1) {
            this._listeners[index](this._sender, args);
        }
    }
};


Now coming to our controller. For our sample use case, we have triggered items added to the model on controller initialization only.
Below is the class:




function ListController(model, view) {
    this._model = model;
    this._view = view;
    var _this = this;
    _this.returnResult();
}
ListController.prototype = {
    returnResult : function () {
        this._model.addItem();
    }
};


Now coming to our main class which will eventually render the form which is our view class.

Here is the view class:




function ListView(model) {
    this._model = model;
    var _this = this;
    this._model.itemAdded.attach(function () {
        _this.addResult();
    });
    this._validators = [];
}


Here we store the model(which in our case is the input to this component) and the validators for each field.
Also, we attach the event handler to our model change which will create the view. Here is the complete code




ListView.prototype = {
    addResult: function() {
        var data = this._model.getFormInput();
        this.createContent(data);
    },
    createContent: function(data) {
        var totalFieldGroup = [];
        var fieldGroup = [];
        var groups = {};
        for (var itr = 0; itr < data.length; itr++) {
            if (!totalFieldGroup.includes(data[itr]["group"]))
                totalFieldGroup.push(data[itr]["group"]);
            if (typeof data[itr]["validator"] == "function")
                this._validators.push(data[itr]["validator"]);
        }
        var form = document.createElement("form");
        for (var j = 0; j < totalFieldGroup.length; j++) {
            fieldGroup.push(data.filter(function(individual) {
                if (individual["group"] == totalFieldGroup[j]) {
                    return true;
                } else {
                    return false;
                }
            }));
            var fieldset = document.createElement("fieldset");
            var legend = document.createElement("legend");
            legend.innerHTML = fieldGroup[0][0]["group"];
            fieldset.appendChild(legend);
            this.createComponent(fieldGroup, fieldset);
            form.appendChild(fieldset);
            fieldGroup = [];
        }
        var subbtn = data.filter(function(individual) {
            if (individual["type"] == "button") {
                return true;
            } else {
                return false;
            }
        });
        if (subbtn.length == 0) {
            alert("no submit button found");
            return;
        }
        var btn = this.addSubmitButton(subbtn, form);
        form.appendChild(btn);
        form.setAttribute("id", "parentForm");
        document.body.appendChild(form);
    },
    addSubmitButton: function(subbtn, form) {
        var x = document.createElement("button");
        x.setAttribute("type", "submit");
        x.innerHTML = "Submit";
        var self = this;
        x.addEventListener("click", function() {
            for (var i = 0; i < self._validators.length; i++) {
                self._validators[i]();
            }
            subbtn[0]["handler"]();
            form.submit();
        });
        return x;
    },
    createComponent: function(fieldGroup, fieldset) {
        var ret = fieldset;
        for (var i = 0; i < fieldGroup.length; i++) {
            switch (fieldGroup[0][i].type) {
                case "text":
                    var label = document.createElement("label");
                    label.innerHTML = fieldGroup[0][i].name;
                    var x = document.createElement("INPUT");
                    x.setAttribute("type", "text");
                    x.setAttribute("placeholder"
                                   fieldGroup[0][i].placeholder);
                    x.setAttribute("id", fieldGroup[0][i].id);
                    fieldset.append(label);
                    fieldset.append(x);
                    break;
                case "dropdown":
                    var label = document.createElement("label");
                    label.innerHTML = fieldGroup[0][i].name;
                    var select = document.createElement("select");
                    select.setAttribute("id", fieldGroup[0][i].id);
                    for (var k = 0; k < fieldGroup[0][i].values.length; k++) {
                        var option = document.createElement("option");
                        option.innerHTML = fieldGroup[0][i].values[k];
                        select.appendChild(option);
                    }
                    select.setAttribute("placeholder"
                                        fieldGroup[0][i].placeholder);
                    fieldset.append(label);
                    fieldset.append(select);
                    break;
                case "radio":
                    var label1 = document.createElement("label");
                    label1.innerHTML = fieldGroup[0][i].name;
                    var label = document.createElement("radiogroup");
                    for (var k = 0; k < fieldGroup[0][i].values.length; k++) {
                        var radio = document.createElement("input");
                        radio.type = "radio";
                        radio.label = fieldGroup[0][i].values[k];
                        radio.innerHTML = fieldGroup[0][i].values[k];
                        radio.value = fieldGroup[0][i].values[k];
                        label.appendChild(radio);
                    }
                    label1.appendChild(label);
                    fieldset.append(label1);
                    break;
            }
        }
    }
};


Here is how we instantiate the component




window.onload = function() {
    var model = new ListModel([]),
        view = new ListView(model),
        controller = new ListController(model, view);
};


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads