Add textboxes dynamically in ASP.NET MVC - c#

I work on an ASP.NET MVC project that I need some help with. I need to be able to create x number of textboxes when the user click "add textbox". When the user enter the page a viewmodel is loaded. This viewmodel need to handle the x number of textboxes that the user create when he is on the page so that when the page is posted these textboxes are part of the model. The model should look something like this..
public class PlanViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public List<EventViewModel> EventList { get; set; } // this should be the list of textboxes that the user "create" by clicking add new
}
public class EventViewModel
{
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Description { get; set; }
}
I'm kinda lost on how to do this so any help is appreciated.
UPDATE
I've added this javascript that add textboxes client side..
<script type="text/javascript">
function GetDynamicTextBox(value) {
return('<input type="text" name="events[0].Key" value="box1" /><input type="text" name="events[0].Value.StartDate" value="box2"/><button type="button" class="btn btn-sm btn-primary" onclick="RemoveTextBox(this)"><i class="fa fa-angle-right"></i> Remove</button>');
}
function AddTextBox() {
var div = document.createElement('DIV');
div.innerHTML = GetDynamicTextBox("");
document.getElementById("divcontent").appendChild(div);
}
function RemoveTextBox(div) {
document.getElementById("divcontent").removeChild(div.parentNode);
}
</script>
<div id="divcontent" class="form-group">
<button type="button" class="btn btn-sm btn-primary" onclick="AddTextBox()"><i class="fa fa-angle-right"></i> Add</button>
</div>
I think I only need to add unique id's for the textboxes like this...
events[0].Key
events[1].Key
events[2].Key
and so on..
But I don't know how. Anyone knows?

You can add a list of String, like this
public String[] MyTextFields
and then create HTML using Javascript, like this:
<input name="myTextFields[0]"></input>
<input name="myTextFields[1]"></input>

In Razor view:
#for (var i = 0; i < Model.EventList.Count; i++)
{
#Html.EditorFor(x => Model.EventList[i].Name)
}
To set the name attribute of all edited elements in javascript, this is to be called on page load, and any time the collection changes (item is added or removed):
var children = document.getElementById("myDIV").children; // TODO: here supposing children are the input elements, may be different on your page (they may be nested in a different way)
for (i = 0; i < children.length; i++)
{
var el = children[i];
el.name = 'EventList[' + i + '].Name'; // TODO: look into source code of the generated page for the actual format of existing elements and then modify the mask accordingly
el.id = 'EventList[' + i + '].Name';
}

If it is ok to have JS dependency than I suggest to use light Knockout library. It will help you to create/edit/delete your inputs. Check example in JS fiddle.
Use HTML to adjust your view. Tag data-bind lets you to bind to data and events
<button data-bind="click: addInput">Add</button>
<div data-bind="foreach: inputs">
<input data-bind="value: text"/><br />
</div>
<button data-bind="click: proceed">Proceed</button>
<!-- Use Knockout JS library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
Then small JS script which handles adding new input and processing data on click.
function InputData(text) {
let self = this;
self.text = text;
}
function InputViewModel() {
let self = this;
// Your array of HTML inputs
self.inputs = ko.observableArray([new InputData("Default value")]);
self.output = ko.observable();
self.addInput = function() {
// Dynamically adds new input on user click button "Add"
self.inputs.push(new InputData(""));
};
self.proceed = function() {
// Process all input with their values
for (var i = 0; i < self.inputs().length; i++) {
console.log(self.inputs()[i].text);
}
}
}
// Bind our JS to HTML view
ko.applyBindings(new InputViewModel());

Related

add list of dynamic components in blazor

