ASP.Net WebApi and KO Issue with Dates - c#

I am using EF in an ASP.Net MVC application and also using WebApi to get an element like so:
[HttpGet]
public Student GetStudent(int id)
{
return db.Students.AsNoTracking().FirstOrDefault(n => n.Student_ID == id);
}
This all works great and I map it directly into ko for use. However when I bind the value of any DateTime properties of the element i.e. date of birth I get: 1955-04-17T11:13:56. I don't want to change the web api methods or the model for every date, is there a 3rd party library or a knockout function that can handle a datetime from asp.net and correct the value for inputs.

As Sridhar suggested in the comment. You can use moment.js to achieve this.
I've created a sample fiddle here - http://jsfiddle.net/sherin81/ordwenj6/
Knockout code
function viewModel()
{
var self = this;
self.dateInput = ko.observable("1955-04-17T11:13:56");
self.formattedDate = ko.computed(function(){
var m = moment(self.dateInput());
return m.format("DD-MM-YYYY");
});
}
ko.applyBindings(new viewModel());
HTML
<input data-bind="value : dateInput" />
<br/>
<span data-bind="text : formattedDate()" />
For demonstration, I've used the value from the input field for formatting. You can modify the code to use the value from the webapi and format it using moment.js.
Update
To do the same using ko custom binding handler, do the following
ko.bindingHandlers.dateFormattedByMoment = {
update: function (element, valueAccessor, allBindingsAccessor) {
$(element).val(moment(valueAccessor()).format("DD-MM-YYYY"));
}
};
HTML
<input id="customBinding" data-bind="dateFormattedByMoment : dateInput()" />
Working fiddle here - http://jsfiddle.net/sherin81/ujh2cg73/

Related

Why model is null after postback in ASP.NET MVC

I'm developing an ASP.NET MVC app using the latest .NET Framework and VS 2019.
In my page, I have an simple textbox and a search button. When I click the search button and postback the form, in controller, I get model.Code is NULL
Here is my code - my view model:
public class UserManagementViewModel
{
public string Code { get; set; }
// some other properties including simple and complex types
}
In View:
<td style="min-width: 300px">
#Html.TextBoxFor(m => m.Code)
</td>
In Controller:
[HttpPost]
public ActionResult UpdateUserPermissions(UserManagementViewModel model)
{
// model.Code is null here!!
return RedirectToAction("UserManagement");
}
After hours of research, I tried following:
I looked into html source and found
<input id="Code" name="Code">
Using Chrome dev tool, I change the name of the input to "Model.Code"
<input id="Code" name="Model.Code">
And this time, I got the entered value in textbox after postback.
I don't know why this happens and why the element name is not generated correctly!
What can I do?
Edit:
I have some other controls like checkbox and radio-buttons which I use pure HTMl code and name them beginning with "Model." like below instead of using #HTML helper.
And it works fine and I get the value in Action.
Workaround:
I found a workaround here: ASP.NET MVC 4 override emitted html name and id so I change my code to:
#Html.TextBoxFor(m => m.Code, new { #Name = "Model.Code" })
However this code helps me to fix my problem, I still don't know why the name generated by Razor engine is not working.
The helper method #Html.TextBoxFor works absolutely correctly. Basically, you need to use this method for almost all situations (except some situations when you need to use no-standard behavior).
Regarding your case, I think you have trouble with HtmlFieldPrefix.
This property is used for the generation of HTML input's name. We can review MVC source codes in GitHub...
MVC generated HTML input (GitHub link) using this code snippet:
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (String.IsNullOrEmpty(fullName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
}
TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullName, true);
string valueParameter = htmlHelper.FormatValue(value, format);
bool usedModelState = false;
...
return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
}
As we can see name property calculated by method GetFullHtmlFieldName.
This method we can also inspect on GitHub (link):
public string GetFullHtmlFieldName(string partialFieldName)
{
if (partialFieldName != null && partialFieldName.StartsWith("[", StringComparison.Ordinal))
{
// See Codeplex #544 - the partialFieldName might represent an indexer access, in which case combining
// with a 'dot' would be invalid.
return HtmlFieldPrefix + partialFieldName;
}
else
{
// This uses "combine and trim" because either or both of these values might be empty
return (HtmlFieldPrefix + "." + (partialFieldName ?? String.Empty)).Trim('.');
}
}
This method calculates the name using the HtmlFieldPrefix property. So if you change this property you can modify your generated name.
I think you use partial Views and you need to render property with some prefix for correct binding.
Please review next links with examples and possible solutions:
ASP.NET MVC Partial Views with Partial Models
ASP.NET MVC3 add a HtmlFieldPrefix when calling Controller.PartialView
Define a custom HTML prefix for your .NET MVC models
in other to sumbmit model back you have to use form tags. MVC sometimes counts name as model property, but it is better to do it explicitly. This code was tested and both variants of textbox are working properly. And if you use the form you don't need any name at all, other contrary in the most cases you will get null if you use the name and form together.
#model UserManagementViewModel
<form asp-action="UpdateUserPermissions" method="post">
#Html.TextBoxFor(m => m.Code)
// or
<div class="form-group">
<label asp-for="Code" class="control-label"></label>
<input asp-for="Code" class="form-control" />
</div>
<div>
<button type="submit"> submit </button>
</div>
</form>

