Javascript updates not posted, properties have default value on binding - javascript

I'm struggling with this issue since yesterday.
I have a simple ViewModel :
public class LoginViewModel
{
[Required]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
[Required]
public double Longitude { get; set; }
[Required]
public double Latitude { get; set; }
}
And this form :
<h2>#ViewBag.Title.</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(x => x.Latitude)
#Html.TextBoxFor(x => x.Longitude)
}
And this javascript that is updating the textbox with user's location.
<script type="text/javascript">
$(document).ready(function () {
navigator.geolocation.getCurrentPosition(getPosition);
});
function getPosition(position) {
document.getElementById("Latitude").value = position.coords.latitude;
document.getElementById("Longitude").value = position.coords.longitude;
}
<script>
And here is my action :
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
return View()
}
What really drives me crazy is that I can see that the textbox is properly filled with the coordinates in the browser, however nothing is received on the controller. All the properties : email, password, rememberbe etc... are correctly binded but these two. That's really weird.
Please I really need to solve that. Any help would be appreciated

Well I got it. It has nothing to do with javascript mechanisms, actually the problem was the delimiter for the number. By default javascript delimited the decimal part with a ".", I had to change it to a ",". I really hope it will save someone's else time.
I've never came accross this problem before, but I can't remember having posted double values in the past. We learn something new everyday.

Related

Using C# MVC multiple dynamic models in View

I have a View with several form that I'm using for searching and displaying the results as partial View in like SearchByNumber, SearchByVehicle, etc.
I'm trying to load view and execute search for different forms by posting link with querystring like www.example.com/Search?number=101010 from different view.
For the first form, SearchByNumber I only have one parameter, string number and i'm returning view with dynamic Model and its working like it should, but I only manage to make search for this form.
Here is my controller:
public ActionResult Index(string number)
{
return View(model: number);
}
and in the View I have:
<form id="searchbynumberform">
Search By Any Number:
<div class="input-group input-group-sm">
<input type="text" class="form-control" name="number" id="number" value="#Model">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" name="numbersearch" id="numbersearch" disabled>
Search
</button>
</span>
</div>
</form>
My Question is, if anyone can help me, How to perform search let's say on the second form where I have int type and string name parameters?
Thank You in advance...
At the moment your Model is only the search string that was entered, which seems rather incomplete. It would make a lot more sense if the Model also contained the actual search results, which after all is what the user wants to see. And then you can also add the other search properties.
The MVC approach for this is to create a (View)Model class, somewhere in your project, something like this:
public class SearchModel
{
public string Number { get; set; }
public int? Type { get; set; }
public string Name { get; set; }
public List<SearchResult> SearchResults { get; set; }
}
And then use it e.g. like this:
public ActionResult Index(string number)
{
var model = new SearchModel
{
Number = number,
SearchResults = GetByNumber(number)
};
return View(model);
}
public ActionResult IndexOther(int type, int name)
{
var model = new SearchModel
{
Type = type,
Name = name,
SearchResults = GetByTypeAndName(type, name)
};
return View(model);
}
And in your Index.cshtml:
#model SearchModel
#* You can now use Model.Number, Model.Type, Model.Name and Model.SearchResults. *#

Use javascript variable in c#