I just started to have a look in blazor (v0.3) and doing some test I wanted to add a list using blazor
First I created a List<string> to test a simple list in the same page
<ul>
#foreach (var item in listItems)
{
<li>#item</li>
}
</ul>
#functions {
private List<string> listItems = new List<string>();
private string newItem;
private void AddItem()
{
if (string.IsNullOrEmpty(newItem))
return;
listItems.Add(newItem);
newItem = "";
}
}
this is working fine, is adding every element to the list when I add it. but then, i tried to add components, add a single component was easy, based on this question here but for a list I had the next problem:
I created a <li> compontent just to test the functionality of components, here is the component view
<li id="#ID">
#Text
</li>
#functions {
[Parameter]
string Text { get; set; }
[Parameter]
string ID { get; set; }
}
then in the parent view
<input type="text" bind="TxtExample" name="inpAdd"/>
<button onclick="#addCompoment">add comp1</button>
<div class="simple-list-list">
#if (!componentListTest.Any())
{
<p>You have no items in your list</p>
}
else
{
<ul>
#foreach (var item in componentListTest)
{
#item
}
</ul>
}
</div>
#functions {
private List<RenderFragment> componentListTest { get; set; }
private int currentCount {get; set;}
private string TxtExample { get; set; }
protected override void OnInit()
{
currentCount = 0;
componentListTest = new List<RenderFragment>();
}
protected void addCompoment()
{
componentListTest.Add(CreateDynamicComponent(currentCount));
currentCount++;
}
RenderFragment CreateDynamicComponent(int counter) => builder =>
{
var seq = 0;
builder.OpenComponent(seq, typeof(listExample));
builder.AddAttribute(++seq, "Text", "text -- "+TxtExample);
builder.AddAttribute(++seq, "id","listed-"+counter);
builder.CloseComponent();
};
}
when I load the fist element is loaded correctly:
but when I entered the second one, all of them are replaced for the last one:
Any idea whats going on?
You are making it too complicated. You don't need to dynamically instantiate components for this scenario to work.
You can just do a:
<ul>
#foreach (var item in listItems)
{
<myComponent bind-myVar="#item"></myComponent>
}
</ul>
And the components will be instantiated for you.
Also see here how to make the parameters work on your component.
This is because TxtExample is global to the component. When Blazor detects a potential UI change, it recalculates the entire component and updates the DOM with any differences. So when you change the textbox, TxtExample is updated and then the Razor is recalculating, inserting the new value of TxtExample for all rows.

How to validate dynamic controls in .net core

I am creating the survey application for which I am creating the controls dynamically(Like checkbox,radio-button,textbox etc).
Each question will have the controls depending upon the control type assigned to the question and on question type the answer choices(checkbox, radio button) will be rendered.
On Next/Previous navigation I am storing current page answers in the database. While navigating the page I am doing ajax call for database saving and my UI/controls are NOT in the form.
I have created ViewModel based on my LMS_SurveyQuestions and LMS_SurveyQuestionOptionChoice table.
So, while creating the UI in view in for loop, I have directly assigned SurveyQuestionOptionChoiceID as Control ID while creating the AnswerChoice controls and stored the same in the table SurveyUserAnswer table.
Model
public class LMS_TraineeSurveyPaginationViewModel
{
public List<LMS_SurveyQuestions> SurveyQuestions { get; set; }
public List<LMS_SurveyQuestionOptionChoice> SurveyQuestionOptionChoice { get; set; }
public SurveyPager Pager { get; set; }
}
and this is how I rendered the view
#foreach (var item in Model.SurveyQuestions)
{
foreach (var data in Model.SurveyQuestionOptionChoice.Where(x => x.SurveyQuestionID == item.SurveyQuestionID).ToList())
{
if (item.QuestionTypeID == QuestionType.RadioButton)
{
<li style="list-style:none;">
<input type="radio" name="rb" id="#data.SurveyQuestionOptionChoiceID" />
<span>#data.OptionChoice</span>
</li>
}
else if (item.QuestionTypeID == QuestionType.CheckBox)
{
<li style="list-style:none;">
<input type="checkbox" id="#data.SurveyQuestionOptionChoiceID" name="#data.SurveyQuestionOptionChoiceID" " />
<span>#data.OptionChoice</span>
</li>
}
}
}
and while saving the answer into database I have created the JSON/JS array as model for SurveyUserAnswer and saved it into database as follows. Below is example for radio button
function SaveValues() {
var surveyQuestion = #Html.Raw(Json.Serialize(Model.SurveyQuestions.ToArray()));
var surveyQuestionOptionChoide = #Html.Raw(Json.Serialize(Model.SurveyQuestionOptionChoice.ToArray()));
for (item in surveyQuestion) {
var surveyQuestionID=surveyQuestionViewModel[item].SurveyQuestionID;
var filteredData = surveyQuestionOptionChoide.filter(function(filteredItem) {
return (filteredItem.SurveyQuestionID==surveyQuestionID);
});
for (optionChoice in filteredData) {
if(surveyQuestion[item].QuestionTypeID=='#QuestionType.RadioButton') {
if (($('#'+SurveyQuestionOptionChoiceID).prop("checked"))) {
surveyUserAnswer.push({ SurveyUserAnswerID: filteredData[optionChoice].SurveyUserAnswerID==null?0:filteredData[optionChoice].SurveyUserAnswerID,
SurveyQuestionOptionChoiceID: SurveyQuestionOptionChoiceID,SurveyUserID:'#ViewBag.SurveyUserID',AnswerText:null,
MarksObtained:filteredData[optionChoice].Marks,WhenCreated:'#DateTime.UtcNow',WhoCreated:'#tenant.UserID'});
}
}
}
}
$.post('#Url.Action("GetTraineeSurvey", "Survey")', {SurveyID:surveyID,page:page, surveyUserAnswer: surveyUserAnswer,PrevBranchQuestionPage:currentPage,IsBranchQuestionAvailable:IsBranchQuestionAvailable }, function (data) {
$('#surveyModalContent').html('');
$('#surveyModalContent').html(data);
$("#surveyModal").modal('show');
}).fail(function() {
alert( "error in GetTraineeSurvey" );
}).success(function() { });
}
So, my question is how can I validate dynamically created controls in this scenario ?
You can use Unobtrusive jQuery validation basic on data attributes on controls.
Read more about that on this LINK.

