I'm creating DIVs on runtime based on user input.
#foreach (var item in Model.BUM_List)
{
<div class="shadow col-sm-2 col-xs-4">
<input class="Columndiv" value="#item.Columns" id="" readonly/>
<input type="checkbox"/><label id="lblSelectColumn">Include this Column</label>
</div>
}
My question is, how do I define ID for each of these newly created DIVs, after creation? It can be names as Column1, Column2, etc.
I tried using the following function, but it's only useful after the DIVs have been created.
$("#Submit_Table").click(function () {
var i = 0;
$('.Columndiv').each(function () {
i++;
var newID = 'menu' + i;
$(this).attr('id', newID);
$(this).val(i);
});
});
Try adding dynamic id by getting the value from item
<div class="shadow col-sm-2 col-xs-4" id="topDiv#item.code">
I am assuming your item will have some unique id which you can use.
Related
I'm trying to dynamically build dropdown lists within my Blazor form component by using a foreach statement. The form is for a product and a product can be linked to tags. Tags themselves have a tag type and I want to dynamically generate a separate dropdown for each tag type which pulls through all the tags linked to that type and only binds to tags of that tag type.
#foreach (var type in tagTypesDto)
{
<div class="form-group mb-3">
<RadzenSelect AllowClear="true" AllowFiltering="true" #bind-Value=#productDto.SelectedProductTags.Where(x => x == type.Tags.Select(x => x.Id).FirstOrDefault()) Multiple="true" Placeholder="#_localizer["PleaseSelect"]" Data=#tagsDto.Where(x => x.TagTypeId == type.Id)
TextProperty="Name" ValueProperty="Id" Label="#_localizer["ProductAttributes"]" />
</div>
}
I'm unable to get this to build because I'm getting the following error:
The left-hand side of an assignment must be a variable, property or indexer
I think the issue is the LINQ query I'm trying to pass it for #bind-Value - if I replace this with just #productDto.SelectedProductTags then it builds and generates a separate dropdown for each tag type, but if I try and select any tags it fails, presumably because it doesn't know which dropdown to assign the tag to.
SelectedProductTags looks like this in my product model:
public virtual IEnumerable<int>? SelectedProductTags { get; set; } = new int[] { };
I'm using .NET 6 and Blazor Webassembly.
I created a demo project on GitHub with a possible solution to your problem: https://github.com/Jimmys20/SO74219635
You have to use manual event binding:
#foreach (var type in tagTypesDto)
{
<div class="form-group mb-3">
var tagTypeId = type.Id;
<RadzenDropDown Data="tagsDto.Where(x => x.TagTypeId == type.Id).ToList()"
Value="productDto.SelectedProductTags"
ValueChanged="values => OnValueChanged(values, tagTypeId)"
TValue="List<int>"
Multiple="true"
ValueProperty="Id"
TextProperty="Name" />
</div>
}
#code {
private void OnValueChanged(List<int> values, int tagTypeId)
{
var data = tagsDto.Where(t => t.TagTypeId == tagTypeId).Select(t => t.Id);
productDto.SelectedProductTags.RemoveAll(x => data.Contains(x));
productDto.SelectedProductTags = productDto.SelectedProductTags.Concat(values).ToList();
}
}
SelectedProductTags needs to be changed to List<int>.
Let's say I have this dynamically created form:
<form>
Item #1 <input type="text" name="qty" id="Item_3223" value="3">
Item #2 <input type="text" name="qty" id="Item_2243" value="1">
Item #3 <input type="text" name="qty" id="Item_5328" value="1">
Item #4 <input type="text" name="qty" id="Item_3250" value="5">
<!--... and so on...-->
<button type="submit">Update quantities</button>
</form>
The fields are generated server side like this:
<input type="text" name="qty" id="Item_#item.Id" value="#item.Qty">
In my controller, how can I receive the array and perform an update on each of the items in it?
Simplified, my Update-method looks like this now:
public async Task<IActionResult> Update(IFormCollection form)
{
var updateItem = new ShoppingCartItem();
foreach (string item in form.Keys)
{
updateItem = (from c in _context.ShoppingCartItems
where (c.ShoppingCartId == ShoppingCartItem.ShoppingCartId &&
c.ProductId == form.[what goes here?])
select c).FirstOrDefault();
updateItem.Quantity = form.[what goes here?]
}
_context.SaveChangesAsync();
return RedirectToAction("Index", "Cart");
}
Since I can't know the name of the form.Keys, how can I extract the values from them? I seem to remember this was quite easy in classic ASP back in the day ...
Edit
With help from #David, I put this together:
<input asp-for="#item.Quantity" name="Item_#item.ProductId" id="Item_#item.ProductId" />
... and:
var updateItem = new ShoppingCartItem();
int qty = 0;
int pId = 0;
foreach (string item in form.Keys.Where(k => k.StartsWith("Item_")))
{
pId = int.Parse(item.Replace("Item_", ""));
updateItem = (from c in _context.ShoppingCartItems
where (c.ShoppingCartId == ShoppingCartItem.ShoppingCartId && c.ProductId == pId)
select c).FirstOrDefault();
qty = int.Parse(form[item]);
updateItem.Quantity = qty;
}
await _context.SaveChangesAsync();
Works like a charm! Thanks! :)
You're not passing an array to the server-side code. You're passing a single value with the name "qty". This would likely be the last instance of that input in your form.
An array needs brackets in the name:
<input type="text" name="qty[]" id="Item_#item.Id" value="#item.Qty">
Then in the server-side code the "key" for the array is "qty[]". You can pull that array from the form object and iterate over it for your needs:
var inputArray = form["qty[]"];
Basically, all you need is the brackets in the name to tell the browser that these multiple elements constitute an array of values. The id has nothing to do with form posting.
Edit: If you do need that "id" value, then you can put it in the name:
<input type="text" name="Item_#item.Id" id="Item_#item.Id" value="#item.Qty">
Then you would have multiple unique key/value pairs in the form post, which you can check as you're currently attempting to (foreach (string item in form.Keys)) and then parse out the "id" from that item string as needed.
It sounds like what you need is a dictionary. For that, you'd need inputs like the following:
<input type="hidden" name="qty[0].Key" value="#item.Id" />
<input type="text" name="qty[0].Value" value="#item.Qty" />
You'd bind that to a param like Dictionary<int, int> qty. Then, you can access the quantities for each item via something like:
item.Qty = qty[item.Id];
That said, it's usually better to just use view models for this kind of thing, so you can bind to properties in a strongly-typed way. For example, you could use something like:
public class ItemQuantityViewModel
{
public int Id { get; set; }
public int Qty { get; set; }
}
Then, you can have as the model for your form (and the param you bind to on your action) be List<ItemQuantityViewModel>. Finally, you'd just have input like:
#for (var i = 0; i < Model.Count; i++)
{
<input type="hidden" asp-for="#Model[i].Id">
<input asp-for="#Model[i].Qty">
}
This question already has answers here:
Post an HTML Table to ADO.NET DataTable
(2 answers)
Closed 7 years ago.
I am displaying a list of items in a Collection in edit mode in a view. after editing the documents, I want to submit. But I am unable to postback the list. List shows null.
here is my View
#model List<NewsLetter.Models.NewsLetterQuestions>
#using (Html.BeginForm("GetAnswersfromUser", "NewsLetter", FormMethod.Post, null))
{
#Html.AntiForgeryToken()
foreach (var item in Model) {
<div>
#Html.DisplayFor(modelItem => item.Question)
</div>
<div>
#Html.TextAreaFor(modelItem => item.Answer)
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
}
Here is my Controller
public ActionResult GetAnswersfromUser(string id)
{
id = "56c5afc9afb23c2df08dd2bf";
List<NewsLetterQuestions> questions = new List<NewsLetterQuestions>();
var ques = context.NewsLetterQuestionCollection.Find(Query.EQ("NewsLetterId", id));
foreach(var x in ques)
{
questions.Add(x);
}
return PartialView(questions);
}
[HttpPost]
public ActionResult GetAnswersfromUser(List<NewsLetterQuestions> nql)
{
string id = "56c5afc9afb23c2df08dd2bf";
foreach (var item in nql)
{
var query = Query.And(Query.EQ("NewsLetterId", id), Query.EQ("Question", item.Question));
var update=Update<NewsLetterQuestions>
.Set(r => r.Answer, item.Answer);
context.NewsLetterQuestionCollection.Update(query,update);
}
return RedirectToAction("NewsLetterIndex");
}
When i hit submit it throws error.
System.NullReferenceException: Object reference not set to an instance of an object.
In the line
foreach (var item in nql)
which means that nql is null.
In order for the model binder to be able to bind the posted data, all your input names need to be in the format of [N].Property, where N is the index of the item within the list. In order for Razor to generate the input names properly, then, you need to pass it an indexed item, which means you need a for loop, rather than a foreach:
#for (var i = 0; i < Model.Count(); i++)
{
...
#Html.TextAreaFor(m => m[i].Answer)
...
}
You're never passing the list back to the controller's Post handler. You need to route the list back to the controller.
You should be doing something similar to this untested code :)
Html.BeginForm("Index", "Home", new { #nql=Model }, FormMethod.Post)
Take a look at this post as well. It is similar to your issue: Pass multiple parameters in Html.BeginForm MVC4 controller action and this Pass multiple parameters in Html.BeginForm MVC
I have a form which asks users for their personal info and their family members.
fields of the family members section is repeating.
my question is what is best practice to handle these kind of repeating forms?
I currently use AJAX to repeat forms but how to collect data from these repeating fields?
since some one asked for how I repeat form, I do it like this:
AJAX Call
$(document).on('click', '.btn-add-item', function (e) {
e.preventDefault();
var $results = $('#results');
$.ajax({
url: '/AJAX/AddFamilyForm',
type: 'post',
success: function (data) {
$(data).appendTo($results);
afterAJAX();
}
});
});
C# code
[HttpPost]
public PartialViewResult AddFamilyForm()
{
if (!Request.IsAjaxRequest()) return null;
return PartialView("_FamilyForm");
}
This is some skeleton code on how to get this to work with proper model-binding in MVC. You'll need to write some JS to be able to delete/add new rows.
Model
public class MyModel
{
public FamilyMembers[] FamilyMembers { get; set; }
}
View
<button id="addNewFamilyMember" type="button">Add</button>
#if (Model.FamilyMembers != null)
{
for (int i = 0; i < Model.FamilyMembers.Length; i++)
{
<tr>
<td>
<button type="button">Delete</button>
#Html.Hidden("FamilyMembers.Index", i)
</td>
<td>
#Html.TextBoxFor(m => Model.FamilyMembers[i].Relation)
</td>
<td>
#Html.TextBoxFor(m => Model.FamilyMembers[i].FullName)
</td>
</tr>
}
}
Below is the code for adding a new member. It creates html dynamically and is able to bind to the posted model because of naming conventions. time gives each added row a unique id so all the data stays together.
JS (using Jquery)
var hidden = '#Html.Hidden("FamilyMembers.Index", "{id}")';
var relationHtml = '#Html.TextBox("FamilyMembers[{id}].Relation")';
var fullNameHtml = '#Html.TextBox("FamilyMembers[{id}].FullName")';
$("#addNewFamilyMember").on("click", function () {
var time = Date.now();
var deleteHtml = "<button type='button'>Delete</button>";
$("#familyMembers-table").find("tbody")
.append($("<tr><td>" + hidden.replace("{id}", time) + deleteHtml + "</td>" +
"<td>" + relationHtml.replace("{id}", time) + "</td>" +
"<td>" + fullNameHtml.replace("{id}", time) + "</td></tr>"));
});
One of the solution could be combination of hidden field and control name.
Steps:
Use a hidden field to keep the count the number of row.
Create controls with name like text_relation_1 for first row and text_relation_2 for second row and so on
Generate other controls in same way.
Increase and decrease the hidden field value so that when values post you can know the number of rows added by the user
On your action use FormCollection and loop though hidden field number and get the values from FormCollection
Like suppose I created 3 rows then I can create a action like below
public ActionResult SomeActionMethod(FormCollection formCollection, string hid)
{
for(int i=1;i<hid;i++)
{
var relationId="text_relation_"+i;
var firstrealtion=formCollection[relationId];
...
}
}
You don't need any extra Ajax requests for this, since you can use established and standard <form> features.
Just append [] to the name of the added forms and you'll end up with an array rather than a single value in your HTTP request once the form is submitted:
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
In this example you'd end up with an array relation and an array fullname, both containing your datasets.
I am using MVC + EF
I have a Feed xml file url that gets updated every 7 minute with items, every time a new item gets added I retrieve all the items to a list variable and then I add these varible to my database table. After that I fill a new list variable which is my ViewModel from the database table. Then I declare the ViewModel inside my view which is a .cshtml file and loop throught all of the objects and display them.
How can I make sure that the newest items get placed on the top and not in the bottom?
This is how I display the items inside my cshtml note that I use a ++number so the newest item needs to be 1 and so on.. :
#model Project.Viewmodel.ItemViewModel
#{
int number = 0;
}
}
<div id="news-container">
#foreach (var item in Model.NewsList)
{
<div class="grid">
<div class="number">
<p class="number-data">#(++number)</p>
</div>
<p class="news-title">#(item.Title)</p>
<div class="item-content">
<div class="imgholder">
<img src="#item.Imageurl" />
<p class="news-description">#(item.Description) <br />#(item.PubDate) | Source</p>
</div>
</div>
</div>
}
</div>
This is how I fill the viewmodel which I use inside the .cshtml file to iterate throught and display the items
private void FillProductToModel(ItemViewModel model, News news)
{
var productViewModel = new NewsViewModel
{
Description = news.Description,
NewsId = news.Id,
Title = news.Title,
link = news.Link,
Imageurl = news.Image,
PubDate = news.Date,
};
model.NewsList.Add(productViewModel);
}
Sorry for the paint lol :P
Any kind of help is appreciated
#foreach (var item in Model.NewsList.OrderByDescending(n => n.PubDate)