Is it possible to use js variables in c# code?
This is my model:
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual ICollection<Department> Parent { get; set; }
}
And this is my cshtml:
#model List<inspinia.models.Department>
<select name="DepartmentId" class="form-control" id="Department">
#foreach (var item in Model)
{
<option value="#item.Id">#item.Name</option>
}
</select>
<script>
$(document).ready(function () {
$("#Department").on("change", function () {
var DepartmentId = $('option:selected', this).attr('value');
//the problem in the next line.
#foreach(var item in Model.Where(x=>x.ParentId==#:DepartmentId))
{
#:alert(#item.id)
}
});
});
</script>
Well I am having error when I use
Model.Where(x=>x.ParentId==#:JAVASCRIPT VARIABLE).
Is there any way to do it or I definitely have to use ajax ?
No you can't, because the javascript code's is for clientside and razor in serverside.
You have two solutions:
1- Put all departement in list(<ul>) and use JQuery for get your data on change
2- Use jquery ajax and get data directly from controller(create controller for that)

Cascading Dropdown list MVC5 using fluent nhibernate

I have read for 2 days every single question that looks like my problem on here and read multiple pages and tutorials and even watched videos and i just can't understand it or make it work...
What im trying to do is that i have 2 dropdown lists, one is "Departamentos" which you can think of it as a state and "Municipios" which you can think of it as a county. What i need and no matter what i just CAN'T make it work is that when i select a departamento ONLY the municipios from that departamento show up on the dropdown list. Im really a complete noob regarding programming and unfortunately i think i started with something way too big for me, so i' sorry if this is a basic really easy thing to do for you.
The departamento class is :
public virtual int id_departamento { get; set; }
public virtual string descripcion { get; set; }
//i specify Relationship for fluent Nhibernate to municipios since it is a 1-n
public virtual IList<Municipios> Municipios { get; set; }
The municipio class is :
public virtual int id_municipio { get; set; }
public virtual string municipio { get; set; }
public virtual int id_departamento { get; set; }//THis is the FK from Departamento
And heres my main class Sitios where i connect everything to:
This is for the Nhibernate relationships
public virtual Departamentos Departamento { get; set; }
public virtual Municipios Municipios { get; set; }
This is for the lists on that same Sitios class:
public virtual List<Departamentos> Departamentos { get; set; }
public virtual List<Municipios> municipiosLista { get; set; }
Now going to MVC this is the controller i have for the Get create where i populate the lists of Departamento and Municipio to be shown:
using (ISession session = NhibernateHelper.OpenSession())
{
var deptos = session.Query<Departamentos>().ToList();
var munis = session.Query<Municipios>().ToList();
var instanciadelacopia=new Sitios
{
Departamentos = deptos,
municipiosLista = munis
};
return View(instanciadelacopia);
}
And the create view for that specific dropdown part:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Sitios</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<label class="control-label col-md-2"> Departamento </label>
<div class="col-md-10">
#Html.DropDownListFor(model => model.id_departamento, new SelectList(Model.Departamentos, "id_departamento", "descripcion"), "--Select--", new {#id = "depto"})
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2"> Municipio </label>
<div class="col-md-10">
#Html.DropDownListFor(model => model.id_municipio, new SelectList(Model.municipiosLista, "id_municipio", "municipio"), "--Select--", new { #id = "muni" })
</div>
</div>
Everything works fine since it brings me all the values for me to select from the db, where i'm stuck and can't advance is that i need a cascading dropdown for the municipios list that when i select certain departamento ONLY the municipios of THAT selected departamento show up on the list.
So for example i select departamento "atlantida" which it's ID is 1 then only the municipios which have that foreign key from Departamentos = 1 are shown which im guessing Jquery is required.
Would be really good if someone can help me with this, i feel so stressed since i just cant figure out what i need to do. Thanks
All examples i've seen are about using JSON like this one:
http://www.c-sharpcorner.com/uploadfile/4d9083/creating-simple-cascading-dropdownlist-in-mvc-4-using-razor/
but since i already have all the data available on the dropdowns i think i don't need that and instead just a plain jquery function which i cant create.
Solved it with this:
<script type="text/javascript">//Script for Cascading Dropdown
$(function () {
$('#id_departamento').change(function() {
var departmentId = $(this).val() || 0;
$.ajax({
url: '/Sitios/Municipios/',
type: 'POST',
data: { id_departamento: departmentId }, // parametro
dataType: 'json',
success: function (data) {
var options = $('#id_municipio');
$('option', options).remove(); //
options.append($('<option />').val('').text('---'));
$.each(data, function () {
options.append($('<option />').val(this.id).text(this.name));
});
}
});
});
});

How to get collection of IDs, and Selected Dropdown values passed from View's table to Controller?

So I have a checklist/task manager kind of application that basically pulls a bunch of string values from a database using a model, and then pipes it out to a view which uses a expand/collapse patterned table. The only element that an end user modifiers beyond the expand/collapse state is the dopdown list for the project status.
Rather than an update button for each row, the customer wants basically a single button on the bottom of the page which updates all the rows the user changed. So my goal is to try and create said button. As far as the View code snippet, I've tried messing around with Html.BeginForm, but haven't been able to figure out how to also capture the multiple item IDs as well:
#using (Html.BeginForm("UpdateDB", "Checklist", new { id = model.ID })) // model does not exist in this current context
{
<table id="checklistGrid" class="table table-bordered table-striped grid">
<tr>
<th>#Html.DisplayNameFor(model => model.ww)</th>
.... // more table headers
</tr>
#foreach (var group in Model.GroupBy(x => x.ww))
{
<tr class="group-header">
<td colspan="12">
<span class="h2">#group.Key</span>
</td>
</tr>
foreach (var dept in group.GroupBy(x => x.dept))
{
<tr class="dept-header">
<td colspan="12">
<span class="h4">#dept.Key</span>
</td>
</tr>
foreach (var item in dept)
{
<tr class="row-header">
<td> #Html.HiddenFor(modelItem => item.ID)</td>
<td>#Html.DisplayFor(modelItem => item.ww)</td>
.... // more table columns
<td>
#{
List<string> ddl = new List<string> { "Done", "Not Done", "N/A" };
SelectList sl = new SelectList(ddl, item.status);
#Html.DropDownList("newStatus", sl); // Added
}
</td>
<td>#Html.DisplayFor(modelItem => item.applied_by)</td>
<td>
#{
DateTime tmp;
//Check if not null, if not null convert to specified time zone
if (DateTime.TryParse(item.timestamp.ToString(), out tmp))
{
tmp = item.timestamp ?? DateTime.MinValue;
string zoneName = "India Standard Time";
var abbrZoneName = TimeZoneNames.TimeZoneNames.GetAbbreviationsForTimeZone(zoneName, "en-US");
TimeZoneInfo zoneInfo = TimeZoneInfo.FindSystemTimeZoneById(zoneName);
DateTime istTime = TimeZoneInfo.ConvertTimeFromUtc(tmp, zoneInfo);
#Html.Raw(istTime.ToString() + " (" + abbrZoneName.Generic + ")");
}
else
{
#Html.DisplayFor(modelItem => item.timestamp);
}
}
</td>
</tr>
}
}
}
</table>
<p><input type="submit" value="Save Changes" /></p>
}
<script type="text/javascript">
// Hide/Show Dept's Data on Click
$(function () {
$('.dept-header').click(function () {
$(this).nextUntil('.dept-header, .group-header').toggle();
});
});
$(function () {
$('.group-header').click(function () {
var elm = $(this);
if (elm.next().is(":visible")) {
elm.nextUntil('.group-header').hide();
} else {
elm.nextUntil('.group-header', '.dept-header').show();
}
});
});
</script>
I suspect the easier (and computationally quicker way) would to be to use Jquery + Ajax && Json attached to a button onclick at the bottom to send the collection of id and selected dropdown text to an ActionResult in the Controller (to then update the database) and then refresh on the success Ajax callback. However my Jquery/Javascript foo is weak and given the clickable 'header' type rows that expand/collapse the data rows, it's not clear to me how I would efficiently use Jquery to navigate and grab the item.IDand the selected dropdown text from each row, bundle it up, and pipe to the desired ActionResult.
So how do I send both the id and selected dropdown text of all the rows to an arbitrary ActionResult be it with Html.BeginForm or Jquery et al?
EDIT: Model for reference:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace TaskTracker.Models
{
[Table("some_update")]
public class TaskInfo
{
public int ID { get; set; }
[Display(Name = "WW")]
public int ww { get; set; }
[Display(Name = "File Name")]
public string filename { get; set; }
[Display(Name = "Dept")]
public string hsd_unit { get; set; }
[Display(Name = "Owner")]
public string owner { get; set; }
[Display(Name = "Manager")]
public string manager { get; set; }
[Display(Name = "Project")]
public string project { get; set; }
[Display(Name = "Status")]
public string status { get; set; }
[Display(Name = "Last Applied By")]
public string applied_by { get; set; }
[Display(Name = "Last Updated Time Stamp")]
public DateTime? timestamp { get; set; }
}
public class TaskInfoDBContext : DbContext
{
public DbSet<TaskInfo> TaskInfoSet { get; set; }
}
}
You have a number of problems with you view including your form controls have duplicate name attributes meaning you cannot bind to you model when you submit, duplicate id attributes which is invalid html, and your creating a dropdownist with a name that is not even a property of your model. In addition your use of queries in the view code is not good practice. All of this can be solved by using view models and generating your html correctly using for loops (not foreach loops) or a custom EditorTemplate. However, since you have indicated a preference for ajax, you can post the data by making the following changes to the view
Replace the following HtmlHelper methods
#Html.HiddenFor(modelItem => item.ID)
#Html.DropDownList("newStatus", sl)
with
#Html.HiddenFor(modelItem => item.ID, new { id = "", #class = "id" })
#Html.DropDownList("Status", sl, new { id = "", #class = "status" })
This will remove the invalid id attributes and add class names for selection. Then replace the submit button with
<button type="button" id="save">Save Changes</button>
And add the following script
var rows = $('.row-header'); // store all the table rows containing the form controls
$('#save').click(function() {
// Build an array of the values to post back
var data = [];
$.each(rows, function() {
data.push({id: $(this).find('.id').val(), status: $(this).find('.status').val() });
});
$.ajax({
url: '#Url.Action("UpdateDB", "Checklist")',
type: "POST",
data: JSON.stringify({ model: data },
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
// do something?
}
})
});
Then create a model to accept the values
public class TaskInfoUpdate
{
public int ID { get; set; }
public string Status { get; set; }
}
And modify you POST method to
[HttpPost]
public ActionResult UpdateDB(IEnumerable<TaskInfoUpdate> model)
Side note: Its not clear what you mean by "and then refresh on the success Ajax callback" (what is there to refresh?). I suggest that the method return either return Json(true); if the items were successfully saved, or return Json(null); (or a HttpStatusCodeResult) if not so that you can notify the user as appropriate.