Use simple MVC Html.DropDownList to control visibility of a div, onLoad and on selectChange

I have the following code:
<div class="form-group">
<label class="col-xs-3 control-label">Intermediary Bank Required?:</label>
<div class="col-xs-9">
<p class="form-control-static">#Html.DropDownList("IntermediaryRequired",(SelectList)ViewBag.IntermediaryRequired,"NO", new { #class = "form-control" })</p>
</div>
</div>
IntermediaryRequired is a bool field on my model
I also have this Extension Helper:
public static class BooleanExtensions
{
public static string ToYesNoString(this bool value)
{
return value ? "YES" : "NO";
}
public static string ToDislay(this bool value)
{
return value ? "normal" : "none";
}
public static string ToChar(this bool value)
{
return value ? "1" : "0";
}
}
My aim is to hide/display a <div> in response to the selected value in the DropDownList for two cases:
when the user Manually changes the DropDownList selection
when the form loads with an existing value for the model field IntermediaryRequired
Please how can we achieve this.
You should be able to do this with a little bit of javascript. Listen to the change event of the dropdown, check the value and hide/show the div. do the samething on document.ready (page loaded) as well to work with existing value of the model.
<script type="text/javascript">
$(function(){
//On page load, update the visiblity
var v=$("#IntermediaryRequired").val();
UpdateDivVisibility(v);
//When user changes the dropdown, update visibility
$("#IntermediaryRequired").change(function(e){
var v=$("#IntermediaryRequired").val();
UpdateDivVisibility(v);
});
});
function UpdateDivVisibility(isVisible)
{
if(v=="YES")
{
$("#DivIdtoHide").show();
}
else
{
$("#DivIdtoHide").hide();
}
}
</script>
EDIT : As per the question in the comment
Usually I create a viewmodel like this
public class CreateCustomerVM
{
public string Name { set;get;}
public List<SelectListItem> IntermediaryOptions {set;get;}
public string IntermediaryRequired {set;get;}
public CreateCustomerVM()
{
this.IntermediaryOptions =new List<SelectListItem>()
}
}
and in your GET actions for create
public ActionResult create()
{
var vm = new CreateCustomerVM();
vm.IntermediaryOptions = GetOptions();
return View(vm);
}
private List<SelectListItem> GetOptions()
{
return new List<SelectListItem>
{
new SelectListItem {Value = "0", Text = "No"},
new SelectListItem {Value = "1", Text = "Yes"}
};
}
And your view will be bounded to the viewmodel
#model CreateCustomerVM
#using(Html.Beginform())
{
<div>
<p>Required?</p>
<p>#Html.DropdowListFor(s=>s.IntermediaryRequired,Model.IntermediaryOptions)
<div id="ExtraOptions">
<!-- Your additional UI elements here -->
</div>
<input type="submit" />
</div>
}
In your Form post, you can read the IntermediaryRequired value and convert that to boolean value
[HttpPost]
public ActionResult Create(CreateCustomerVM model)
{
//check model.IntermediaryRequired
// to do : Save and Redirect(PRG pattern)
}
You can do something like this to show/hide the div when the user Manually changes the Drop Down
var yourDiv = $('#yourDiv');
$('#IntermediaryRequired').on('change', function(){
if ($(this).val() == 'YES') {
yourDiv.show();
}
else {
yourDiv.hide();
}
});
And to get the same result on page load you can try
#if (Model.value)
{
<div id="yourDiv">
....
</div>
}
As a side note use p only when you want to add a paragraph, if you just want to add -meaningless- block element, you can use a div. You can read more about semantics here.

