Kendo Observable binding for checkbox is always showing ticked - asp.net-mvc

I am using ASP.NET MVC application with Kendo framework. For some reason, i am always getting the checkbox "#IsInterestDeemed" in ticked state. Although, the viewmodel property "IsInterest" is false under the controller action method.
Please suggest where am i making the mistake.
<div id="RunModelDiv" style="min-width:300px">
<div>
<input type="checkbox" id="IsInterestDeemed" value="IsInterestDeemed" data-bind="checked: IsInterestDeemed, disabled: IsReadOnly" />
<label for="IsInterestDeemed"> Interest</label>
</div>
<div>
<script>
var myViewModel;
$(document).ready(function(){
myViewModel = kendo.observable({
IsReadOnly: #Html.Raw(Json.Encode(Model.IsReadOnly)),
IsInterestDeemed : '#Html.Raw(Json.Encode(Model.IsInterest))'});
kendo.bind($("#RunModelDiv"), myViewModel);
});
</script>
ViewModel Property:-
public bool IsInterest { get; set; }
public bool IsReadOnly { get; set; }

Why are the values for IsReadOnly and IsInterestDeemed in the Javascript handled differently (one is a string, the other is raw text)? Perhaps this is what is causing a syntax error on the page when it loads, and so the page will not behave as expected.
More specifically, these two lines are inconsistent:
IsReadOnly: #Html.Raw(Json.Encode(Model.IsReadOnly))
IsInterestDeemed : '#Html.Raw(Json.Encode(Model.IsInterest))'
Check the HTML output and verify your solution.

Related

ASP Net Core - hidden input field data is not passed in the Controller

I need to pass which button is clicked for the form submit. But the hidden input is not received in the controller. Below are my code snippets
View Model:
public class DocumentViewModel
{
public int Id { get; set; }
public int ActionId { get; set; }
}
razor(cshtml):
<form....>
<input id="docActionId" name="docActionId" asp-for="ActionId" type="hidden" value="initialValue" />
</form>
JavaScript:
$("#save_btn").on("click", function ()
{
$("#docActionId").val("test1");
});
$("#submit_btn").on("click", function ()
{
$("#docActionId").val("test2");
});
I tried showing the current value in alert function if it is change and it did.
In my Controller/Action when I debug, I get a null value for the ActionId variable. I think this is very simple but I don't know what I did wrong.
Please help. I'm stucked in this for 2 hours now. Thanks in advance.
remove the "Id" attribute... that's all you need. "asp-for" does that. adding yours duplicates it kind
Remove the name="docActionId" html attributes, they will be generated automatically by Razor.
Specifically by the asp-for="ActionId" attribute.
Furthermore the reason this doesn't work is because the name attribute which is docActionId != ActionId which is the name of the property of your object. So it doesn't know where to bind it to.
<form id="myform">
<input id="docActionId" asp-for="ActionId" type="hidden" value="initialValue" />
<button type="submit" id="save">Submit</button>
</form>
Try this javascript perhaps the form is submitted before you attach the value
var saveButton = documnet.getElementById('save')
saveButton.addEventListener('click', function(e) {
e.stopPropagation();
documnet.getElementById('docActionId').value = "test value"
documnet.getElementById('myform').submit()
})
<input type="hidden" id="tag" >
type="hidden" didn't work for me with .net core 7 but only hidden keyword worked.
<input hidden id="tag">

Can't bind checkbox in partial view to the main model (MVC)

