I am completely new to a project I am to maintain.
Simply question: I have in my cshtml page (which I understand are razor pages?) set up a few checkboxes and a label to test the bound class behind it.
This I got to work:
#model Application.Areas.Cms.Models.ProduktBeispielViewModel
<label>#Model.Test</label>
And the VM:
public string Test { get; set; } = "THIS IS A TEST";
And happy me: the words are displayed on my page. So the binding is working.
Now I put up a few checkboxes and once a submit button is pressed, I need to retrieve each checkbox and see if their value is checked or unchecked (shouldnt be too hard).
I first now just tried to display a value (eg true or false) from my VW onto my existing checkboxes.
This is what I did:
public bool Test2 { get; set; } = true;
CSHTML:
<input type="checkbox" name="FoodTrends" value="#Model.Test2" />
I am seeing my checkbox, but it is unchecked.
1.) Why is my simple binding not working? is "value" not the right property?
2.) How would I retrieve my value from this checkbox
Thank you all!
Please have a look at this:
I am returning my model, with the value on Test2 being false
Now this is my exact code in my view:
<input type="checkbox" name="FoodTrends" value="#Model.Test2" checked="#Model.Test2" />
And the result is that the checkbox is checked, even though the value is set to false.
I have noticed also that my checkboxes are inside "<form>" tag.
EDIT:
Razorcode (briefly):
#model Application.Areas.Cms.Models.ProduktBeispielViewModel
#{
ViewBag.PopupHeadline = "Produktbeispiele";
ViewBag.PopupSubHeadline = Model.Item != null ? Model.Item.NameInCurrentLang : "";
ViewBag.HideLanguageComparison = true;
}
#section TabMenu
{
<ul>
<li>Einstellungen</li>
<li>Bild</li>
</ul>
}
<form action="#Url.Action("SaveIndex")" method="POST" id="idForm">
#Html.HiddenFor(m => m.AutoCloseWindow)
#Html.HiddenFor(m => m.Item.Id)
<input type="checkbox" name="FoodTrends" value="#Model.Test2" />
</form>
The checkbox wouldn't work that way you have to use the checked attribute to get selected your checkbox.
#model Practice_web.Models.TestModel
#{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about building Web apps with ASP.NET Core.</p>
</div>
<div>
<input type="checkbox" name="FoodTrends" value="#Model.Test2" checked="#Model.Test2" />
</div>
Action Controller
public IActionResult Index()
{
var model = new TestModel { Test2 = true };
return View(model);
}
Here is the demo video link
Demo Link
Please try this. This works for me like a charm
I don't know if this is a bug or I just don't understand how razor pages work but I have to new up the model first in the AddModel method then it seems to pick up the default value I set in the POCO for my checkbox bool property.
[BindProperty]
public NewMember? User { get; set; }
public AddModel()
{
User = new NewMember();
}
Related
I'm working on a project for my office. The end result I'm looking for is that a boilerplate letter is pulled from a database, the sections that need specific input are extracted, a form is generated from those sections, the form then returns user data, and the letter is rebuilt with the user data integrated into the text of the letter.
for example, the string pulled from the database would look like this
Claim #: |string^clmNum^Claim Number: | - Ref#: |string^RefNum^Reference Number: |
and would end up like the following after it was rebuilt with user data:
Claim #: 123456 - Ref#: 789012
This is what I have working so far...
The sections between the | are pulled out, split, and loaded into an IEnumerable
My foo model is:
public class Foo
{
public string InputType {get; set;}
public string InputName {get; set;}
public string InputLabel {get; set;}
}
I pass the IEnumerable to the view with a ViewModel
public class FormBuildViewModel
{
public IEnumerable<Foo> FooProperty {get; set;}
}
I then display the input items dynamically with the following Razor markup on my view.
<form>
#{ var e = Model.FooProperty.ToList();
foreach (var subItem in e)
{
<div class="FormGroup-items">
<label>#subItem.InputLabel</label>
<input name="#subItem.ObjName" type="text" />
</div>
}
}
<..// form button stuff //..>
</form>
Which creates the following HTML:
<form>
<div class="FormGroup-items">
<label>Claim Number: </label>
<input name="clmNum" type="text" />
</div>
<div class="FormGroup-items">
<label>Reference Number: </label>
<input name="RefNum" type="text" />
</div>
<..// button stuff //..>
</form>
I have everything working up to this point. I need to take the data entered on the dynamically created form and get it back to the controller in a manner that I can index to rebuild the string.
I've tried using the #html.beginform similar to this
#using (Html.BeginForm())
{
#for(int i=0; i<Model.Count; i++)
{
#Html.CheckBoxFor(m => m[i].IsActive, new { #value = Model[i].Id })
#Html.DisplayFor(m => m[i].Name)
}
<input type="submit" value="Save" />
}
but to use #Html.BeginForm you need to know the names of the items before runtime, and it doesn't seem to work with a dynamically created form like this.
The only thing I can think of is I need to load the form data into a List< T > and return that to the controller, but I can't think of a way to get C# to allow me to initialize a List< T > and load the values in the view. I know I've got to be missing something, but I'm kinda lost at this point. Any help would be appreciated.
Are you passing your viewmodel back to your page? This seems like you are setting the viewmodel with data atleast from a 5000 foot view:
[HttpGet]
public IActionResult MyCallMethod()
{
FooProperty = getmydatafromsomewhere();
return View();
}
Then your page would have a way to build appropriately
#model My.Name.Space.MyViewModel
#using (Html.BeginForm("Action", "Controller"))
{
#foreach (var item in #Model.FooProperty)
{
<div class="FormGroup-items">
<label asp-for="item.InputType" />
<input asp-for="item.InputType" class="form-control" />
</div>
//other data
}
}
I also assume you have a post setup on the controller.
[HttpPost]
public IActionResult MyCallMethod(MyViewModel viewModel)
{
//do something with the viewmodel here
//same page, somewhere else, do something else, etc.
}
You can use some tag helpers as well for your labels and inputs if you so chose:
#Html.LabelFor(m => item.InputType, new { #class="whateverIwant" })
#Html.TextBoxFor(m => item.InputType, new { #class="form-control" })
I am using jquery repeater in my form to dynamically add only a part of input group to form. I did try but couldn't get inputs bind to model, I went blind.
Here my model.
public class SuggestionCreateEditViewModel
{
public Guid[] DetectionId { get; set; }
public SuggestionCreateEditRepeatedModel[] Repeated { get; set; }
}
public class SuggestionCreateEditRepeatedModel
{
public Guid To { get; set; }
public string Description { get; set; }
public DateTime Deadline { get; set; }
}
Form, I removed a lot of parts of form for brevity
<div class="col-lg-9 col-md-9 col-sm-12">
<select asp-for="DetectionId" asp-items="ViewBag.AllDetections" class="m-bootstrap-select m_selectpicker toValidate"
multiple data-actions-box="true" data-width="100%"></select>
</div>
<div class="col-lg-9 col-md-9 col-sm-12 input-group date">
<input name = "Repeated.Deadline" type="text" readonly class="form-control toValidate dtDueDate" />
</div>
<div class="col-lg-12 col-md-12 col-sm-12">
<textarea name = "Repeated.Description" class="form-control toValidate txtSuggestion" type="text" >
</textarea>
</div>
after adding a new repeated section to form and before posting it to server, if I form.serailizeArray() it returns collection like as follows (what jquery form repeater dynamically shape names I believe)
{name: "DetectionId", value: "afca1b82-0455-432e-c780-08d6ac38b012"}
{name: "[0][Repeated.To][]", value: "b1176b82-1c25-4d13-9283-df2b16735266"}
{name: "[0][Repeated.Deadline]", value: "04/04/2019"}
{name: "[0][Repeated.Description]", value: "<p>test 1</p>"}
{name: "[1][Repeated.To]", value: "188806d8-202a-4787-98a6-8dc060624d93"}
{name: "[1][Repeated.Deadline]", value: "05/04/2019"}
{name: "[1][Repeated.Description]", value: "<p>test 2</p>"}
and my controller
[HttpPost]
public IActionResult CreateSuggestion(SuggestionCreateEditViewModel model, IFormFile[] documents)
{...
controller couldn't get Repeated binded, only DetectionId is binded. How should I shape my model to get the data?
Here is a working demo for with jquery.repeater.js, pay attention to this line <div data-repeater-list="Repeated"> which will format the field like name="Repeated[0][Description]"
#model TestCore.Models.SuggestionCreateEditViewModel
#{
ViewData["Title"] = "Contact";
}
<form class="repeater" asp-action="CreateSuggestion" method="post">
<!--
The value given to the data-repeater-list attribute will be used as the
base of rewritten name attributes. In this example, the first
data-repeater-item's name attribute would become group-a[0][text-input],
and the second data-repeater-item would become group-a[1][text-input]
-->
<div data-repeater-list="Repeated">
<div data-repeater-item>
<div class="col-lg-9 col-md-9 col-sm-12">
<select asp-for="DetectionId" asp-items="ViewBag.AllDetections" class="m-bootstrap-select m_selectpicker toValidate"
multiple data-actions-box="true" data-width="100%"></select>
</div>
<div class="col-lg-12 col-md-12 col-sm-12">
<textarea name="Description" class="form-control toValidate txtSuggestion" type="text">
</textarea>
</div>
</div>
</div>
<input data-repeater-create type="button" value="Add" />
<input type="submit" value="Submit"/>
</form>
#section Scripts{
<!-- Import repeater js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.repeater/1.2.1/jquery.repeater.js"></script>
<script>
$(document).ready(function () {
$('.repeater').repeater({
// (Optional)
// start with an empty list of repeaters. Set your first (and only)
// "data-repeater-item" with style="display:none;" and pass the
// following configuration flag
initEmpty: true,
// (Optional)
// "show" is called just after an item is added. The item is hidden
// at this point. If a show callback is not given the item will
// have $(this).show() called on it.
show: function () {
$(this).slideDown();
},
// (Optional)
// "hide" is called when a user clicks on a data-repeater-delete
// element. The item is still visible. "hide" is passed a function
// as its first argument which will properly remove the item.
// "hide" allows for a confirmation step, to send a delete request
// to the server, etc. If a hide callback is not given the item
// will be deleted.
hide: function (deleteElement) {
if (confirm('Are you sure you want to delete this element?')) {
$(this).slideUp(deleteElement);
}
},
// (Optional)
// Removes the delete button from the first list item,
// defaults to false.
isFirstItemUndeletable: true
})
});
</script>
}
By the looks of things, the controller cannot bind the repeater properties back to your view model because the naming of the posted content does not match the naming in your view model (as Topher mentioned).
The DetectionId is named correctly though because the name of the property matches and its not an array.
To resolve an array we need to make sure we include the property name in the form as well as an index so that mvc model binding knows where to bind the result to.
With that, can you try changing the format of the name to:
Repeated[0].To
That should match up with your controller and correctly bind.
For more info on binding, please see this.
In the example below when clicking the button the value selected in the dropdownlist should be passed to the controller, but its not. How can I pass the value?
View:
#model BillingModel
...
<select id="ddl" asp-for="SelectedCompanyID" asp-items="Model.Companies" class="form-control"></select>
<a asp-action="Create" asp-controller="Invoice" asp-route-id="#Model.SelectedCompanyID" class="btn btn-primary btn-sm"></span> Create Invoice</a>
....
Model:
public class BillingModel
{
public int SelectedCompanyID { get; set; }
public SelectList Companies { get; set; }
}
Your link is using razor code to specify the route id value which is server side code. It does not change on the client side just because a option is selected.
Either use a form that makes a GET and submits the option value
<form asp-controller="Invoice" asp-action="Create" method="get">
<select id="ddl" asp-for="SelectedCompanyID" asp-items="Model.Companies" class="form-control"></select>
<input type="submit" value="Create Invoice" /> // you can style this to look like your link if you want
</form>
Note that this will generate the url with a query string value for the id, not a route value (i.e. it will generate ../Invoice/Create?id=1, not ../Invoice/Create/1)
Alternatively, you could use javascript/jquery to make the redirect by building a url based on the selected option
<a id="create" href="#" class="btn btn-primary btn-sm">Create Invoice</a>
$('#create').click(function() {
var baseUrl = '#Url.Action("Create", "Invoice")';
location.href = baseUrl + '/' + $('#SelectedCompanyID').val();
}
I have the following div in my view:
<div id="review">
REVIEW:
<p>
<%:ViewData["Review"]%>
</p>
<input name="submit" id="submit" type="submit" value="OK"/>
</div>
How can I set DIV visibility to visible inside of controller when a certain button is clicked?
This is a code fragment in my controller:
public ActionResult Index(EsafeModel model,string submit, string create)
{
if (button.Equals("Create"))
{
ViewData["Review"] = ESafeData.CreateReview(eSafe);
}
else if (button.Equals("OK"))
{
if (ESafeData.Create(eSafe))
{
ViewData["Message"] = "E-Safe data created!!!";
}
}
}
You can't do this directly as div is on client side and the controller is server side.
What you can do is have a property in your model which is bound to view .This can have the value of visibility attribute for div and then you can assign it using server tags to div
Below is a sample code snippet
<div id="elementid" style="visibility:'<%=Model.Visible %>'"/>
On the click of button you can return the same view and model but this time model will have Visible ="hidden"
I am pretty new in C# and .NET MVC framework and I have the following problem.
I have a first JQuery Mobile view that show a navbar containing some tabs. Into one of this tab I putted a ListView that show the element of a collection of DataModel.Vulnerability.Fix objects represented by the Model.VulnerabilityFixes into my model object. On the right of every element of the list I have put a button/link to delete the related Fix object that generate this row in the list.
This work fine and I obtain the following result (I post a screenshot):
This is the code of the previous tab (the one that show the Fix list):
<!-- TAB-2: FIXES, SOLUTION e MITIGATING STRATEGY: -->
<div id="tab-2" class="ui-body-d ui-content">
<h3>Fixes</h3>
<a href="#Url.Action("SearchCPE", "Asset", new { id = Model.Id })" data-icon="plus" data-inline="true" data-mini="true" data-role="button" >Aggungi un Fix</a>
<!-- Tabella contenente la lista delle fix: -->
<ul data-role="listview" data-inset="true" data-theme="b" data-split-icon="delete">
#foreach (DataModel.Vulnerability.Fix item in Model.VulnerabilityFixes)
{
<li><a href="#Url.Action("Details", "Product", new { Title = item.Title })">
<h2>#item.Title</h2>
<table style="width: 100%">
<tr>
<th>Id</th>
<th>FixName</th>
<th>Vendor</th>
<th>Version</th>
</tr>
<tr>
<td>#MyHelper.decodeNull(item.Id)</td>
<td>#MyHelper.decodeNull(item.FixName)</td>
<td>#MyHelper.decodeNull(item.Vendor)</td>
<td>#MyHelper.decodeNull(item.Version)</td>
</tr>
</table>
</a>
Delete
</li>
}
</ul>
</div>
<!-- /tab-2 -->
Ok, now I have a DeleteFix() method into the EditingController class that handle the request generated clicking od the Delete button.
This one:
public ActionResult DeleteFix(long vulnId, int currentFixId, string currentFixName)
{
DataModel.Vulnerability.Fix model = new DataModel.Vulnerability.Fix();
manager.openConnection();
try
{
model.Id = currentFixId;
model.FixName = currentFixName;
}
finally
{
manager.closeConnection();
}
return View(model);
}
This method show another view, the DeleteFix.cshtml file, that show a confirm window where is asked to the user to confirm the delete operation or if come back to the previous page, this is the code:
#model DataModel.Vulnerability.Fix
#{
ViewBag.Title = "DeleteFix";
Layout = "~/Views/Shared/MasterPageMobile.cshtml";
}
<h1>Delete Fix</h1>
<h2>Fix: #Model.Title (id: #Model.Id)</h2>
<p>
Confermare la cancellazione del fix "#Model.FixName" ?
</p>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<input type="hidden" name="id" value ="#Model.Id" />
<div data-role="controlgrup" data-type="horizontal" data-mini="true">
<a href="#Url.Action("Index", "Editing", new { id = Model.Id })" data-inline="true" data-mini="true" data-role="button" >Torna alla lista</a>
<a href="#Url.Action("Details", "Groups", new { id = Model.Id })" data-mini="true" data-inline="true" data-role="button" >Annulla</a>
<input type="submit" value="Delete" data-mini="true" data-inline="true" />
</div>
}
My problem is that I want that if the user click on the Torna alla lista button it have to be taken to the initial view that show the list of Fix object but I can't do it
Someone can help me to understand what am I missing? What can I do to obtain this result?
Tnx
Basically I would add a new property in the viewmodels used in the secondary views to have a trace of the primary url :
public string BackUrl { get; set; }
Maybe if you want this feature for more that I secondary view, you can create a base view model that all secondary viewmodels inherit.
Then, when calling a secondary view, just initialize the BackUrl property :
Delete
In the end, in the DeleteFix action, redirect to the BackUrl instead of redering the view (if the viewstate is valid :
public ActionResult DeleteFix(long vulnId, int currentFixId, string currentFixName)
{
if (ModelState.IsValid)
{
DataModel.Vulnerability.Fix model = new DataModel.Vulnerability.Fix();
manager.openConnection();
try
{
model.Id = currentFixId;
model.FixName = currentFixName;
}
finally
{
manager.closeConnection();
}
return Redirect(viewModel.BackUrl);
}
// Invalid viewstate, re-render the view
return View(model);
}