How to add #Html.ValidationMessageFor for each item in a collection?

How would you add #Html.ValidationMessageFor() for each item in a collection? Say,
public class FooVm
{
// some property
public ICollection<BarVm> Bars { get; set; }
}
public class BarVm
{
// some property
[Range(1, int.Max, ErrorMessage = "Must be greater than 1")
public float? Fox { get; set; }
}
Then in a view
#model namespace.here.FooVm
<div class="container"></div>
Populate
<script>
$(function() {
var i = 0;
var populate = function() {
var strBuilder = '<input type="text" name="Bars[i].Fox" />';
$(".container").append(strBuilder);
return false;
};
$(".trigger").click(populate);
});
</script>
It's all working. But how can I add the validation in every textbox? I'm using ASP.NET MVC 4 still practicing. I'm also utilizing unobtrusive validation for client validation. Any you-should-do-something-like-this suggestions or tips, sample code would be great. Thanks.
Actually, using Javascript to populate a View is not the way MVC should be used. Instead, you can render all textboxes like this:
First the code for the class:
public class FooVm
{
// some property
public List<BarVm> Bars { get; set; }
public FooVm()
{
// Make sure the collection exists to prevent NullReferenceException
this.Bars = new List<BarVm>();
}
}
public class BarVm
{
// some property
[Range( 1, Int32.MaxValue, ErrorMessage = "Must be greater than 1" )]
public float? Fox { get; set; }
}
Now the code for the View:
#model WebApplication2.Models.FooVm
<h2>Sample View</h2>
#using ( Html.BeginForm( "YourAction", "YourController" ) )
{
<div class="container">
#for ( int i = 0; i < Model.Bars.Count; i++ )
{
#Html.TextBoxFor( m => m.Bars[i].Fox )
#Html.ValidationMessageFor( m => m.Bars[i].Fox );
}
</div>
}
This will render the necessary tags - and of course the validationmessage-bits. However, it's also possible to combine all error messages in one place by using
#Html.ValidationSummary()
If you really want to display the stuff only after clicking a button, consider using a partial view and loading that one. That's a much better approach than trying to create all necessary tags and attributes for validation using javascript.
Regards,
Frank

Checkbox and controllers

I have one Model Having two virtual Properties i.e
public virtual IEnumerable MediumIds { get; set; }
public virtual IEnumerable AnsLanguageIds { get; set; }
I have Used ViewBag To Populate Them i.e
ViewBag.MediumIds = db.ExamMediums.Where(x => x.ExamId == _ExamId).Select(x => x.Medium);
ViewBag.AnsLanguageIds = new SelectList(db.AnswerLanguages.ToList(), "AnswerLanguageId", "AnsLanguage");
And My View Is
#foreach (var item in
ViewBag.MediumIds)
{
<input id="MediumIds" name="MediumIds" value="#item.MediumId" type="checkbox" /><strong>
#item.Medium1 </strong>
#Html.DropDownList("AnsLanguageIds")
<br />
}
I want The functionality like when the checkbox is selected than only the the dropdown should be enabled else it should be disabled and also i want that for which medium which anslanguage is selected
Your answer will be appreciated.
You can make the drop down lists disabled by default by passing new { disabled = "disabled" } as the DropDownList() method's htmlAttributes argument. This JQuery should toggle the select's disabled state when each checkbox is checked:
$(function() {
$("input#MediumIds").click(function() {
var checkbox = $(this);
var dropDownlist = checkbox.sibling("select:first");
dropDownlist.attr("disabled", checkbox.is(":checked") ? "" : "disabled");
});
});
...I'm not sure what you mean by "i want that for which medium which anslanguage is selected"?

Categories

Resources