Edit view - search form - c#

I have some basic CRUD views for a model:
public class Task
{
public Task()
{
this.Users = new List<ApplicationUser>();
}
public string Id { get; set; }
public string Name { get; set; }
public DateTime DueDate { get; set; }
public virtual IList<ApplicationUser> Users { get; set; }
}
Now, when I edit a Task, I want to have a textbox where
you can enter the username
click search
return any results that matches the criteria
select one or more users from results // this is bound to Users property
save the task
All this done within the edit view.
Everything works fine CRUD, except I don't know how to implement the search withing the edit view.
P.S. I have a feeling I need some Ajax forms with some partial view ...

The general idea behind this solution is to request user to enter username they are searching for and then sending a request to the server for retrieving all users with that username. Once a response is received, then we dynamically populate a select with the results received from the server and make user select a username.
Note: I don't recommend you to use this solution in its current state as I always discourage mixing Javascript code with your views. I strongly recommend to move your Javascript code in a different file!
UsernameModel.cs
public class UsernameModel
{
[Required]
public string Username { get; set; }
}
UserController.cs
using Newtonsoft.Json;
public class UserController : Controller
{
...
[HttpPost]
public ActionResult Get(UsernameModel model)
{
// user service has code to search for users
// whose username equals to or is similar to model.Username
var users = userService.GetUsersByUsername(model.Username);
var response = new
{
Users = users
};
return Content(JsonConvert.Serialize(response), "application/json");
}
...
}
View:
<!-- HTML Markup to edit different task properties -->
<input type="hidden" id="UserSearchUrl" value="#Html.Action("Get", "User")" />
<label for="Username">Username:</label>
<input type="text" name="Username" id="Username" />
<button id="SearchUsername" type="button">Search</button>
<select id="UserSelect"></select>
<script type="text/javascript">
$(function () {
$('#SearchUsername').on('click', function () {
$.ajax({
type: "POST",
url: $("#UserSearchUrl").val(),
contentType: "application/json",
data: { Username: $('#Username').val() },
dataType: "json",
success: function (data, textStatus, jqXHR) {
for (var i = 0; i < data.Users.length; i++) {
var option = $('<option>').attr('val', data.Users[i]);
option.text(data.Users[i]);
$('#UserSelect').append(option);
}
},
error: function (jqXHR, textStatus, errorThrown) {
// enter error handling code here
}
});
});
});
</script>
This is just 1 possible way of searching for users. It uses Javascript DOM Manipulation and Ajax. There are several other ways of achieving this other than this one but all will involve some sort of Javascript.
Another possible way of doing this is to loading all users at the same time as loading task information on the edit page. Then search for users using Javascript within client's browser, thus removing a need for sending request to the server. I can give an example of this solution if you would like.

Related

[dotNET Core]Passing model to view -> bind checkbox with it's data in POST method form -> sending whole model to controller

