Accessing dynamically added HTML controls in ASP.net MVC - c#

I have a problem which I'm unable to solve so any help would be appreciated. I have a view in which I'm dynamically adding textboxes (depending of a value chosen in dropdownlist).
Basically, I'm entering data for the product which depending of the category it belongs to has specific attributes added to it. For example, if the product is soft dring it could have following attributes: type of packaging, flavor, volume, etc. while some other product like cell phone may have attributes like: weight, RAM, CPU clock, CPU type, etc.
This is how the database looks like:
Dynamically creating controls isn't a problem and it is done with this code:
<script type="text/javascript">
$(document).ready(function () {
$("#ProductCategoryId").change(function () {
if ($("#ProductCategoryId").val() != "") {
var options = {};
options.url = "http://localhost:59649/Product/GetProductCategoryAttributes";
options.type = "POST";
options.data = JSON.stringify({ id: $("#ProductCategoryId").val() });
options.dataType = "json";
options.contentType = "application/json";
options.success = function (productCategoryAttributes) {
$("#atributtes").empty();
for (var i = 0; i < productCategoryAttributes.length; i++) {
$("#atributi").append("<div class='editor-label'><label>" + productCategoryAttributes[i].Name + "</label></div>")
.append("<div class='editor-field'><input class='text-box single-line' id='" + productCategoryAttributes[i].Name + "' name='" + productCategoryAttributes[i].Name + "' type='text'>");
}
};
options.error = function () { alert("Error retrieving data!"); };
$.ajax(options);
}
else {
$("#atributtes").empty();
}
});
});
</script>
Method in controller that retrieves ProductAttributeCategory names depending of ProductCategoryId selected:
public JsonResult GetProductCategoryAttributes(int id)
{
var productCategoryAttributes = db.ProductCategoryAttribute
.Where(p => p.ProductCategoryId == id)
.Select(p => new { Name = p.Name, p.DisplayOrder })
.OrderBy(p => p.DisplayOrder)
.ToList();
return Json(productCategoryAttributes, JsonRequestBehavior.AllowGet);
}
Controller code for POST:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Product.Add(product);
db.SaveChanges();
var productCategoryAttributes = db.ProductCategoryAttribute
.Where(p => p.ProductCategoryId == product.ProductCategoryId)
.OrderBy(p => p.DisplayOrder);
foreach (ProductCategoryAttribute productCategoryAttribute in productCategoryAttributes)
{
//Find HTML element that matches productCategoryAttribute.Name
ProductProductCategoryAttribute productCategoryAttributeValue = new ProductProductCategoryAttribute();
productCategoryAttributeValue.ProductId = product.ProductId;
//productCategoryAttributeValue.ProductCategoryAttributeId = Find HTML element that matches ProductCategoryAttributeID and pass its id here
//productCategoryAttributeValue.Value = Find HTML element that matches ProductCategoryAttributeID and pass its value here
db.ProductProductCategoryAttribute.Add(productCategoryAttributeValue);
db.SaveChanges();
}
return RedirectToAction("Index");
}
ViewBag.LanguageId = new SelectList(db.Language, "LanguageId", "Name", product.LanguageId);
ViewBag.ProductCategoryId = new SelectList(db.ProductCategory, "ProductCategoryId", "Name", product.ProductCategoryId);
return View(product);
}
Product model:
public partial class Product
{
public Product()
{
this.ProductPhoto = new HashSet<ProductPhoto>();
this.ProductProductCategoryAttribute = new HashSet<ProductProductCategoryAttribute>();
}
public int ProductId { get; set; }
public int LanguageId { get; set; }
public int ProductCategoryId { get; set; }
public string Name { get; set; }
public string EAN { get; set; }
public string Description { get; set; }
public virtual Language Language { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
public virtual ICollection<ProductPhoto> ProductPhoto { get; set; }
public virtual ICollection<ProductProductCategoryAttribute> ProductProductCategoryAttribute { get; set; }
}
ProductCategory model:
public partial class ProductCategory
{
public ProductCategory()
{
this.Product = new HashSet<Product>();
this.ProductCategoryAttribute = new HashSet<ProductCategoryAttribute>();
}
public int ProductCategoryId { get; set; }
public int LanguageId { get; set; }
public string Name { get; set; }
public string PhotoLocation { get; set; }
public int DisplayOrder { get; set; }
public bool Active { get; set; }
public virtual Language Language { get; set; }
public virtual ICollection<Product> Product { get; set; }
public virtual ICollection<ProductCategoryAttribute> ProductCategoryAttribute { get; set; }
}
ProductCategoryAttribute model:
public partial class ProductCategoryAttribute
{
public ProductCategoryAttribute()
{
this.ProductProductCategoryAttribute = new HashSet<ProductProductCategoryAttribute>();
}
public int ProductCategoryAttributeId { get; set; }
public int ProductCategoryId { get; set; }
public string Name { get; set; }
public string MetaName { get; set; }
public string SymbolLocation { get; set; }
public int DisplayOrder { get; set; }
public bool Active { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
public virtual ICollection<ProductProductCategoryAttribute> ProductProductCategoryAttribute { get; set; }
}
What I can't figure out is how to get the values from those dynamically created textboxes. Pseudocode (inside the controller) would be something like this:
Get the ProductCategoryId of the product
List all the attributes belonging to the selected product category
For each attribute find the appropriate textbox inside the view and get the value entered
Save the value to the database
I'm fairly new to the MVC so my approach may be wrong. Feel free to correct me.

It's very hard to read your code so here is a simplified version that should help you. Suppose you have these two models:
public class ProductCategory
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
public class Product
{
public Product()
{
Categories = new List<ProductCategory>();
}
public int ProductId {get;set;}
public string ProductName { get; set; }
public IEnumerable<ProductCategory> Categories { get; set; }
}
If these where your models then then the name attribute of your dynamically added textbox should be:
<input type="textbox" name="Categories[i].CategoryName" />
You can safely ignore the id attribute since name attribute is enough for proper model mapping/binding. Whatever value you enter in the textbox should map into an instance of a ProductCategory's CategoryName in the list of Categories attached to the Product model...

Thank you both of you. I've found a quick and, as it seems, dirty way of accomplishing this.
Basically, you can access any HTML control by using:
var value = Request["controlName"];
That is how I was able to get values from the form. I don't know if this is the right way but it sure worked for me. Any suggestion for improving this is more than welcome.

Related

Property set to null when mapped value is added

I created a piece of code that sets the property of a model to null when it is combined with another propert;
my model:
public partial class IngredientAmount
{
public string Id { get; set; }
public string MealId { get; set; }
public string IngredientId { get; set; }
public string Amount { get; set; }
[NotMapped]
public string DayId { get; set; }
[NotMapped]
public List<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
public virtual Ingredient Ingredient { get; set; }
public virtual Meal Meal { get; set; }
}
My script that loads my model into a certain div by calling an url.action:
<script>
function loadCreateIngredientAmountPartial() {
$("#AddIngredient").load('#Url.Action("CreatePartial", "IngredientAmount", new IngredientAmount() { MealId = Model.Id, DayId = "2" })');
}
</script>
My controller method:
public ActionResult CreatePartial(IngredientAmount ingredient)
{
dbContext = new NutritionzContext();
IngredientAmount ingredientAmount = new IngredientAmount() { MealId = ingredient.MealId, DayId = ingredient.DayId };
ingredientAmount.Ingredients = dbContext.Ingredient.OrderBy(x => x.Name).ToList();
return PartialView("~/Views/IngredientAmount/Create.cshtml", ingredientAmount);
}
If I only send the DayId within the new IngredientAmount the property is filled, however when I add a mealId it turns empty.
I've found a way around my own question, the problem was that it wasn't routed correctly, so I created a new instance in the controller and just sent the two parameters via the action like so:
<script>
function loadCreateIngredientAmountPartial() {
$("#AddIngredient").load('#Url.Action("CreatePartial", "IngredientAmount", new { mealId = Model.Id, dayId = Model.DayId })');
}
</script>
Then I tasked the controller to route that specific action like so:
[Route("IngredientAmountController/CreatePartial/{mealId}/{dayId}")]

How to save tags in database c#

In frontend in Angular I created the possibility of adding tags and memes. In the back-end in the web api I would like to save the tags in the database. Using entity framework code first, I created the structure of three tables:
public class Memes
{
public int Id { get; set; }
public string MemName { get; set; }
public string UserId { get; set; }
public string Image { get; set; }
public List<Memes_tags> MemesIteam { get; set; }
}
public class HashTag
{
public int Id { get; set; }
public int MemesId { get; set; }
public string Name { get; set; }
}
public class Memes_tags
{
public int Id { get; set; }
public int MemesId { get; set; }
public int HashTagId { get; set; }
public virtual Memes Memes { get; set; }
public virtual HashTag HashTags { get; set; }
}
Then I created a method that should save memes and tags in the database:
[HttpPost]
[Route("api/Memes/AddMemes")]
public IHttpActionResult CreateMemes([FromBody] MemHashTagsViewModel createMem)
{
ApplicationDbContext db = new ApplicationDbContext();
if (createMem != null)
{
Memes mem = new Memes()
{
MemName = createMem.MemName,
Image = createMem.Image,
UserId = createMem.UserId
};
db.MemesModel.Add(mem);
foreach (var item in createMem.HashTags)
{
var hashTag = new HashTag()
{
MemesId = mem.Id,
Name = item
};
db.HashTags.Add(hashTag);
}
db.SaveChanges();
return Ok();
}
else
{
return NotFound();
}
}
Incoming data:
I have problem with the correct Memes Id record. For example, I created a mem that has Id = 4 and in the table HashTags should be 4 and in my case is 0.
Is there any other better solution for saving tags in the database? Is my solution is good?
Yeah, the thing is: since you didn't save the mem first, it doesn't have an ID when you add it to the hashtag.
If you want to do it that way, you should make HashTag a member, in form of a list (property) on the mem. Then, when creating the HashTag objects, not add a member ID. The merely add the Mem to the database, and EF will take care of the object structure.
(On my phone, will make a code example in the morning if no one beats me to it)
EDIT: Here's how i would do it:
Respectfully: Drop the Memes_tags class as their seems to be no point in having it at all. It merely works as a relation between Memes and HashTags, but that already exists.
For purposes of Best practice, at least according to MS's own EF 'get start' doc the id of the class should be named: <class_name>Id, so that has been 'corrected' as well.
public class Memes
{
public int MemesId { get; set; }
public string MemName { get; set; }
public string UserId { get; set; }
public string Image { get; set; }
public List<HashTag> HashTags { get; set; }
}
public class HashTag
{
public int HashTagId { get; set; }
public int MemesId { get; set; }
public string Name { get; set; }
public virtual Memes { get; set; }
}
Below is the modified 'CreateMemes'. The idea is, that instead of adding the ID of the 'Memes' to hashtag, we merely add the HashTags to the meme object, thus they are add to EF as well, and when the 'Memes' record is add to the database, EF will make certain to create the hashtags too.
[HttpPost]
[Route("api/Memes/AddMemes")]
public IHttpActionResult CreateMemes([FromBody] MemHashTagsViewModel createMem)
{
ApplicationDbContext db = new ApplicationDbContext();
if (createMem != null)
{
Memes mem = new Memes()
{
MemName = createMem.MemName,
Image = createMem.Image,
UserId = createMem.UserId
};
foreach (var item in createMem.HashTags)
{
var hashTag = new HashTag()
{
Name = item
};
mem.HashTags.add(hashTag);
}
db.add(mem);
db.SaveChanges();
return Ok();
}
else
{
return NotFound();
}
}
Just adding the created instance to the context entity model isn't enough db.MemesModel.Add(mem);. Id value doesn't gets generated unless you call SaveChanges() on it. This in your below code there is no Id value yet and so what you observe
var hashTag = new HashTag()
{
MemesId = mem.Id,
Name = item
};

How do I update a record that has a child entity?

I am trying to update a record and its child at the same time. When I create the object from the database the child property is null (the property is a generic list).
I want to update the class and also update the child class without creating duplicated records in the system.
Here is how I generate the object:
var r = db.SupplierAs.Where(o => o.id == 1).First();
The SupplierA class has a property List. Using the above line of code this comes back null. I have been trying work out the code to initialize this property so I can update it but I am having no joy.
This is the original item I created:
db.Products.Add(new Product
{
name = "product test",
supplierA = new SupplierA
{
name = "supA",
price = 1.99m,
sku = "abc123",
otherCurrencies = new List<Currency>
{
new Currency
{
eur = 2.99m,
usd = 3.99m
}
}
},
});
db.SaveChanges();
I can update the supplier on its own easily like so:
var r = db.SupplierAs.Where(o => o.id == 1).First();
r.name = "Updated name";
db.SupplierAs.Attach(r);
db.Entry(r).State = EntityState.Modified;
db.SaveChanges();
But I cannot figure out how to generate the Currency object list as part of the SupplierAs object. Currencies doesnt seem to be in the db context.
Here are the class files:
public class Product
{
public int id { get; set; }
public string name { get; set; }
public virtual SupplierA supplierA { get; set; }
}
public class SupplierA
{
public int id { get; set; }
public string name { get; set; }
public string sku { get; set; }
public decimal price { get; set; }
public List<Currency> Currencies { get; set; }
}
public class Currency
{
public int id { get; set; }
public decimal eur { get; set; }
public decimal usd { get; set; }
}
The idea of products, suppliers and currencies doesn't make the greatest sense I know, I have extracted logic from my app in example, hopefully it makes enough sense what I am trying to achieve.

Fill dropdown list from database in create view

How can I fetch and insert data at a specific time in one view in mvc razor view? I mean to fill a dropdown list from the database in create view.
I want to fill the following when I add the subject and cheater models.
department list
semester list
standard list
stream list
cheater model:
namespace firstapp.Models
{
public class chepter
{
[ForeignKey("dip_id")]
public int dipart_id { get; set; }
public int chep_id { get; set; }
public string subject { get; set; }
public string chepter { get; set; }
public List<dipartment> dipartlist { get; set; }
public List<dipartment> stdlist { get; set; }
public List<dipartment> semlist { get; set; }
public List<dipartment> stremlist { get; set; }
}
}
department model:
namespace firstapp.Models
{
public class dipartment
{
public int dip_id { get; set; }
public string dipart { get; set; }
public string std { get; set; }
public string sem { get; set; }
public string strem { get; set; }
}
}
#Html.DropDownListFor(model => model.dipart_id, new SelectList(Model.dipartlist.Select(s => new SelectListItem() { Value = s.dip_id, Selected = false, Text = s.dipart })), "Select")
Change your model so the list property is a selectlist:
public SelectList<dipartment> dipartlist { get; set; }
Then, when you populate the model call a service class method(you might not have a service layer, I just prefer to not have database calls in the controller)
dipartlist = _departmentService.GetAsSelectList();
The GetAsSelectList service method looks like this:
public SelectList GetAsSelectList()
{
return (from d in _context.Set<department>().OrderBy(x => x.dipart)
select new
{
Id = d.dipart_id,
Name = d.dipart
}).ToList();
}
And finally your view:
#Html.DropDownListFor(model => model.dipart_id, Model.dipartlist)
This technique means you don't have linq in either the view or controller. Also as you're only creating the selectlist in one place (the service), you can cache it with MemoryCache to prevent multiple requests for the same data. And as it looks like you're populating 4 selectlists, this might be useful.

Returning multiple models in a list to one view

I am having an issue returning multiple models in one view. I have a model that has a collection of other models. So when I return the model (ProblemsVM) that has the collection of the other 2, I would like to attach the other 2 to the model being returned so I can access it's values in the view. Any help will be greatly appreciated and thank you for taking your time to read.
This is my ProblemsVM model and the 2 IEnumerables that I want to include in the views return.
public class ProblemsVM
{
public int Id { get; set; }
public string Title { get; set; }
public int ProjectId { get; set; }
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<Type> Types {get; set;}
}
This is the action returning the view I want to attach the 2 models to. Right now it populates a table of different problems by ProjectId that it receives from the API.
public ActionResult ReturnProblems()
{
List<ProblemsVM> dv = new List<ProblemsVM>();
// return problems
var requestDef = new RestRequest("problem", Method.GET);
requestDef.AddHeader("id", userId);
requestDef.AddHeader("key", userKey);
requestDef.RequestFormat = DataFormat.Json;
var responseDef = Userclient.Execute(requestDef) as RestResponse;
List<ProblemsVM> problems = JsonConvert.DeserializeObject<List<ProblemsVM>>(responseDef.Content);
IEnumerable<ProblemsVM> problemsToReturn = problems.Where(d => d.ProjectId == item.ProjectId);
return View(problemsToReturn);
}
So what I am looking to do would be something like this that I have in another application. In this one it works fine when using a Model not in a list or IEnumerable. I am having trouble switching this over to the above method to be returned with the list of problems. ProbVM is the same as ProblemsVM in the other project.
ProbVM tt = new ProbVM();
tt.Cats = db.Categories.ToList();
tt.Typ = db.Types.ToList();
return View(tt);
This is my other 2 models that are stored in the SQL database:
public partial class Type
{
public int Id { get; set; }
public int Order { get; set; }
public string Description { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public partial class Category
{
public Category()
{
this.Types = new HashSet<Type>();
this.Companys = new HashSet<Company>();
}
public int Id { get; set; }
public string Title { get; set; }
public int CompanyId { get; set; }
public virtual ICollection<Company> Companys { get; set; }
public virtual ICollection<Type> Types { get; set; }
}
-----UPDATED PART------
So when this button(which is in the rows of the datatables) is clicked it pops up a modal:
Type
and on the button id "launch" is called in ajax by:
<script>
$(function () {
$("#type-modal").dialog({
autoOpen: false,
height: 600,
width: 500,
modal: true,
open: function () {
$("#accordion").accordion({ autoHeight: true });
}
});
$(".launch").click(function () {
Id.value = $(this).attr('value');
$("#type-modal").dialog("open");
})
});
</script>
and in the type-modal div is where I will use the Category and Type model to use razor(would like to use something like this like I did in the other project):
foreach (var item in Model.Cats)
{
#Html.Label(item.Title, new { cats = item.Title })<br/>
foreach(var tri in Model.types)
{
if (item.CategoryId == tri.CategoryId)
{
#Html.RadioButton(tri.Category.Title, tri.Order)
#Html.Label(tri.Description)<br/>
}
}
}

Categories