I would like to bind html data-* attribute to separate property in my model. How to do that?
As you can see here my button is binding to Operation property and I would like to bind data-* to property Data_RemoveAt.
public enum LinkListOperation
{
AddOne,
RemoveOne,
RemoveAll,
Submit,
RemoveAt
}
public class StepThree_Notification_TemplateEmailViewModel
{
public LinkListOperation Operation { get; set; }
[DisplayName("data-removeat")]
public int Data_RemoveAt { get; set; }
}
#using (var form = Html.BeginForm("AcceptTask", "Task", FormMethod.Post))
{
<div>Linki:</div>
for(int i = 0; i < Model.Links.Count; ++i)
{
<div>
#Html.TextBoxFor(f => f.Links[i], new { Name = string.Format("Model.Links[{0}]", i) })
<button value="RemoveAt" type="submit" name="Model.LinkOperation" data-removeat="#i">Remove</button>
</div>
}
<button value="AddOne" type="submit" name="Model.LinkOperation">MORE</button>
<button value="RemoveOne" type="submit" name="Model.LinkOperation">LESS</button>
<button value="RemoveAll" type="submit" name="Model.LinkOperation">REMOVE ALL</button>
<button value="Submit" type="submit" name="Model.Operation">OK</button>
}
If you are not using ajax, what you can do is to wrap your submit button inside a form tag and set the values you want to sent as form fields. You may use hidden fields for that.
for(int i = 0; i < Model.Links.Count; ++i)
{
<div>
#Html.TextBoxFor(f => f.Links[i],
new { Name = string.Format("Model.Links[{0}]", i) })
#using(Html.BeginForm("Remove","Home"))
{
<input type="hidden" name="Operation" value="#Model.Operation" />
<input type="hidden" name="RemoveIndex" value="#i" />
<button value="RemoveAt" type="submit" name="Model.Operation">Remove</button>
}
</div>
}
Assuming your action method looks like this
public ActionResult Remove(string Operation,int RemoveIndex)
{
// to do : return something with the passed in values.
}
If you already have an outer form tag, This approach won't be ideal as nested forms are not a good idea. You may consider using javascript-ajax code to read the data attribute value and send it to server.
You do not need to add the form tag like what i mentioned above. Keep your markup as it is except, we will add a css class to the submit button which we will use later as the jQuery selector.
<button value="RemoveAt" class='removeBtn' data-removeat="#i"
type="submit" name="Model.Operation">Remove</button>
And add this javscript to listen to the click event on the button and read the data attribute value and then make an ajax post call. Assuming you have jQuery library loaded to your page,
$(function(){
$(".removeBtn").click(function(e){
e.preventDefault();
var _this=$(this);
var removeIndex=_this.data("removeat");
var op=_this.attr("name");
var url = "#Url.Action("Remove","Home")";
url=url+"?Operation="+op+"&RemoveIndex="+removeIndex;
$.post(url,function(res){
//do something when a response comes back, may be update the UI/reload the page ?
//window.location.href=window.location.href;
});
});
});
Related
hello community I have a problem putting a bind-value and an onchange shows me the following error:
The attribute 'onchange' is used two or more times for this element. Attributes must be unique (case-insensitive). The attribute 'onchange' is used by the '#bind' directive attribute.
this is my input checkbox:
<div class="form-group col-md-2">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="customSwitch1"
#bind-value="#ProveedorEstadoCarrito.Cotizacion.Aceptada"
#onchange="e => CheckChanged(e)">
<label class="custom-control-label" for="customSwitch1">Aceptada</label>
</div>
</div>
this is the event:
private Boolean Aceptada = false;
private async Task CheckChanged(ChangeEventArgs ev)
{
Aceptada = (Boolean)ev.Value;
ProveedorEstadoCarrito.Cotizacion.Aceptada = Aceptada;
if (Aceptada == true)
{
var httpResponse = await repositorio.Put("api/Cotizacion", ProveedorEstadoCarrito.Cotizacion);
if (httpResponse.Error)
{
await mostrarMensajes.MostrarMensajeError(await httpResponse.GetBody());
}
else
{
navigationManager.NavigateTo("/formulario-cotizacion");
}
}
}
I want the checkbox to be activated with the bind if it was clicked
First off, you usually don't bind to the value attribute. It remains fixed, and when present, and within a form element, it is passed as form data to the server.
What you want is the checked attribute, like the code snippet below demonstrates:
<input type="checkbox" checked="#selected"
#onchange="#((args) => selected = (bool) args.Value)" />
#code {
private bool selected;
}
The above code show how to bind to a check box element. As you can see, the above code creates a two-way data binding, from a variable to the element, and from the element to the variable. The value attribute is involved. The same usage is applicable to the radion button element. Unlike other input elements, both employ the checked attribute, not the value attribute.
You may also use this variation:
<input type="checkbox" #bind="selected" />
which is equivalent to the code above: a checked attribute + onchange event, but the version above can let you solve your issue. Here's how your code may look like:
<input type="checkbox" class="custom-control-input" id="customSwitch1" checked="#ProveedorEstadoCarrito.Cotizacion.Aceptada" #onchange="CheckChanged">
And
private async Task CheckChanged(ChangeEventArgs ev)
{
Aceptada = (Boolean)ev.Value;
ProveedorEstadoCarrito.Cotizacion.Aceptada = Aceptada;
if (Aceptada == true)
{
Hope this helps...
You can't. But you can set checked=ProveedorEstadoCarrito.Cotizacion.Aceptada to update the state of the checkbox, and for the #onchange event do #onchange=CheckChanged and in that method you can set ProveedorEstadoCarrito.Cotizacion.Aceptada = (bool) ev.Value;
We have a list bound to view as
#model List<DataModels.UseCase>
This view contains html form as
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count(); i++)
{
#Html.CheckBoxFor(m => Model[i].IsSelected)
//few other controls as
}
<input type="submit" value="Submit Selection" >
}
And In Controller, POST method is like below
[HttpPost]
public ActionResult payment([Bind(Include = "Id,IsSelected// few other properties")] List<UseCase> useCases)
{
// Few business logic
return View();
}
Please note- Just for example I have shown only check box control on form, there are other few controls as well.
Now, in this case for example view contains 10 records but out of 10 only 2 are selected then we need to pass only 2 selected records to POST method and not all 10. This is to reduce overload on POST method.
Can we achieve this type of scenario in any way?
Good question, I might implement this on my projects as well.
I could only think of one way-- using javascript, when form is submitted, delete the other form input fields first then resubmit the form.
First is we need to put the input fields inside a parent div with class input-container, so we could quickly delete all the fields by just deleting the entire div. I also added a class targetCheckbox to your input field so we could attach an event to it;
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Count(); i++)
{
<div class="input-group">
#Html.CheckBoxFor(m => Model[i].IsSelected, new { #class="targetCheckbox" })
//few other controls as
<div class="input-group">
}
<input type="submit" value="Submit Selection" >
}
We'll need to bind an event to to your form. On form submit, we need to identify which targetCheckbox are not checked, then delete the div that contains them. We also need to replace the indexes of the input fields because ASP.NET MVC model binding must start with 0 and should not skip. After all that resubmit the form;
<script>
$(document).ready(function(){
$("form").submit(function(e){
e.preventDefault();
var index = 0;
// loop through all the checkbox
$(".targetCheckbox").each(function(){
if($(this).is(":checked")){
// get the parent
var parent = $(this).closest(".input-container");
// loop through all the input fields inside the parent
var inputFieldsInsideParent = $(parent).find(":input");
// change the index inside the name attribute
$(inputFieldsInsideParent).each(function(){
var name = $(this).attr("name");
var firstBracket = name.IndexOf("[");
var secondBracket = name.IndexOf("]");
if(firstBracket != null && secondBracket != null){
// check if this is a valid input field to replace
var newName = name.substring(0,firstBracket)+index+name.substring(secondBracket);
// result should be IntputFieldName[newIndex].Property
// assign the new name
$(this).attr("name",newName);
}
});
index++;
}else{
// empty the parent
$(this).closest(".input-container").html("");
}
});
// submit the form
$(this).submit();
});
});
</script>
I'm trying to create paging inside html form.
How do I make each page button change the model's CurrentPage property?
I'm new to MVC, so bear with me please.
I have a list of reports that I need to display in a view named Search. I made a model for that (ReportViewModel), and another model that holds a list of those, named SearchViewModel. In SearchViewModel I have some other variables like SearchName, SearchDate, CurrentPage, PageCount, PageSize, etc. The goal is to do paging and searching in one action named Search in ReportsController.
Now, I would like to keep that action simple by giving that function only SearchViewModel instance as a parameter, so that inside I use all variables of that model. Doing that, I won't have to type parameter names twice (once in SearchViewModel and once as parameters in Search action). I will not need that search function elsewhere, so it's ok for it to not have any parameters other than SearchViewModel instance.
So, very loosely, it looks something like this:
ReportsController.cs
public class ReportsController : Controller
{
public ActionResult Search(SearchViewModel model)
{
if (!ModelState.IsValid)
return View(model);
// do search magic here
return View(model)
}
}
Search.cshtml
#model ISFinReports.Models.SearchViewModel
#{
ViewBag.Title = "List";
Layout = "~/Views/_masterLayout.cshtml";
}
<link href="~/CSS/Search.css" rel="stylesheet" />
<script src="~/Scripts/Search.js"></script>
#using (Html.BeginForm("Search", "Reports", FormMethod.Get))
{
<div id="divSearchBar">
// add #Html.DropDownListFor, #Html.TextBoxFor and submit button here
</div>
<div id="divReportsTable">
<table id="tblReports">
#if (Model.Reports != null)
{
foreach (var r in Model.Reports)
{
// add tr, td of all reports in report list
}
}
</table>
</div>
<div style="text-align:center">
#for (int i = 0; i < Model.PageCount; i++)
{
#Html.ActionLink(i.ToString(), "Search", "Reports", new { CurrentPage = i });
// or
// <input type="submit" value="#i" onClick="meybe something like this?" />
}
Current Page #Html.EditorFor(q => q.CurrentPage), of total #Model.PageCount pages
</div>
}
I know that I can simply type twice all the data I need to flow between controller and view, once in model and once as Search action parameters, but that's extactly what I'm trying not to do. Currently I have a textbox for editing the current page, and it works, because it's created via #Html.EditorFor, but I'd like to have multiple buttons for each page.
I could maybe create a hidden field in that form, and create a javascript function that'll find that hidden field by id, and change it's value, and then call whatever the submit button onClick function is. BUT, is there a better/shorter/more-MVC way to implement that?
I have two text boxes. One textbox for input and other for output. when number entered in first textbox by button click then i want to find square of that number in second textbox by clicking Square button. But i am not able to get the desired result. Nothing is displayed in second textbox when Square button is clicked.
Here is Code snippet:
#Html.TextBoxFor(model => model.textBox, new { #readonly = "readonly" })
<br/>
#Html.TextBoxFor(model => model.textBox1, new { #readonly = "readonly" })
<input name="button" type="submit" id="btntwo" value="2" />
<input name="button" type="submit" id="btnthree" value="3" />
<input name="button" type="submit" id="btnfour" value="4" />
<input name="button" type="submit" id="btnSqr" value="Sqr" />
Here is code snippet for controller:
if (button == "Sqr")
{
model.value1 = model.textBox;
model.textBox1 = (float.Parse(model.value1) * float.Parse(model.value1)).ToString();
}
Note: I have only provided the code which is required to solve the issue.
Here is my assumptions of what you are doing "IF You Use Server Side Solution"
public ActionResult FindSquare()
{
SquareModel model = new SquareModel(); // model contains textbox, textbox1
return View(model);
}
And when you submit form on button click
public ActionResult FindSquare(SquareModel model, FormCollection collection)
{
if(!string.IsNullOrEmpty(collection["button"]) && collection["button"].ToString() == "Sqr")
{
double value = Convert.ToDouble(model.textBox);
var result = value * value;
model.textBox1 = Convert.ToString(result);
return View(model);
}
}
Edit:
Remove removeonly from your view page.
Use Client Side Script to do it easily
$('#btntwo').click(function (e) {
var num = parseInt($('#textBox').val());
$('#textBox1').val(num*num);
return false;
});
If you want to continue in server side, you have to remove #readonly =
"readonly".
I suspect the issue you're having is related to the fact you've got two textboxes (textBox and textBox1), and both are read-only. Nothing can be changed, so perhaps the controller is not firing at all?
I'd also be worried your controller implementation is not wired up properly. You've provided too little code to validate the mainstay failure points for postback processing are not the cause.
On an app I'm working on, I have a requirement that, upon clicking a certain button, validation fires on some related textboxes; if they do not pass, nothing occurs, otherwise an action that is fired from an AJAX call occurs. This AJAX call returns some message about the success of the operation.
My partial view this is occurring in looks a bit like this:
<div>
<div id='cell-phone'>
#Model.ValidationMessageFor(x=>x.Model.CellNumber)
#Model.TextBoxFor(x=>x.Model.CellNumber)
</div>
<div id='pager'>
<!-- Ditto, only x.Model.Pager; yes, some people use pagers still. -->
</div>
<div id='page-address'>
<!-- ... x.Model.PageAddress ... -->
</div>
<div id='pager-test'>
<a href='#' id='test-pager' class='button'>Test</a>
<span id='test-result'></span>
</div>
</div>
<script>
var $cellNum = $('#cell-phone input'),
$pagerNum = $('#pager input'),
$pageAddress = $('#page-address input'),
$testPager = $('#pager-test'),
$testResult = $('#test-result');
$(document).ready(function () {
$testPager.click(function () {
pagerTest();
});
});
function pagerTest() {
var args = { address: $pageAddress.val() };
$.getJSON('#Url.Action("SendTestPage")', args, function(result) {
$testResult.html(result.Message);
});
}
</script>
...down at the server level...
public JsonResult SendTestPage(string address)
{
// SNIP: Other unnecessary details.
var result = new EmailSendResult
{
success = SendEmailMethod(address)
};
result.message = result.success
? "Message sent!"
: "Couldn't send message...";
return result;
}
....
public class EmailSendResult
{
public bool success;
public string message;
}
Question: while I am able to get the message/success values back, I also need to cause the View Model's validations to fire. I don't see how to do this using an AJAX call. My suspicion is that either A) I'm using the wrong tool for the job, or B) I'm using the right tool for one job, but I need something else. What am I missing to be able to cause validations to fire?
When you click on 'test-pager' link, the action will be called but the validation of your form doesn't trigger because your link is not a submit. If you want to validation work you must have a submit button on the form. When the user clicks it the validation will fire. So change the test-pager to something like this:
<input type="submit" id="test-pager" class="button" />
Or ( if I understand question correctly) you can bind address textbox change event and within it call testPage function.