1 Answer, 1 is accepted
Hello Tu Tran,
Indeed, this functionality can be achieved by using the Telerik UI Form as per the following REPL example:
https://netcorerepl.telerik.com/cmkewrbA19zwm9hG43
In addition, this feature is already logged in our Feedback Portal. You can follow the progress here (feel free to cast your vote and follow it to receive status updates):
If you have any questions, don't hesitate to let me know.
Regards, Mihaela Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.
Hi Mihaela,
Thank you very much for sharing your demonstration.
I see the concept of implementing a dynamic field with Telerik Form. I will try to see if I can extend your code to have multiple fields can be added/removed at the same time instead of one field. For instance, I want to dynamically add the fields: First Name, Last Name, phone, and address (group together) simultaneously.
In fact, Telerik should have made a knowledge base for dynamically (add/remove) generating form fields available. This is not only showing how powerful Telerik UI controls can be leveraged, but it also helps developers find the actual implementation faster. This should be standard for most web applications. I know it is logic and how to implement it, but it does save the developers some time to research and familiar all Telerik APIs.
I truly appreciate your time and assistance.
Hi Tu Tran,
Thank you for your feedback. I am glad that the example is helpful.
I completely agree that this example is suitable for a KB article. I will create an article and share it here once it is published.
If you need further assistance with the dynamic form fields, feel free to let me know.
Hi Mihaela,
Your demonstration is great. I hope you can extend it to add/remove multiple form fields at once for the knowledge. I have been working on the expansion for adding and removing multiples fields. I was able to add three fields at the same time using "maskedtextbox", but I have not been able to get remove three fields at the same time. I wonder if there is a way to inject a div tag with unique ID as a wrapper to wrap three fields (e.g. first name, last name, address). This is probably easier to keep reference of the row without depending on "maskedtextbox". Any suggestion for me to accomplish multiple entries? Also, I want to implement one field as a Combobox (custom editor), I have a difficulty to convert the format of the code below to the inside of Push( ..... ) function.
i.Add() .Field(f => f.ComboBox) .Label(l => l.Text("ComboBox:").Optional(true)) .Editor(e => { e.ComboBox() .HtmlAttributes(new { }) .Placeholder("Select...") .DataTextField("ProductName") .DataValueField("ProductID") .HtmlAttributes(new { style = "width:100%" }) .Height(520) .DataSource(source => { source.Read(read => { read.Action("Items_GetProducts", "Form"); }) .ServerFiltering(true); }); });
AND a DropDownList format inside Push(........)
i.Add()
.Field(f => f.AppoinmentType)
.Label(l => l.Text("Appointment Type"))
.ColSpan(1)
.Editor(e =>
{
e.DropDownList()
.DataTextField("Text")
.DataValueField("Value")
.OptionLabel("Select Appointment Type")
.BindTo(new List<SelectListItem>()
{
new SelectListItem() {
Text = "Annual", Value = "1"
},
new SelectListItem() {
Text = "Fall/Spring", Value = "2"
},
new SelectListItem() {
Text = "Summer", Value = "3"
},
new SelectListItem() {
Text = "May", Value = "4"
}
});
});
Are there any examples that I can follow? I can reply on a JavaScript or JQuery to generate a dynamically add/remove form entries. However, I want to take potential/advanced built-in features with Telerik's form.
Again, I truly appreciate your insights and time.
Hello Tu Tran,
Basically, to add/remove multiple Form fields simultaneously you can follow the same approach from illustrated in the REPL example. For example, to remove multiple fields when a button is clicked:
- Handle the "click" event of the button;
- Get the "formData" and "items" objects through the Form options ($("#exampleForm").data("kendoForm").options);
- Update the "formData" and "items" objects by removing the required fields:
var formComponent = $("#exampleForm").data("kendoForm");
var formData = formComponent.options.formData;
for(var i = 2; i < formData.Phones.length; i++) { //loop through the available Phones fields and remove all accept the Phone 1 and Phone 2
formData.Phones.splice(i, 1);
}
- Populate the new Form items by adding the appropriate editor and its options (i.e., insert "TextBox" for the FirstName, "MaskedTextBox" for the Phone and so on). You can review the available Form editors here;
- Use the setOptions() method to apply the Form changes.
Alternatively, you could add/remove a group of multiple fields by adding/removing an object in the "items" object:
var formComponent = $("#exampleForm").data("kendoForm");
var formItems = formComponent.options.items; //returns an array of objects
var newItem = {
"type": "group",
"label": {
"text": "New Group of Items"
},
"items": [
{
"field": "FirstName2",
"label": {
"text": "First Name 2"
},
"validation": {},
"shouldRenderHidden": true
},
{
"field": "LastName2",
"label": {
"text": "Last Name 2"
},
"validation": {},
"shouldRenderHidden": true
},
{
"field": "Address2",
"editor": "TextBox",
"shouldRenderHidden": true
}
]
};
formComponent.options.items.push(newItem); //add a new group of fields. Ensure that the names of the Model properties are unique
formComponent.setOptions({ items: formComponent.options.items }); //Update the Form items options
Here you can review the format of the Form items object:
Regarding the ComboBox editor, it can be inserted in the Form items as follows:
var newItem = {
"field": "Country1",
"editor": "ComboBox",
"editorOptions": {
placeholder: "Select...",
dataTextField: "ProductName",
dataValueField: "ProductID",
height: 520,
dataSource: dataSource
},
"label": {"text": "Country"},
"shouldRenderHidden": false
};
var dataSource = new kendo.data.DataSource({
serverFiltering: true,
transport: {
read: {
url: '@Url.Action("Items_GetProducts", "Form")',
dataType: "json"
}
}
});
$("#exampleForm").data("kendoForm").options.items.push(newItem);
In terms of the DropDownList, check out this example: https://docs.telerik.com/kendo-ui/api/javascript/ui/form/configuration/items#itemseditoroptions
I hope these suggestions will be helpful to your scenario.
Hi Mihaela,
Thank you very much your patience, suggestions, and time.
I have followed your example from REPL, and I think I am almost getting close to what I need. I have not tried your alternative approach to add an object in "items" object.
As I follow the first approach by adding individual field. Adding the fields is not an issue for me, but when I try click on Remove, multiple rows are removed. I understand that I have to remove each individual field using splice and push functions. The issue I may have to keep track the index of row of the items. As you see in the screenshot (attached in this comment), if I click one of remove buttons (red arrow), all rows will be removed except the first row. It is supposed to remove the row when I click on the remove button. I will include my remove function here. Hopefully, you can point out what I am doing wrong.
The second issue is that every time I add a new field, the data of previous fields are refreshed and clear (empty). In other words, the data are not persisted (stay) when adding new fields. I have done extensive research and reading on Telerik's forum, I can't find a solution for it. The example from REPL does not persist data as well. In a traditional Web forms, it looks like post back and loss the data and need to rebind. How would I prevent this issue?
Your suggestions and example are very helpful, and I am learning Telerik's components. When you have a moment, could you please give me your advice? Thank you very much for your continued support.
function onRemoveBtnClick(e) { var formComponent = $("#exampleForm").data("kendoForm"); var formData = formComponent.options.formData; var speedTypeItems = formComponent.options.items[1].items; AccountSpeedType.splice(speedTypeId, 1); speedTypeItems.splice(speedTypeId, 1); var speedTypeLength = formData.AccountSpeedType.length; var newSpeedTypeItems = []; for(var i = 0; i < formData.AccountSpeedType.length; i++) { newSpeedTypeItems.push({ "field": `AccountSpeedType${i}`, "editor": "ComboBox", "editorOptions": { "autoBind":false, "dataTextField":"AccountSpeedType", "dataValueField":"AccountSpeedType", "height":520, "placeholder":"Select Speedtype", "filter":"contains", "dataSource": { "transport": { "read": { "url":"/request/?handler=Read", "data": dataFunction }, "prefix":"" }, "serverFiltering":true, "filter":[], "schema": { "errors":"Errors" } } }, // END OF COMBOBOX EDITOR OPTION "label": { "text": `Select Speedtype ${i + 1}` }, "shouldRenderHidden": false }); newSpeedTypeItems.push({ "field": `ProjectStartDate[${i}]`, "editor": "DatePicker", "label": { "text": `Funding Start Date ${i + 1}` }, "shouldRenderHidden": false }); newSpeedTypeItems.push({ "field": `ProjectEndDate[${i}]`, "editor": "DatePicker", "label": { "text": `Funding End Date ${i + 1}` }, "shouldRenderHidden": false }); newSpeedTypeItems.push({ "field": `PayAmountPerSpeedtype[${i}]`, "editor": "NumericTextBox", "editorOptions": { "Format": "c", "spinners": false, }, "label": { "text": `Pay Amount ${i + 1}` }, "shouldRenderHidden": false }); } // FOR Loop formComponent.options.items[1].items = newSpeedTypeItems; formComponent.setOptions({ formData: formData, items: formComponent.options.items }); insertRemovePhoneButton($('[data-role="numerictextbox"]')); }
Hi Tu Tran,
Your feedback is much appreciated!
1) When creating the "remove" button for each input, assign an id to each button (i.e., "removeSpeedTypeBtn_{index}"). Then you can get the index of the element that should be removed in the "click" event handler of the button:
function onRemoveBtnClick(e) {
var speedTypeId = $(e.sender.element).attr("id").split("_")[1]; //get the id of the clicked icon
var formComponent = $("#exampleForm").data("kendoForm");
var formData = formComponent.options.formData;
var speedTypeItems = formComponent.options.items[1].items;
formData.speedType.splice(speedTypeId, 1); //ensure that the removed item is removed from the formData object
speedTypeItems.splice(speedTypeId, 1);
....
}
function insertRemovePhoneButton(fields) {
$.each($(fields), function(i,v){
var deletePhoneBtn = `<em id="removePhoneBtn_${i}"></em>`; //set an id attribute to each remove button
$(deletePhoneBtn).insertAfter($(this));
$(`#removePhoneBtn_${i}`).kendoButton({
icon: "trash",
click: onRemoveBtnClick
});
});
}
2) To persist the form data, you can access the data through the form model and update the formData object when adding/removing items. For example:
function onRemoveBtnClick(e) {
var phoneId = $(e.sender.element).attr("id").split("_")[1];
var formComponent = $("#exampleForm").data("kendoForm");
var formData = formComponent.options.formData;
var phoneItems = formComponent.options.items[1].items;
var currnetPhonesData = formComponent._model.Phones; //get the current entered phones
formData.Phones.splice(phoneId, 1);
phoneItems.splice(phoneId, 1);
currnetPhonesData.splice(phoneId, 1);
...
for(var i = 0; i < formData.Phones.length; i++) { //loop through the formData Phones and set their values
formData.Phones[i] = currnetPhonesData[i];
}
...
}
function onAddPhone(e) {
var formComponent = $("#exampleForm").data("kendoForm");
var formData = formComponent.options.formData;
var phoneItems = formComponent.options.items[1].items;
var currnetPhonesData = formComponent._model.Phones;
var totalPhones = phoneItems.length;
formData.Phones.push("");
...
for(var i = 0; i < formData.Phones.length; i++) {
if(currnetPhonesData[i] != formData.Phones[i]) {
formData.Phones[i] = currnetPhonesData[i];
}
}
....
}
Here is the revised REPL example for your reference:
https://netcorerepl.telerik.com/cmkpPvlX28qLVQka29
I hope these suggestions will be helpful.
Your assumption is correct - $(".k-form-fieldset").slice(-1) selects the last fieldset element (class "k-form-fieldset").
For more information about the jQuery slice() method, refer to the jQuery API:
For example, if you would like to show the second Form group:
$(".k-form-fieldset").slice(1,2).show();
Hi,
Apologies for the necropost, but I'm trying to get this exact solution to work using TypeScript and the Kendo TS definitions.
var currentAnalystData = formComponent._model.SmeIds;
The TypeScript definitions show `_model` as undefined and won't render to JavaScript.
Any ideas on what needs to be done to correct this?
Thanks!
Hi Brian,
You could get the "_model" information from the "formData" object:
var currnetPhonesData = formComponent?.options.formData?.Phones;