I've been running into to issue and I've been searching for an answer but nothing helped.
I have a Model:
public class Filters
{
public bool Filter1 { get; set; }
public bool Filter2 { get; set; }
public bool Filter3 { get; set; }
etc...
}
I have a partial view with multiple checkboxes and tried multiple things:
<input id="Filter1" name="Filter1" type="checkbox" value="true">
<input type="hidden" value="false" name="Filter1" />
and
#Html.CheckBoxFor(model => model.Filter1)
Then I have a main model:
public class Dashboard
{
...
public Filters FiltersDashboard { get; set; }
}
And somewhere in the main view I insert the partial view like this:
#Html.EditorFor(model => model.FiltersDashboard, "Filters")
In a jquery, I execute an alert when the checkbox is clicked and shows the value of the checkbox. This value remains unchanged.
<script>
$("#Filter1").click(function () {
alert(" #Model.FiltersDashboard.Filter1 ")
});
</script>
EDIT: Also tried it in the .submit function but model value remains unchanged:
<script>
$("#searchform").submit(function (event) {
alert(" #Model.FiltersDashboard.Filter1 ")
event.preventDefault();
});
</script>
This tells me that something isn't correctly bound but I have no clue what I'm doing wrong.
Also, the reason I'm not using a checkboxlist is because I need to execute a different query for each filter so I need specific names and bindings for them.
#Model.FiltersDashboard.Filter1 is razor code and is executed on the server before its sent to the view, so it will only ever return the initial value of the property (if you inspect the source code, you will see the initial value has been hard coded in your script).
However, if your script is being executed, then it means that you are using the manual <input> tags in your 2nd code snippet, in which case your view is not binding to your model because the correct name attribute would be name="FiltersDashboard.Filter1" and the associated id attribute would be id="FiltersDashboard_Filter1".
Always use the strong typed #Html.CheckBoxFor() which will generate the correct html for 2-way model binding and client side validation.
Note also that it just needs to be #Html.EditorFor(model => model.FiltersDashboard) - the 2nd parameter is unnecessary.
Then the script should be
<script>
$('#FiltersDashboard_Filter1').click(function () {
var isChecked = $(this).is(':checked');
alert(isChecked);
});
</script>

Create dynamic forms that grow at run time

I'm working in asp.net core inside a MVC application. I'm using the scaffolding feature that creates the views and controller based on a model. Below is the model that i'm using:
class ShoppingList
{
public int ShoppingListId { get; set; }
public string Name { get; set; }
public List<string> ListItems { get; set; }
}
The form that displays to the user via the view only displays the field for Name. I would like the form to be able to show a field for a list item, and then if the user wants to add another list item they can hit a button to add another field to do so. They at run time decide how many shopping list items they want to add.
Here is the razor cshtml form i'm using:
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
Is there an easy way to do this? I don't want to have to hard code a number.
If you want to allow the user to add a new form element on the client side you need to use javascript to update the DOM with the new element you want to add. To list the existing items you may use editor templates. Mixing these 2 will give you a dynamic form. The below is a basic implementation.
To use editor templates, we need to create an editor template for the property type. I would not do that for string type which is more like a generic one. I would create a custom class to represent the list item.
public class Item
{
public string Name { set; get; }
}
public class ShoppingList
{
public int ShoppingListId { get; set; }
public string Name { get; set; }
public List<Item> ListItems { get; set; }
public ShoppingList()
{
this.ListItems=new List<Item>();
}
}
Now, Create a directory called EditorTemplates under ~/Views/YourControllerName or ~/Views/Shared/ and create a view called Item.cshtml which will have the below code
#model YourNameSpaceHere.Item
<input type="text" asp-for="Name" class="items" />
Now in your GET controller, create an object of the ShoppingList and send to the view.
public IActionResult ShoppingList()
{
var vm = new ShoppingList() { };
return View(vm);
}
Now in the main view, All you have to do is call the EditorFor method
#model YourNamespace.ShoppingList
<form asp-action="ShoppingList" method="post">
<input asp-for="Name" class="form-control" />
<div class="form-group" id="item-list">
Add
#Html.EditorFor(f => f.ListItems)
</div>
<input type="submit" value="Create" class="btn btn-default" />
</form>
The markup has an anchor tag for adding new items. So when user clicks on it, we need to add a new input element with the name attribute value in the format ListItems[indexValue].Name
$(function () {
$("#add").click(function (e) {
e.preventDefault();
var i = $(".items").length;
var n = '<input type="text" class="items" name="ListItems[' + i + '].Name" />';
$("#item-list").append(n);
});
});
So when user clicks it adds a new input element with the correct name to the DOM and when you click the submit button model binding will work fine as we have the correct name attribute value for the inputs.
[HttpPost]
public IActionResult ShoppingList(ShoppingList model)
{
//check model.ListItems
// to do : return something
}
If you want to preload some existing items (for edit screen etc), All you have to do is load the ListItems property and the editor template will take care of rendering the input elements for each item with correct name attribute value.
public IActionResult ShoppingList()
{
var vm = new ShoppingList();
vm.ListItems = new List<Item>() { new Item { Name = "apple" } }
return View(vm);
}
First this is you must have a public accessor to your ShoppingList class.
So, public class ShoppingList.
Next is your view will need the following changes.
#model ShoppingList
<h1>#Model.Name</h1>
<h2>#Model.ShoppingListId</h2>
foreach(var item in Model.ListItems)
{
<h3>#item</h3>
}
So, the above code is roughly what you are looking for.
In Razor you can accessor the models variables by using the #model at the top of the view. But one thing you need to note is if your model is in a subfolder you'll need to dot into that.
Here's an example: #model BethanysPieShop.Models.ShoppingCart.
Here BethanysPieShop is my project name, Models is my folder the ShoppingCart class is in.