Return json to partial view in core 2

I would like to create a view that would contain a different view. I've never used json before. How i can do this and How can I format the json data in the view?
My first function "Details" is to retrieve a object from the database and return view "Details.cshtml". In this view I want generates a partial view ("Stats.cshtml"). And now I want to generate a partial view with the data downloaded in the json format inside the Stats function.
Controller
public IActionResult Details(int? id = 1)
{
var person = _context.Persons.Find(id);
return View(champion);
}
public IActionResult Stats()
{
var json = new WebClient().DownloadString("url");
return Json(s);
}
View - Details.cshtml
#model Person
<div class=row">
<div class="col-sm-5"> #Model.Name </div>
<div class="col-sm-5"> #Html.Partial("Stats") </div>
</div>
View - Stats.cshtml
<h2>Stats</h2>
<div> here I want to put in a json field </div>
When I run "Stats" function from the address localhost/Home/Stats I get the result in json, but when I run "Details" function I get view "Details" and "Stats" without the json value.
to render a partial, you have many options, by your code,
the simplest one is: move your Stats code to Details action
public ActionResult Details()
{
...//prepare your person viewModel
var result = new WebClient().DownloadString("url");
var stats = JsonConvert.DeserializeObject<YourViewModel>(result);
//you have 2 options to return data
yourPersonModel.Stats=stats ; //<== you have to change PersonViewModel
//or ViewBag.Stats=stats;
return View(yourPersonModel);
}
then in Details.cshtml:
#Html.Partial("Stats", ViewBag.Stats or Model.Stats)//by your choice before.
Since Html.Action is removed, but ViewComponent comes in Core, you cannot directly call it right now, but this link will tell you how to make it back: #Html.Action in Asp.Net Core
public ActionResult Stats()
{
var result = new WebClient().DownloadString("url");
var yourViewModel = JsonConvert.DeserializeObject<YourViewModel>(result);
return PartialView(yourViewModel);
}
add the following code in your View - Stats.cshtml:
#model YourViewModel
then in Details.cshtml:
#Html.Action("Stats")
Be aware that Html.Action cannot call async actions, be careful to use it.
the next solution is to use new feature ViewComponent, here is details:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-2.1
the last one will not be what you expected: use AJAX to load this partial page on page Details is loaded to client

AngularJs and ASP.NET MVC 5 - ng-model overriding textbox value

I have a form built using ASP.NET MVC 5 using #Html.TextBoxFor to populate the form with my model (e.g, after a form navigation or server side validation failure).
I have now introduced a client side address lookup using Angular which means some of the form fields are now decorated with ng-model to enable the Angular lookup to populate the searched address.
e.g.:
#Html.TextBoxFor(m => m.Town, new { #class="form-control", ng_model="address.town" })
Adding the ng-model now overrides the value set from the MVC model when the page is reloaded.
Viewing the source in the browser shows that the textbox value is set correctly to Town from the MVC model but Angular then comes along and populates it with address.town which is empty so the form displays no value.
How can I prevent Angular from doing this?
You can use ng-init to force a value from MVC
<input name="Town" type="text" ng-model="address.town" ng-init="address.town= #Model.Town" />
#Html.TextBoxFor(m => m.Town, new { ng_model="address.town", ng_init="address.town= Model.Town" })
Alternatively, I use a directive which I found here https://stackoverflow.com/a/22567485/2030565
app.directive('input', function ($parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, element, attrs) {
if (attrs.ngModel) {
val = attrs.value || element.text();
$parse(attrs.ngModel).assign(scope, val);
}
}
}; });
You could set a variable in a Javascript section at the bottom of the server template that your Angular controller assigns to the model on initialization.
<script>
My.Namespace.town = #Html.Raw(Model.Town);
</script>