How to get checkbox selected value and pass to controller in asp.net mvc 4

I am trying to get the selected checkboxes value
this are my models,
public class VehicleViewModel : Vehicle
{
[Display(Name = "Vehicle Type")]
[Required( ErrorMessage = "{0} is required.")]
public string VehicleTypeName { get; set; }
[Display(Name = "Location")]
[Required(ErrorMessage = "{0} is required.")]
public string LocationName { get; set; }
public IEnumerable<AssignProductsViewModel> AssignedProducts { get; set; }
}
public class AssignProductsViewModel
{
public long ProductID { get; set; }
public string ProductName { get; set; }
}
here's my razor view
#foreach (var item in Model.AssignedProducts)
{
<tr>
<td>
<input type="checkbox" value ="#item.ProductID"/>
</td>
<td>
#Html.DisplayFor(model => item.ProductName)
</td>
</tr>
}
and here's my controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult NewVehicle(VehicleViewModel vehicleViewModel, string selected)
{
//Do something with the string here
return View();
}
I know I need to pass the selected check boxes value to a string using javascript and pass the string to a controller. But i have no idea how to do it since I'm a newbie in javascript and MVC.
Based on the comments that an ajax post is not required, your AssignProductsViewModel requires an additional property to bind the checkboxes
public class AssignProductsViewModel
{
public long ProductID { get; set; }
public string ProductName { get; set; }
public bool IsSelected { get; set; } // add this
}
In the view, use a for loop or a custom EditorTemplate to render the collection do the controls are correctly named with indexers. A foreach loop generates duplicate id (invalid html) and name attributes (cannot be bound to a collection)
#model VehicleViewModel
#using(Html.BeginForm())
{
// controls for VehicleTypeName, LocationName
for(int i = 0; i < Model.AssignedProducts.Count; i++)
{
#Html.HiddenFor(m => m.AssignedProducts[i].ProductID) // ditto for ProductName if you want it on postback
#Html.CheckBoxFor(m => m.AssignedProducts[i].IsSelected)
#Html.LabelFor(m => m.AssignedProducts[i].IsSelected, Model.AssignedProducts[i].ProductName)
}
....
}
and post back to
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult NewVehicle(VehicleViewModel model)
{
// model.AssignedProducts contains the collection with a value indicating if the product has been selected
}
Alternatively, you can use an EditorTemplate for type of AssignProductsViewModel to render the collection
In /Views/Shared/EditorTemplates/AssignProductsViewModel.cshtml
#model AssignProductsViewModel
#Html.HiddenFor(m => m.ProductID) // ditto for ProductName if you want it on postback
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m..IsSelected, Model.ProductName)
and in the main view
#model VehicleViewModel
#using(Html.BeginForm())
{
// controls for VehicleTypeName, LocationName
#Html.EditorFor(m => m.AssignedProducts)
<input type="submit" />
}

Categories