MVC Razor Form submit not working

I am trying to use a simple form to allow authorized users to modify content on select pages on an MVC3 Razor site that I'm building. I am unable to get the edit form to post correctly though.
My model is as follows:
public class WebContent
{
public virtual UInt32 id { get; set; }
public virtual String page { get; set; }
public virtual String section { get; set; }
[UIHint("tinymce_jquery_full"), AllowHtml]
public virtual String content { get; set; }
}
My Controller:
[Authorize]
public ActionResult Edit(String page, String section)
{
WebContent content = _WebContent.GetSection(page,section);
return View(content);
}
[Authorize]
[HttpPost]
public ActionResult Edit(WebContent content)
{
if (ModelState.IsValid)
{
_WebContent.Update(content);
return View("Index");
}
else return View("Index");
}
And my View:
#model SongbirdsStudios.Models.WebContent
#{
ViewBag.Title = "Edit '"+Model.page+"'Page Content";
}
<div>
<h2>Edit</h2>
#using (Html.BeginForm())
{
<fieldset>
<legend>Page Content</legend>
#Html.HiddenFor(m => m.id)
#Html.HiddenFor(m => m.page)
#Html.HiddenFor(m => m.section)
<div class="editor-label">
Content
</div>
<div class="editor-field">
#Html.EditorFor(m => m.content)
</div>
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
}
</div>
The view renders correctly and displays the expected elements. The UIHint("tinymce_jquery_full") is getting picked up correctly and the TinyMCE editor appears on the page. But, when the form submits, I get an exception.
System.Web.HttpRequestValidationException: A potentially dangerous Request.Form value was detected from the client (content=...)
Everything I've read indicates that the AllowHTML attribute should allow this to post, but it's not for some reason.
I can get around this by adding the [ValidateInput(false)] attribute to the HttpPost controller method. If I do that, then this exception does not occur, but the model still does not get passed to the controller. It just passes null instead. Examining the HttpContext in the debugger indicates that it is passing 4 separate values - one for each property in my model instead of passing the model class back to the controller. I can't figure out what I need to change to make this work correctly.
I'm hoping it's something simple that I missed, and someone with a better eye can see what it is.
So after further investigation into how ASP MVC maps form fields to the model class and examining the HTML emitted to the browser, I found that this was an issue with the name of the property in my WebContent class.
public virtual String content { get; set; }
The TinyMCE editor uses a content variable to define certain characteristics associated with the editor interface. This was apparently causing the HTML 'content' generated by the user input in the editor to not get mapped back to the Model property.
Simply changing the name of the property in the model class (and of course fixing the corresponding database mapping and view references) immediately fixed the problem.
public virtual String web_data_content { get; set; }
Everything else being identical, this worked perfectly with the UIHint and AllowHTML attributes.
Add this attribute on your action
[ValidateInput(false)]
This should solve your problem
if you use ie7
this may has some err
<input type="submit" value="Update" />
give the button a name