I need some help. I am trying to pass ShowSeatsViewModel to Seats.cshtml bind it with passed view model's field and send it back with new values to controller.
ViewModel :
using System.Collections.Generic;
namespace theatre_dotNET.Models
{
public class SpectacleShowsViewModel : Spectacle
{
public List<Show> IncomingShows { get; set; }
public Show pickedShow { get; set; }
public int[] availableSeats { get; set; }
public int[] bookedSeats { get; set; }
public SpectacleShowsViewModel(Spectacle s, List<Show> incomingShows)
{
this.SpectacleId = s.SpectacleId;
this.Title = s.Title;
this.Description = s.Description;
this.Price = s.Price;
this.VideoLink = s.VideoLink;
this.Rating = s.Rating;
IncomingShows = incomingShows;
}
}
}
Two methods of controller :
[Authorize]
public IActionResult Seats(Show chosenShow)
{
int[] availableSeats = _context.Seats.Select(s => s.SeatId).ToArray();
int[] bookedSeats = _context.BookedSeats.Where(i => i.ShowId == chosenShow.ShowId).Select(s => s.SeatId).ToArray();
return View(new ShowSeatsViewModel(chosenShow, availableSeats, bookedSeats));
}
[Authorize]
[HttpPost]
public IActionResult Seats(ShowSeatsViewModel chosenShowSeatsViewModel)
{
return Content("Picked seats : "+chosenShowSeatsViewModel.PickedSeats);
}
View:
#model ShowSeatsViewModel
<h1>Chosen Spectacle : Model.SpectacleId</h1>
<form asp-controller="Booking" asp-action="Seats" method="post">
<div class="row">
#foreach(var seat in Model.AvailableSeats)
{
#if(Model.UnavailableSeats.Contains(#seat))
{
<div class="col">
#Html.CheckBoxFor(m=>m.PickedSeats[#seat-1])
#Html.LabelFor(m=>m.PickedSeats[#seat-1],#seat.ToString())
</div>
}
else
{
<div class="col">
#Html.CheckBoxFor(mdl => mdl.PickedSeats[#seat-1])
#Html.LabelFor(mdl => mdl.PickedSeats[#seat-1],#seat.ToString())
</div>
}
}
</div>
<button type="submit">Submit</button>
</form>
And an error - after choosing checkboxes and clicking submit button:
InvalidOperationException: Could not create an instance of type 'theatre_dotNET.Models.ShowSeatsViewModel'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the 'chosenShowSeatsViewModel' parameter a non-null default value.
How could I send existing(passed from Controller) ViewModel via post method? Is it possible?
EDIT: I'm updating my answer.
In your case, you can't model bind to that Object without custom model binding.
It's a complex object that doesn't have a parameterless constructor.
You need to have all of the properties in the form to pass the entire model with. As you stated that's not what you want.
In your case a custom model binder is a complex implementation that you would have to do over and over throughout the app. I wouldn't recommend this approach.
On the client side I would use a library like JQUERY to perform the post for complex objects.
Here is an example.
Create a JS script to run these POST requests with complex objects with the following function.
Here is an example with JQuery
function ajaxRequest(httpVerb, url, model, onSuccess, onFail, onComplete) {
if (httpVerb === null || httpVerb.length === 0) httpVerb = "POST";
$.ajax({
url: url,
type: httpVerb,
cache: false,
traditional: true,
data: JSON.stringify(model),
dataType: "json",
contentType: 'application/json; charset=utf-8'
}).done(function (data) {
//onSuccess()...
}).fail(function (err) {
//onFail()...
}).always(function (data) {
//onComplete()...
});
}
Here is how you would call it
var model = #Html.Raw(Json.Encode(Model)); //this gets your view model and converts it to a JS object.
//Or you can take the time and create it manually
var model = {...}
//take the form values and replace the model properties as needed
// make sure to give your HTML elements in the forms ID attributes so you can select them.
model.PickedSeats = $('#myCheckboxById').is(':checked');
...
// do the same with the other form values
ajaxRequest("POST", "/Booking/Seats", model , null, null, null);
//the null values being passed in are the callback functions you would perform in the method signature. I just left them null to simplify things.

Get selected value from HTML control

I am new to the web side of things and I am currently struggling with Razor Pages. Can someone explain the ways I can get a value from control in this case.
How can I extract the content of the selected and pass it to a variable to the code behind;
#page
#model ViewToVM.Pages.IndexModel
#{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<section id="cityList">
<select id="selectedCity">
#using Model;
#foreach(City city in Model.Cities)
{
<option>#city.SelectedCity</option>
}
</select>
</section>
with this code behind
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using ViewToVM.Model;
namespace ViewToVM.Pages
{
public class IndexModel : PageModel
{
public List<City> Cities = new List<City>()
{
new City("Sofia"),
new City("Plovdiv"),
new City("Velingrad")
};
public string selectedCities = string.Empty;
public void OnGet()
{
}
}
}
The City class just contains a single string for demo purposes. I know this is probably a pretty bad way to do the code behind but It help me illustrate the problem better.
you can use Javascript/Jquery and add an onchange event listener that can make a Ajax call and pass it's value to the controller. similar to the code below:
<script type="text/javascript">
// assuming you're using jQuery
$("#selectedCity").change( function (event) {
$.ajax({
url: "Controller/PostDropdown/",
data: { id = $(this).val() },
type: "POST",
dataType: "html",
success: function (data, textStatus, XMLHttpRequest) {
// do something
}
});
});
You should wrap select with form. When form was submitted, it will call your controller.
see this:
Submitting form and pass data to controller method of type FileStreamResult

