I have 2 methods : Add and Subtract. I want that when clicked Add button, the 3rd textbox displays the result of A + B; clicked "Subtract" button, the 3rd textbox displays the result of A - B.
I thought it should be a simple thing to do (because it can be done within a few lines of code in ASP.NET WebForm Application). But after trying many times and asked a few of friends, I still cannot find the solution...
#using (Html.BeginForm("Add", "MyMVC"))
{
<ol>
<li>
#Html.TextAreaFor(m => m.A)
</li>
<li><span>+ </span></li>
<li>
#Html.TextAreaFor(m => m.B)
</li>
<li><span>= </span></li>
<li>
#Html.TextAreaFor(m => m.C)
</li>
</ol>
#{
Html.RenderAction("Calculate", "MyMVC");
}
#{
Html.RenderAction("Subtract", "MyMVC");
}
}
The RenderAction tags aren't needed. The BeginForm extension also needs to render a form element that posts to itself.
Change your view to
#model SampleMvc.Models.SumModel
#using (Html.BeginForm())
{
<ol>
<li>
#Html.TextAreaFor(m => m.A)
</li>
<li><span>+ </span></li>
<li>
#Html.TextAreaFor(m => m.B)
</li>
<li><span>= </span></li>
<li>
#Html.TextAreaFor(m => m.C)
</li>
</ol>
<input type="submit" id="add" name="Calculate" value="Add" />
<input type="submit" id="subtract" name="Calculate" value="Subtract" />
}
Notice the name attribute on the submit inputs, this will enable the default model binder to bind to a property called Calculate.
For you View model use something similar to
public class SumModel {
public int A { get; set; }
public int B { get; set; }
public int C { get; private set; }
public string Calculate { get; set; }
public void RunCalculation() {
if (Calculate.Equals("add",StringComparison.InvariantCultureIgnoreCase)) {
C = A + B;
} else {
C = A - B;
}
}
}
Then in the controller have actions similar to:
public ActionResult Calculate() {
return View();
}
[HttpPost]
public ActionResult Calculate(SumModel sumModel) {
sumModel.RunCalculation();
return View(sumModel);
}
When the form is submitted via the click events on the submit inputs the value of the button will be auto-magically bound to the Calculate property of the SumModel.
Then when the method RunCalculation is called it uses the property to work out which operation to run against the values A and B
Other easy approach is , you can have the same name for the buttons. and then read it in controller.
<input type="submit" id="add" name="buttonclicked" value="Add" />
<input type="submit" id="subtract" name="buttonclicked" value="Subtract" />
[HttpPost]
public ActionResult Index(string buttonclicked, SumModel sumModel) {
if(buttonclicked=="add"){
}
else{
}
}
Related
I'm trying to build one view that includes all (Create, Edit, Delete, and Index) in one View which is Index.
The problem is with Editing. Always returns null to the controller as shown in the gif.
I have Model and ViewModel as follows.
The Model BootstrapCategory
public class BootstrapCategory
{
[Key]
public Guid Id { get; set; }
[MaxLength(20)]
[Required]
public string Category { get; set; }
}
The ViewModel VMBPCategoris
public class VMBPCategoris
{
public List<BootstrapCategory> bootstrapCategories { get; set; }
public BootstrapCategory bootstrapCategory { get; set; }
}
The View
Note: Edit not by the usual button in the table it instead by another
button as shown in the gif
#model VMBPCategoris
#foreach (var item in Model.bootstrapCategories)
{
<tr>
<td>
<form asp-action="Edit" method="post">
<input type="hidden" asp-for="#item.Id" />
<div class="#item.Id d-none">
<div class="input-group">
<input id="btnGroupEdit" type="submit" value="Save" class="input-group-text btn btn-primary" />
<input asp-for="#item.Category" class="form-control" aria-label="Input group example" aria-describedby="btnGroupEdit">
</div>
<span asp-validation-for="#item.Category" class="text-danger"></span>
</div>
</form>
<div class="#item.Id">
#Html.DisplayFor(modelItem => item.Category)
</div>
</td>
<td>
Edit |
<a asp-action="Details" asp-route-id="#item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
The Controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit([Bind("Id,Category")] BootstrapCategory bootstrapCategory)
{
_context.Update(bootstrapCategory);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
//return View(vMBPCategoris);
}
The view model class VMBPCategoris needs to have its members properties assigned to instances in a constructor:
public class VMBPCategoris
{
public List<BootstrapCategory> bootstrapCategories { get; set; }
public BootstrapCategory bootstrapCategory { get; set; }
public VMBPCategoris()
{
bootstrapCategories = new List<BootstrapCategory>();
bootstrapCategory = new BootstrapCategory();
}
}
You can give a name to your input tag.Change your form like below.
<form asp-action="Edit" method="post">
<input type="hidden" asp-for="#item.Id" name="Id"/>
<div class="#item.Id d-none">
<div class="input-group">
<input id="btnGroupEdit" type="submit" value="Save" class="input-group-text btn btn-primary" />
<input asp-for="#item.Category" name="Category" class="form-control" aria-label="Input group example" aria-describedby="btnGroupEdit">
</div>
<span asp-validation-for="#item.Category" class="text-danger"></span>
</div>
</form>
I made an easy solution by changing a little bit with the ViewModel and using the value attribute to get value from a model and using asp-for to set a value to another model.
It's all clarified in that Stackoverflow post
I have a viewmodel:
public class ProductInShopsCheckboxViewModel
{
public ProductInShop? ProductInShop { get; set; }
public IEnumerable<ProductInShop>? Checkboxes { get; set; }
}
view(that works):
#model WebApp.ViewModels.ProductInShopsCheckboxViewModel
#if (Model?.Checkboxes != null) {
<h4>#Model.Checkboxes!.ElementAt(0).Shop!.ShopName</h4>
<p></p>
<form asp-action="Create">
#for (var i = 0; i < Model.Checkboxes!.Count(); i++) {
<input type="checkbox" asp-for="Checkboxes.ElementAt(i).IsSelected">#Model.Checkboxes.ElementAt(i).Product?.ProductName
<p></p> }
<div class="form-group">
<input type="submit" value="Send email" class="btn btn-primary"/>
</div>
</form> }
<p></p>
<a asp-action="Index">Back to Shops</a>
and controller:
[HttpPost]
public IActionResult Create(ProductInShopsCheckboxViewModel vm)
{
//TODO: check which vm.Checkboxes items are selected
//Send those items to mail(code ready)
}
However I get back viewmodel with empty list and I cannot figure out what I'm doing wrong. I appreciate any advice.
I'm having a weird issue with a form in my View not returning the model's correct Id property value. You'll noticed in the code below I have the script section logging the model's Id. Doing this shows the correct Id on the console, but when the Id is passed to the Controller's action method it is always 0, which is incorrect.
Here's the view:
#model EotE_Encounter.Models.Encounter
<div>
<h4>#Model.Name</h4>
<div>
<form asp-action="CreateCharacter" asp-controller="Encounter" data-bind="" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#character-container">
<input id="encounterId" type="hidden" value="#Model.Id" />
<button class="btn btn-default" type="submit">Create Character</button>
</form>
</div>
<hr />
<div>
<ul>
#{
if(Model.CharactersInEncounter != null)
{
foreach(Character character in Model.CharactersInEncounter)
{
<li>#character.Name</li>
}
}
}
</ul>
</div>
</div>
<script>
console.log(#Model.Id);
</script>
Related Action Method:
public ActionResult CreateCharacter(int encounterID)
{
return RedirectToAction("CreateCharacter", "Character", encounterID);
}
And the Encounter model:
public class Encounter
{
//these first three properties may not be used just yet.
public int Id { get; set; }
public string Name { get; set; }
public byte Round { get; set; }
public List<Character> CharactersInEncounter { get; set; }
[StringLength(2000)]
public string Notes { get; set; }
}
Only form elements with a name attribute will have their values passed when submitting a form. So, add the name attribute to your hidden element. Id and the name are not the same.
#model EotE_Encounter.Models.Encounter
<div>
<h4>#Model.Name</h4>
<div>
<form asp-action="CreateCharacter" asp-controller="Encounter" data-bind="" data-ajax="true" data-ajax-mode="replace" data-ajax-update="#character-container">
<input id="encounterId" name="encounterID" type="hidden" value="#Model.Id" />
<button class="btn btn-default" type="submit">Create Character</button>
</form>
</div>
<hr />
<div>
<ul>
#{
if(Model.CharactersInEncounter != null)
{
foreach(Character character in Modelsel.CharactersInEncounter)
{
<li>#character.Name</li>
}
}
}
</ul>
</div>
</div>
<script>
console.log(#Model.Id);
</script>
Notice the name attribute of the <input id="encounterId" name="encounterID" type="hidden" value="#Model.Id" /> element. It has to be the same name as the action parameter (int encounterID). If it's not the same then the parameter binding will not work.
I have read plenty of posts about this issue but I can't seem to find a solution that fits with my implementation. I'm giving MVC another attempt (I'm a webforms guy). The model being passed to my ActionResult is basically empty when it should be populated. I'm starring at the sample that works and I can find no differences. It seems to be something impossible to debug too. Any pointers will be greatfully appreciated.
View:
#model WebApplication1.Models.SiteViewModel
#{
ViewBag.Title = "Delete your site";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<p>Are you sure you want to delete this site?</p>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false)
<ul class="list-group">
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Name)</b></div>
<div class="col-md-6">#Model.Name</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Phase)</b></div>
<div class="col-md-6">#Model.Phase</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-2"><b>#Html.LabelFor(model => model.Type)</b></div>
<div class="col-md-6">#Model.Type</div>
</div>
</li>
</ul>
<button type="submit" class="btn btn-danger">Delete</button>
#Html.ActionLink("Back to List", "Index", null, new { #class = "btn btn-default" })
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Model:
public class SiteViewModel
{
public SiteViewModel()
{
Services = new List<ServiceModel>();
}
public SiteViewModel(SiteModel site)
{
this.SiteId = site.SiteId;
this.Name = site.Name;
this.Type = site.Type;
this.Phase = site.Phase;
this.Services = site.Services;
}
public int SiteId { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
public SchoolType Type { get; set; }
public SchoolPhase Phase { get; set; }
public DateTime? Deleted { get; set; }
public virtual ICollection<ServiceModel> Services { get; set; }
}
Controller Action:
[HttpPost]
public ActionResult Delete(SiteViewModel model)
{
var site = siteRepository.GetById(model.SiteId);
if (site == null) { throw new ArgumentException(string.Format("Site with Id [{0}] does not exist", model.SiteId)); }
try
{
siteRepository.SoftDeleteAndSubmit(site);
base.SetSuccessMessage("The site has been (soft) deleted.");
return RedirectToAction("Index");
}
catch (Exception ex)
{
base.SetErrorMessage("Whoops! Couldn't delete the site. The error was [{0}]", ex.Message);
}
return View(model);
}
Thanks,
Chris.
If you want a fully-populated model you'll need to use form elements or the form helper functions to post your data.
#using(Html.BeginForm())
{
<input type="text" value="#Model.Name" />
// or
Html.TextBoxFor(m => m.Name)
...
}
The example you linked relies on a URL routing rule to match a model's parameter. So you need to rename SiteId to Id or add/modify a routing rule.
If you only need the id then I would just pass that parameter as it will make your intent more obvious and is less prone to breaking.
#using(Html.BeginForm())
{
#Html.HiddenFor(m => m.Id)
<button type="submit">Delete</button>
}
[HttpPost]
public ActionResult Delete(int id)
{
var site = siteRepository.GetById(id);
...
}
I wrote something like this in the controller.
public ActionResult Giris()
{
ViewData["Tarif"] = (from t in _entities.Tarif
join k in _entities.Kullanici on t.KID equals k.KID
select new {KAdi = k.KAdi, TAdi = t.TAdi})
.Take(4);
return View();
}
I am using it as below in the view page.
<% foreach (var item in (IEnumerable<dynamic>)ViewData["Tarif"]) { %>
<div class="begenilen-video" style="float:left">
<img class="video-resmi" alt="reklam" src="../../Uygulama/Resimler/Reklam/1.jpg" />
<span class="benzer-yemek-tarifi-adi"></span><%=item.TAdi %><br />
<span class="benzer-yemek-tarifi-ekleyen">Ekleyen: </span><br />
<span class="benzer-yemek-tarifi-izlenme">İzlenme: </span>
</div>
<% } %>
However,I am receive the error in the select statement.How do I invoke the items in the view page?
Thanks in advance.
As a guess because you haven't posted the error:
The object being stored in ViewData["Tarif"] will be of the type IQueryable<T> where T is an anonymous object and in your view you are casting to IEnumerable<dynamic>. IQueryable is also lazily loaded so you will be trying to execute your query once the object has been disposed.
You should really create a strongly typed view model
public class ViewModelType {
public IEnumerable<TarifType> Tarif { get; set; }
}
Tarif type
public class TarifType {
public string KAdi { get; set; }
public string TAdi { get; set; }
}
controller
public ActionResult Giris() {
var viewModel = new ViewModelType();
viewModel.Tarif = (from t in _entities.Tarif
join k in _entities.Kullanici on t.KID equals k.KID
select new TraifType { KAdi = k.KAdi, TAdi = t.TAdi }
).Take(4)
.ToList();
return View(viewModel);
}
view
<% foreach (var item in viewModel.Tarif) { %>
<div class="begenilen-video" style="float:left">
<img class="video-resmi" alt="reklam" src="../../Uygulama/Resimler/Reklam/1.jpg" />
<span class="benzer-yemek-tarifi-adi"></span><%=item.TAdi %><br />
<span class="benzer-yemek-tarifi-ekleyen">Ekleyen: </span><br />
<span class="benzer-yemek-tarifi-izlenme">İzlenme: </span>
</div>
<% } %>