ASP.NET MVC Paging for a search form

I've read several different posts on paging w/ in MVC but none describe a scenario where I have something like a search form and then want to display the results of the search criteria (with paging) beneath the form once the user clicks submit.
My problem is that, the paging solution I'm using will create <a href="..."> links that will pass the desired page like so: http://mysite.com/search/2/ and while that's all fine and dandy, I don't have the results of the query being sent to the db in memory or anything so I need to query the DB again.
If the results are handled by the POST controller action for /Search and the first page of the data is rendered as such, how do I get the same results (based on the form criteria specified by the user) when the user clicks to move to page 2?
Some javascript voodoo? Leverage Session State? Make my GET controller action have the same variables expected by the search criteria (but optional), when the GET action is called, instantiate a FormCollection instance, populate it and pass it to the POST action method (there-by satisfying DRY)?
Can someone point me in the right direction for this scenario or provide examples that have been implemented in the past? Thanks!
My method is to have an Action that handles both the post and the get scenarios.
This is my which can be handled by both GET and POST methods:
public ViewResult Index([DefaultValue(1)] int page,
[DefaultValue(30)] int pageSize,
string search,
[DefaultValue(0)] int regionId,
[DefaultValue(0)] int eventTypeId,
DateTime? from,
DateTime? to)
{
var events = EventRepo.GetFilteredEvents(page, pageSize, search, regionId, eventTypeId, from, to);
var eventFilterForm = EventService.GetEventFilterForm(from, to);
var eventIndexModel = new EventIndexModel(events, eventFilterForm);
return View("Index", eventIndexModel);
}
The eventFilterForm is a presentation model that contains some IEnumerable<SelectListItem> properties for my search form.
The eventIndexModel is a presentation model that combines the eventFilterForm and the results of the search - events
The events is a special type of IPagedList. You can get more information and code for that here and here. The first link talks about IPagedList where as the second link has an Advanced Paging scenario which you should need.
The advanced paging has the following method that I use:
public static string Pager(this HtmlHelper htmlHelper, int pageSize, int currentPage, int totalItemCount, RouteValueDictionary valuesDictionary)
And I use it like so:
<%= Html.Pager(Model.Events.PageSize,
Model.Events.PageNumber,
Model.Events.TotalItemCount,
new
{
action = "index",
controller = "search",
search = ViewData.EvalWithModelState("Search"),
regionId = ViewData.EvalWithModelState("RegionId"),
eventTypeId = ViewData.EvalWithModelState("EventTypeId"),
from = ViewData.EvalDateWithModelState("From"),
to = ViewData.EvalDateWithModelState("To")
}) %>
This creates links that look like:
/event/search?regionId=4&eventTypeId=39&from=2009/09/01&to=2010/08/31&page=3
HTHs,
Charles
Ps. EvalWithModelState is below:
PPs. If you are going to put dates into get variables - I would recommend reading my blog post on it... :-)
/// <summary>
/// Will get the specified key from ViewData. It will first look in ModelState
/// and if it's not found in there, it'll call ViewData.Eval(string key)
/// </summary>
/// <param name="viewData">ViewDataDictionary object</param>
/// <param name="key">Key to search the dictionary</param>
/// <returns>Value in ModelState if it finds one or calls ViewData.Eval()</returns>
public static string EvalWithModelState(this ViewDataDictionary viewData, string key)
{
if (viewData.ModelState.ContainsKey(key))
return viewData.ModelState[key].Value.AttemptedValue;
return (viewData.Eval(key) != null) ? viewData.Eval(key).ToString() : string.Empty;
}
Make the Search parameter part of your View Model:
public SearchViewModel
{
string SearchParameters { get; set; }
List<SearchObjects> SearchResults { get;set; }
}
Then just set the Search Textbox equal to SearchParameters.
You cannot "store" the search query unless you bring back ALL results and then store those in the page somehow. That is horribly inefficient. The web is stateless, so you will have to go back to the database and re-query for more results.
I understand what you are saying; you could change the form to use buttons and post the page back everytime. Or, you could pass all the criteria in the URL for the paging as querystring variables. Or you could use JQuery to do the post (it has a $.post method that can be invoked from a link click or other click (http://api.jquery.com/jQuery.post/).
HTH.
This problem goes away if you include the search text, as well as the current results page, in your querystring instead of POSTing the search text. As an added benefit, your users can then bookmark their search results.
To do this your search button just needs to build the GET request URL using the current value of the search box. This can be done either in javascript or by using GET as your search form's method attribute, e.g. <form method="get" action="/search">.
I recommend cacheing your search results and giving them an ID. Then for each paging link, you can reference the search ID as a parameter (on each search page link) and in your action, pull it from cache, then query over it.
Using this method, you don't need to worry about anything other than the first POST submit of the search form.
Refer to my post for more details.
I had this same problem and here's what I did.
Download PagedList from Nuget
Change your form to do a GET and create a ViewModel type similiar to this (if you love AdventureWorks and Model Binding as much as I do):
`
using PagedList;
namespace SearchFormResultPagingExample.Models {
public class SearchViewModel {
public int? Page { get; set; }
public string EmailAddress { get; set; }
public string LastName { get; set; }
public IPagedList<Contact> SearchResults { get; set; }
public string SearchButton { get; set; }
}
}
`
3.Use the ViewModel as the parameter to your controller's action method
using System.Linq;
using System.Web.Mvc;
using SearchFormResultPagingExample.Models;
using PagedList; //NOTE: use Nuget to reference PagedList
namespace SearchFormResultPagingExample.Controllers {
public class SearchController : Controller {
const int RecordsPerPage = 25;
public ActionResult Index(SearchViewModel model) {
if (!string.IsNullOrEmpty(model.SearchButton) || model.Page.HasValue) {
var entities = new AdventureWorksEntities();
var results = entities.Contacts.Where(c => c.LastName.StartsWith(model.LastName) && c.EmailAddress.StartsWith(model.EmailAddress))
.OrderBy(o => o.LastName);
var pageIndex = model.Page ?? 0;
model.SearchResults = results.ToPagedList(pageIndex, 25);
}
return View(model);
}
}
}
Use the pager on in your View:
#model SearchFormResultPagingExample.Models.SearchViewModel
#using PagedList.Mvc;
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm("Index", "Search", FormMethod.Get)) {
#Html.ValidationSummary(false)
<fieldset>
<legend>Contact Search</legend>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmailAddress)
#Html.ValidationMessageFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
<p>
<input name="SearchButton" type="submit" value="Search" />
</p>
</fieldset>
}
#if (Model.SearchResults != null && Model.SearchResults.Count > 0) {
foreach (var result in Model.SearchResults) {
<hr />
<table width="100%">
<tr>
<td valign="top" width="*">
<div style="font-weight: bold; font-size:large;">#result.LastName, #result.FirstName</div>
#result.Title<br />
#result.Phone<br />
#result.EmailAddress
</td>
</tr>
</table>
}
<hr />
#Html.PagedListPager(Model.SearchResults,
page => Url.Action("Index", new RouteValueDictionary() {
{ "Page", page },
{ "EmailAddress", Model.EmailAddress },
{ "LastName", Model.LastName }
}),
PagedListRenderOptions.PageNumbersOnly)
}
MVC will coerce the querystring to and from your ViewModel type parameter. It's very slick!

Resources