Update a partial view with a string from another partial view on submit?

I am new to MVC and Web Api, and getting stuck.
Please Note: I am using ApiControllers, not Controllers!
Situation
I have two partial views. The first has drop down lists and a submit button. The second is just a confirmation view showing what the user selected after user submits. On clicking submit, I want to send a string (containing the user's combined selections) to the second partial view and update it.
Question
When I press submit on the first partial view, I want to send a string to the second partial view and update the second partial view. How can I do this? The sending of the string can be indirect, of course, like view to controller then back to view. I just want to know at least one way.
Again, note that I am using Web Api Controllers, NOT Controllers. Most people use normal Controllers, which doesn't apply to me. They are different.
Things I tried / What I believe I cannot do from other StackOverflow answers:
I cannot use a Controller method to return a Partial View, like in this very similarly asked question Same question but not with ApiControllers.
I cannot store any data into a class, so I cannot strongly type a view with #model ExampleClass to access stored contents with #Model.ExampleProperty.
Since I cannot store data into a class or return a model from a controller method like View(model), I also could not use Html.DropDownListFor(model => model.SelectedValue) to store the SelectedValue into my model and access it in the other partial view. Instead to populate my drop down lists, I manually added a div, called a GET method that returned a list, and populated the lists by appending to them.
So, what exactly CAN I do? I also tried ViewData, but the stored data would not last after a Controller method finished. I am out of ideas. I hope I am mistaken about something and missed a method, because it feels it should not be this hard. If you could even briefly describe the flow of how I would transfer a string and/or update the second partial view after pressing submit, that would be greatly appreciated!
Thanks so much in advance!
Model class
public class Vehicle
{
public class Make
{
public int MakeId { get; set; }
public string MakeName { get; set; }
}
public class Model
{
public int ModelId { get; set; }
public string ModelName { get; set; }
}
}
ApiController class. The ViewData seems to not save its contents. I wanted to store the user's selections into ViewData with the first POST method, and GET the selections with the second method, but the contents of ViewData have become null already.
public class VehicleController : ApiController
{
public ViewDataDictionary ViewData = new ViewDataDictionary();
[HttpPost]
public void StoreMakeAndModel(string user_selections){
ViewData["message"] = user_selections;
}
[HttpGet]
public string ConfirmationMessage(){
ViewData["message"] = "You selected the " + ViewData["message"].ToString();
return ViewData["message"].ToString();
}
//This returns a list to populate a drop down list
[HttpGet]
public IEnumerable<Vehicle.Make> Makes(){
//This function reads from xml and returns List<Vehicle.Make>
...
return makesList;
}
//This returns a list to populate a drop down list
[HttpGet]
public IEnumerable<Vehicle.Model> Models(int id){
//This function reads from xml the car models that match the make id
...
return modelsList;
}
}
First partial view It has 2 drop down lists and submit button. There is a hidden div that shows once the form submits that renders the second partial view. I hoped that it would load only when it is shown so that I can use $(document).ready(function()) to display my string there, but it loads when the main page loads even though it's hidden. So I don't know how to update it with the string after the first partial view submits.
<select required id="makes_DDL" name="makes_DDL"></select>
<select required id="models_DDL" name="models_DDL"></select>
<input type="submit" id="submit_button" value="Submit Form" onclick="formSubmit()" />
<div id="thank_you_div" style="display:none;">
#Html.Partial("_ThankYou");
</div>
function formSubmit() {
//Combines the make and model into one string, Ex: Chevrolet Malibu
var parameter = $("#makes_DDL").children(":selected").text() + " "
+ $("#models_DDL").children(":selected").text();
var uri = "api/vehicle/storemakeandmodel/" + parameter;
$.ajax({
url: uri,
type: "post",
updatetargetid: "thank_you_div",
success: function (data) {
$("#thank_you_div").show();
},
});
}
My list population looks like this; this is my first list. I could not get #Html.DropDownListFor to work because I could not store anything since I cannot return something like View(model) from a controller method.
$(document).ready(function () {
$.getJSON("api/vehicle/makes", function (makes) {
$("#makes_DDL").append("<option value= ''>Select a make</option>");
$.each(makes, function (id, make) {
$("#makes_DDL").append("<option value=" + make.MakeId + ">" + make.MakeName + "</option>");
});
});
});
Second partial view It tries to get the stored message in ViewData, but it is already gone and GETS null.
<div id="thank_you_div">Thank you!</div>
<script type="text/javascript">
$(document).ready(function () {
alert("Thank You Partial View ready!");
$.ajax({
url: "api/vehicle/confirmationmessage",
type: "get",
updatetargetid: "thank_you_div",
success: function (message) {
$("#thank_you_div").html(message)
}
});
})
</script>
Take a look at your ApiController action methods.
[HttpPost]
public void StoreMakeAndModel(string user_selections)
{
ViewData["message"] = user_selections;
}
[HttpGet]
public string ConfirmationMessage()
{
ViewData["message"] = "You selected the " + ViewData["message"].ToString();
return ViewData["message"].ToString();
}
Looks like you are trying to store data to ViewData when the first method is called and read it in the second method when you make your second ajax call. But that will not work! Because http is stateless. Your second call has no idea what your first call did. You cannot store data in ViewData to access between multiple http requests. It does not make sense to use ViewData in an api controller at all. Api controller endpoints should be purely to return data.
In your case, what you should be doing is, simply have your first method return the string message you want. In the success handler of the first ajax call,you will get this string (Response from the api method) in the success call back. You can use this to update the DOM as needed.
There is no need to have the partial view as well. All you need is a container div element to show the response.
<select required id="makes_DDL" name="makes_DDL"></select>
<select required id="models_DDL" name="models_DDL"></select>
<input type="submit" id="submit_button" value="Submit Form" onclick="formSubmit()" />
<div id="thank_you_div" style="display:none;"></div>
Now in your first ajax call's success callback, update the DOM with the response coming back.
Also $.ajax does not have a setting property called updatetargetid !
function formSubmit() {
//Combines the make and model into one string, Ex: Chevrolet Malibu
var parameter = $("#makes_DDL").children(":selected").text() + " "
+ $("#models_DDL").children(":selected").text();
var uri = "api/vehicle/?user_selections=" + parameter;
$.ajax({
url: uri,
type: "post"
}).done(function(res) {
$("#thank_you_div").html(res).show();
}).fail(function(x, v, e) {
console.log(e);
});
}
Assuming your api controller method returns the string you want
[HttpPost]
public string StoreMakeAndModel(string user_selections)
{
return "You selected the "+user_selections;
}

.NET MVC jQuery AJAX - JSON object as parameter not serializing values

I'm trying to figure out how to simply pass a JSON object into a controller method param. I'll often use JSON.serialize() when posting forms and the serialization to C# is automatic. However, I'm struggling to get this to work using GET while trying to load a partial.
I built a small sample project as a basic simulation of what I need, which is a controller method that accepts an int param and a separate model class, which I'd like to pass in as JSON. The partial html response is loaded into a div.
The view:
<div class="row">
Loading a partial via Ajax, passing a JSON object as a param!
</div>
<div class="row">
<div id="peoplePartial"></div>
</div>
#section Scripts {
<script>
$(function() {
var id = 1;
var person = {
Name: "Homer Simpson",
Age: 45
};
load(id, person);
});
function load(id, person) {
$.ajax({
cache: false,
type: "GET",
dataType: "html",
url: '/Home/PeoplePartial',
data: {
id: id,
person: JSON.stringify(person)
}
}).done(function(data) {
$("#peoplePartial").html(data);
}).fail(function(data) {
console.log(data.responseText);
});
}
</script>
}
The partial:
#model MVCLearning.Models.PeoplePartialModel
<div>
<span>Person found! Name is #Model.Person.Name</span>
</div>
The partial ViewModel:
public class PeoplePartialModel {
public int Id { get; set; }
public PersonModel Person { get; set; }
}
The data model/entity I'm trying to serialize:
public class PersonModel {
public string Name { get; set; }
public int Age { get; set; }
}
And finally, the controller:
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
public ActionResult PeoplePartial(int id, PersonModel person) {
if (person != null) {
var model = new PeoplePartialModel {
Id = id,
Person = person
};
return PartialView("_People", model);
} else {
throw new System.Exception("An Error Has occoured");
}
}
}
The "id" param always works, of course, but the PeopleModel param is always null when using JSON.stringify(). I would have expected it to serialize to the model and contain the JSON values I passed in from the JS in the view. I can fix this by not using JSON.stringify() but then the values are always ignored e.g. always passed in as Name: null, Age: 0. I just can't seem to pass values like this.
I've tried changing the ajax call to POST but it changes nothing. I've tried setting the contentType to html and json, neither change anything.
Thanks in advance.
I got it. Here's the magic combination of properties:
function load(id, person) {
$.ajax({
type: "POST",
dataType: "html",
url: "/Home/PeoplePartial",
data: {
id: id,
person: person
}
}).done(function(data) {
$("#peoplePartial").html(data);
}).fail(function(data) {
console.log(data.responseText);
});
}
I think one of the variations I had tried was POST with the contentType set. That throws an error saying both controller method params are null, strangely. I also could have omitted dataType on this and it works. I could not get any variation of GET to work, even though GET is what I'm trying to do. I'd really like to know why this is the case.