MVC4 - AutoComplete using Json

I'm working on an intranet web app using ASP.NET MVC4 and Entity Framework. On one of my views, I have a list of persons which might be huge in the future. So, to make things easier, I wanted to implement an autocomplete field component using jQuery UI and Json.
The thing is that when I'm using my database to provide a source to my jQuery code, it is not working. However, when I create a variable by hard coding data, it works.
My Action :
public ActionResult AutoComplete(string term)
{
BuSIMaterial.Models.BuSIMaterialEntities db = new Models.BuSIMaterialEntities();
//var result = new [] {"A","B","C","D","E","F"}; with this, it works
var result = (from obj in db.Persons where obj.FirstName.ToLower().Contains(term.ToLower()) select obj).ToArray(); // with this, it doesn't work
return Json(result, JsonRequestBehavior.AllowGet);
}
My View :
#{
ViewBag.Title = "Auto";
}
<h2>Auto</h2>
<label for="persons">Persons : </label><input type="text" id="persons" name="persons"/>
#section Scripts
{
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
#Styles.Render("~/Content/themes/base/css")
<script type="text/javascript" language="javascript">
$(document).ready(function () {
$('#persons').autocomplete({
source: '#Url.Action("AutoComplete")'
});
});
</script>
}
I tried to modify my return type (JsonResult instead of ActionResult) but nothing changes. Any idea to solve this problem?
The reason your code doesn't work is because you are attempting to send a domain model to the view which most probably contains circular references in its object graph and is not JSON serializable. To fix the problem do this:
public ActionResult AutoComplete(string term)
{
BuSIMaterial.Models.BuSIMaterialEntities db = new Models.BuSIMaterialEntities();
//var result = new [] {"A","B","C","D","E","F"}; with this, it works
var result = db
.Persons
.Where(p => p.FirstName.ToLower().Contains(term.ToLower()))
.Select(p => p.FirstName) // <!-- That's the important bit you were missing
.ToList();
return Json(result, JsonRequestBehavior.AllowGet);
}
Notice how I am projecting the Person entity to a string (taking only its FirstName). In your example you were directly taking the entire Person object which doesn't make sense for the autocomplete plugin as you need to send an array of strings to the view.

How to pass an object from a mvc controller action to a view using jquery

Can someone help me out I'm new to jQuery, I would like to know how can I pass an object through from a controller action MVC
public ActionResult UserGroupSetting()
{
UserGroupSetting setting = //some code to retrieve the settings
ViewData["Theme"] = setting.Theme;
ViewData["Image"] = setting.LogoImage;
returnView(setting);
}
What I want its to get the Theme and the logo image, in a jQuery function, that I will use to update a class in a stylesheet, The theme object contains a colour in Hex form only for now.
If someone can please help me with the call to a jquery function and how will I expose both the image and the theme objects in jquery.. Thanks
You can return the data in jSon format and let jquery make a call to your action method with getJSON method
public ActionResult UserGroupSetting()
{
var result=new { Theme="YourTheme", Logo="YourLogoURL"};
return Json(result, JsonRequestBehavior.AllowGet);
}
In your page,Call this from your javascript
$(function(){
$.getJSON('YourController/UserGroupSetting', function(data) {
alert(data.Theme);
alert(data.Logo);
});
});
if i were you , i would do this:
you can use ViewBag: in action: ViewBag.Setting = setting;
UserGroupSetting setting = //some code to retrieve the settings
ViewBag.Theme = setting.Theme;
ViewData.Image = setting.LogoImage;
returnView(setting);
then in Razor:
#{
var setting = (UserGroupSetting)ViewBag.Setting;
}
output it to javascript tag:
<script type="text/javascript">
var setting = new setting{
Theme = #setting.Theme,
Image = #setting.Image
}
</script>
This may not be the best solution, but one option I've used in the past is to create a hidden input in your view and access the value of the input in your jQuery code. This would be an example of the code you would have in your view:
<input id="someID" type="hidden" value="#TempData["Theme"]" />

Categories

Resources