Populate DropDownList from Azure Database in MVC

I am still new to MVC and working my way around it. I need to get the "name" column of my district table into a dropdownlist to be able to pick from different Districts. The end game is that the user will pick a District from the dropdownlist and then be directed to a page where a list of schools(in a different table) will be shown with the selected district (i think that would be a query on the database using the value given from the dropdownlist). Basically what I have done so far is:
Create an MVC Application.
Create a Entity Framework Model.
Create an empty controller.
Create a view model(since every tutorial/ site answer has said to do
so)
Create a view.
I replicate step by step what these tutorials are telling me to do, but I get a different result. My dropdownlist gives me this outcome:
I need help sorting out what could be going wrong and why the data is not showing up in my dropdownlist.
Try I believe you are using the wrong SelectList constructor. Assuming you want the value of the drop down list to be the "leaID" property
#Html.DropDownList("myList", new SelectList(ViewBag.districts, "leaId", "name")
I would however, approach it another way which will keep it mostly strongly typed:
public class DistrictViewModel
{
public string SelectedDistrictId { get; set; }
public IEnumerable<SelectListItem> Districts { get; set; }
}
Action:
public ActionResult Index()
{
var viewModel = new DistrictViewModel()
{
Districts = new SelectList(db.Districts.ToList(), "leaID", "name")
}
return View(viewModel);
}
cshtml:
#model DistrictViewModel
#Html.DropDownListFor(m => m.SelectedDistrictId, Model.Districts)
Here is my answer to your comment using ajax
//Model
public class DistrictViewModel
{
public string name {get;set;}
public SelectList District {get;set;}
public int SelectedDistrict {get;set}
}
//Controller
public class DistrictController : Controller
{
KUDEREntities db = new KUDEREntities();
public ActionResult Index()
{
var model = new DistrictViewModel();
model.Districts = db.Districts.ToList();
model.SelectedDistrict=0;
return view(model);
}
[HttpPost]
public ActionResult Search(int id)
{
//do the search with the id of the selected district
var data = db.Districts.Where(m=>m.leaId = id).FirstorDefault();//this would return the whole object.
return Json(data, JsonRequestBehavior.AllowGet);
}
}
//Index View
#Model DistrictViewModel
<div>
//with this your selector would be "#SelectedDistrict"
#Html.DropDownListFor(model=>model.SelectedDistrict, new SelectList(Model.Districts,"leaId","name",Model.SelectedDistrict), new {#class=""})
//with this your selector would be "#myList"
//#Html.DropDownList("myList", new SelectList(ViewBag.districts, "leaId", "name", Model.SelectedDistrict)
</div>
<script>
$(document).ready(function(){
//when you want to search for the id selected, you just need to call the Search function below
function Search() {
//... now you have the value of the selecteditem
var parameters = { id: $("#SelectedDistrict").val() };
$.ajax({
type: 'post',
url: '#Url.Action("Search", "District")',
cache: false,
contentType: "application/json; charset=utf-8",
dataType: "html",
async: true,
data: JSON.stringify(parameters),
success: function (data) {
//...do whatever with the data
},
failure: function (msg) {
//... show a message of error
}
});
}
});
</script>

Categories

Resources