I have this view. It's ok besides that I want to add: onclick="javascript:SubmitClick(#Model.id, answer.Id);" to definition of RadioButtons in it.
#model WebApplication2.Models.Question
<div>
#Html.HiddenFor(x => x.Id)
<h3> #Model.QuestionText </h3>
#foreach (var answer in Model.Answers) {
<p>
#Html.RadioButtonFor(b => b.SelectedAnswer, answer.Id) #answer.AnswerText
</p>
}
</div>
Question and Answer are models which look like that.
public class Question {
public int Id { set; get; }
public string QuestionText { set; get; }
public virtual ICollection<Answer> Answers { set; get; }
public string SelectedAnswer { set; get; } //this field is SET after clicking the radio button, I don't need this field ant this behaviour
}
public class Answer {
public int Id { set; get; }
public string AnswerText { set; get; }
}
As far as I know b => b.SelectedAnswer this expression inside #Html.RadioButtonFor makes that after click the field SelectedAnswer in class Question will be set. I don't need that I only need that only one of those radio buttons inside foreach loop can be selected at the same time and clicking on them invokes onclick="javascript:SubmitClick(#Model.id, answer.Id);". Also it can be done in obsolete way, just to make it work.
Screen which will help undestand how the view looks:
The view:
#model WebApplication2.Models.Question
<div>
#Html.HiddenFor(x => x.Id)
<h3> #Model.QuestionText </h3>
<form action="">
#foreach (var answer in Model.Answers) {
<p>
<input type="radio" onclick="javascript:SubmitClick(#Model.Id, #answer.Id);">#answer.AnswerText<br>
</p>
}
</form>
</div>
The script function is in strongly typed view against Person:
function SubmitClick(qid, aid) {
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { questionId: qid, answerId: aid, personId:#Model.Id }, function (data) {
alert('questionId: ' + qid + ' answerId ' + aid);
});
}
Method invoked by script
public void SubmitSurvey(int questionId, int answerId, int personId) {
System.Diagnostics.Debug.WriteLine("UPDATING DATABASE " + "Selected personId: " + personId);
}
Related
I am working on an ASP.NET core project. I am using the same javascript function to add and edit for multiple modal forms. Each modify"obj" modal form has it's own data-target, so the correct modal will open. What I'm trying to do is change the title to Add "obj" or Edit "obj", depending on if the user is opening the Add or Edit Modal, but I can't seem to get it right.
Update I've added the viewModels. There are two other objects that use this modal. They have their own modal partial view and their own target id's, which are #modifyClient, #modifyEmployee, and #modifyEvent. But they all use the Modify Object js function.
Event Model
public class Event
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string EventName { get; set; }
public string Type { get; set; }
[Required]
public string Location { get; set; }
public DateTime EventDate { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public bool Food { get; set; }
public bool Bar { get; set; }
public int Guests { get; set; }
public string? Notes { get; set; }
[ForeignKey("Client")]
public int ClientId { get; set; }
public int? CreatedBy { get; set; }
public DateTime? CreatedOn { get; set; }
public DateTime LastUpdate { get; set; } = DateTime.Now;
EventViewModel
public class EventViewModel
{
public Event Event { get; set; }
[Required]
public string ClientName { get; set; }
}
EventListDetails
public class EventListDetails
{
public int EventId { get; set; }
public string EventName { get; set; }
public DateTime EventDate { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string Type { get; set; }
public int ClientId { get; set; }
public string ContactName { get; set; }
public string Phone { get; set; }
}
This is the shared function for all of my modify modals in site.js
$(function () {
var placeholderElement = $('#PlaceHolderHere');
console.log(placeholderElement);
var id;
var controller;
var action;
var targetModal; //stores the id of correct modal
//onclick from index to open modal
$('button[data-bs-toggle="ajax-modal"]').click(function (e) {
target = e.target;
controller = $(target).data('controller');
action = $(target).data('action');
targetModal = $(target).data('target');
//checks data-modify to determine if form is Add or Edit
var addOrEdit = $(target).data('modify-type');
if (addOrEdit == 'Edit')
{
id = $("#selectedId").val();
/*document.querySelector('#modifyLabel').innerHTML = "Edit Event";*/
/*above commented out code causes "Uncaught TypeError: Cannot set properties of
null, replacing querySelectior with getElementById causes the same error
here*/
if (id == null || id == "") {
alert("Please Select a Record to Edit");
return;
}
}
else
{
id = 0;
}
var url = "/" + controller + "/" + action + "/" + id;
console.log(url);
$.get(url).done(function (data) {
//cannot find the code to attempt updating the data for title here
placeholderElement.html(data);
placeholderElement.find('.modal').modal('show');
});
/*this function is never reached, Also not sure if targetModel can be passed this way*/
$(targetModal).on('show.bs.modal', function (event) {
//if this works addOrEdit would need to be checked again before change
$(this).find('#modifyLabel').text("Edit Event");
});
//this version also never reached
//$('.modal').on('show.bs.modal', function (event) {
// if (addOrEdit == 'Edit') {
// $(this).find('h4.modal-title').text("Edit Event");
// }
//});
})
//onclick to save modal form information
placeholderElement.on('click', '[data-bs-save="modal"]', function (event) {
event.preventDefault();
//get form and form action and serialize (for Employee, Client, and Event _Modify""ModalPartials)
var form = $(this).parents('.modal').find('form');
var actionUrl = form.attr('action');
var sendViewModel = form.serialize();
alert(sendViewModel);
//close modal if all fields are valid
$.post(actionUrl, sendViewModel).done(function (data) {
if (data === true) {
placeholderElement.find('.modal').modal('hide');
location.reload();
return;
}
//replace modal with modelState errors if fields are not valid
var newBody = $('.modal-body', data);
placeholderElement.find('.modal-body').replaceWith(newBody);
});
});
});
code for modal in Event/_ModifyEventModalPartial.cshtml
#model Project.ViewModels.EventViewModel
<div class="modal modal-fade" id="modifyEvent">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="modifyLabel">Add Event</h4>
<button type=button class="close" data-bs-dismiss="modal">
<span>x</span>
</button>
</div>
<div class="row">
<p class="text text-danger" id="eventDateEditNotice">
<!--html here, is also only for Edit, and needs to be added dynamically-->
</p>
</div>
<form action="Event/Modify" method="Post" runat="server" autocomplete="off">
<div class="modal-body">
<div class="row">
<span asp-validation-for="Event.EndTime" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="col form-group">
<div>
<label>Event Date</label>
<input asp-for="Event.EventDate" id="datepicker" type="date" /> #*value="Event.EventDate"*#
<span class="add-on">
<i data-date-icon="icon-calendar">
</i>
</span>
</div>
</div>
<div> ... //input for EventViewModel properties continues
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" id="savechanges" data-bs-save="modal">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Button events for opening modal "Add/Edit" and placeholder div in Event/Index.cshtlm
#model IEnumerable<EventListDetails>
<div id="PlaceHolderHere"></div>
<!--button to _ModifyEventModalPartial "Add" (js.ModifyObject)-->
<button type="button" class="btn btn-primary" data-bs-toggle="ajax-modal" data-
target="#modifyEvent" data-controller="Event" data-action="Modify" data-modify-
type="Add">
Add Event
</button>
<!--button call to _ModifyEventModalPartial "Edit" (js.ModifyObject) -->
<button class="btn btn-primary" data-bs-toggle="ajax-modal" data-target="#modifyEvent"
data-controller="Event" data-action="Modify" data-modify-type="Edit">
Edit
</button>
Modify Get and Post Actions in EventController.cs
[HttpGet]
public IActionResult Modify(int id)
{
EventViewModel viewModel = new EventViewModel();
//add new event in modal
if (id == 0)
{
viewModel.Event = new Event();
viewModel.Event.EventDate = DateTime.Parse(DateTime.Now.ToString("MM/dd/yyyy hh:mm tt"));
//create new viewModel properties cont...
}
//edit event with id in modal
else
{
viewModel.Event = _db.Events.Find(id);
//other properies cont...
}
return PartialView("_ModifyEventModalPartial", viewModel); ;
}
//Post: /Event/Modify/{id}
/*adds and edits and event. Code behind for ~Views/Event/_ModifyEventModalPartial. Called from site.js Modify Object Function*/
[HttpPost]
public IActionResult Modify(EventViewModel viewModel)
{
viewModel.Event.StartTime = viewModel.Event.EventDate.Date +
viewModel.Event.StartTime.TimeOfDay;
//set end time...
if (viewModel.Event.StartTime < viewModel.Event.EndTime)
{
if (ModelState.IsValid)
{
//add new
if (viewModel.Event.Id == 0)
{
//add new event
}
//if edit
else
{
//edit event
}
return Ok(true); //return pass to Modify Object function in site.js
}
return PartialView("_ModifyEventModalPartial", viewModel);
}
I added kind of a lot of code in case anyone wanted to try reproducing the problem. but it's definitely something wrong in the site.js function, I just can't seem to figure out how to fix it.
I've got this test program I'm building to learn Blazor WASM, and I've built a generic table that takes a render fragment that runs a function on a given row. This function is just a simple JS alert that shows the details of a field of the specified row.
My problem is that this call causes the UI to render twice (I don't want it to render at all). I have found a work around at the moment by using an override for ShouldRender() and switching the render boolean to false both before, and after the JS call. But, I don't really like this approach.
Does anyone have any insight into why this is happening? Can I avoid having to do this with a different approach?
Here's the method, It's in a parent component to the table. You can see the boolean being set twice. This matters because the Table can be sorted, and I lose the sort whenever the alert is clicked (If I don't set the boolean twice, that is. If I set it once before or after the JS call it re renders once, and if I don't set it at all it renders twice).
`private async Task HandleClick(WeatherForecast forcast)
{
Render = false;
await js.InvokeVoidAsync("alert", $"{forcast.TemperatureF}");
Render = false;
}`
Here's the action that calls the method
<button #onclick="#(() => SelectedItem.InvokeAsync(item))">#renderFragment</button>
#typeparam GenericType
#code {
[Parameter] public EventCallback<GenericType> SelectedItem { get; set; }
[Parameter] public RenderFragment renderFragment { get; set; }
[Parameter] public GenericType item { get; set; }
}
And here's the table where the button is clicked.
#using System.ComponentModel;
#using System.Reflection;
#typeparam GenericType
#if (Items == null)
{
<img src="https://media4.giphy.com/media/3oEjI6SIIHBdRxXI40/giphy.gif" />
}
else
{
<table class="table">
<thead>
<tr>
#foreach (var prop in Props)
{
//Find the displayname, if it exists.
var name = PDesctirptions.Find(prop.Name, true)?.DisplayName ?? prop.Name;
<td #onclick="#(() => OrderBy(prop.Name))">#name</td>
}
#* if we have actions, show them. *#
#if(ActionFragment != null)
{
<td>Actions</td>
}
</tr>
</thead>
<tbody>
#foreach (var item in Items)
{
<tr>
#foreach (var p in Props)
{
//Get's the property value of the item to display in the table.
<td>#item.GetType().GetProperty(p.Name).GetValue(item).ToString()</td>
}
#if (ActionFragment != null)
{
<td>#ActionFragment(item)</td>
}
</tr>
}
</tbody>
</table>
}
#code {
private bool desc = false;
[Parameter] public IEnumerable<GenericType> Items { get; set; }
[Parameter] public RenderFragment<GenericType> ActionFragment { get; set; }
private Type T = typeof(GenericType);
private PropertyInfo[] Props { get; set; }
private PropertyDescriptorCollection PDesctirptions { get; set; }
protected override void OnInitialized()
{
Props = T.GetProperties();
PDesctirptions = TypeDescriptor.GetProperties(T);
}
void OrderBy(string name)
{
PropertyDescriptor pDescriptor = TypeDescriptor.GetProperties(T).Find(name, true);
if (desc)
{
Items = Items.OrderBy(d => pDescriptor.GetValue(d)).ToList();
desc = false;
}
else
{
Items = Items.OrderByDescending(d => pDescriptor.GetValue(d)).ToList();
desc = true;
}
}
}
Here's a visualization of the table, if it helps.
enter image description here
All -
I am new to MVC/Javascript and have tried multiple tutorials on ways to get a cascading drop down list working for State and City on an MVC view. My most recent attempt I am using this Example
The states populate from the DB, but the cities are a blank drop down with a bout 10 empty spaces. Regardless of what state I pick. What am I missing? Thanks for looking and advice/feedback!
Here is the DB Tables info:
Regions (contains states): Id (PK Int), Name (Varchar(255) state/regionname), CountryId (Int)
Cities (Contains cities): Id (PK Int), Name (Varchar(255) state/name), RegionID (int, linkes to region table)
Here is the view model:
public class UserAds
{
public int addId { get; set; }
public int userCreatedAcctId { get; set; }
public string userForAccEmail { get; set; }
public string photoFileLocation { get; set; }
public string addTitle { get; set; }
public string addDesc { get; set; }
public string addPersonalityTraits { get; set; }
public string addHobbies { get; set;}
[Required]
[Display(Name = "State / Region")]
public int stateId { get; set; }
[Required]
[Display(Name = "City")]
public int cityId { get; set; }
}
Here is the controller:
[HttpGet]
public ActionResult CreateAd()
{
List<Region> stateList = db.Regions.ToList();
ViewBag.StateList = new SelectList(stateList, "Id", "Name");
return View();
}
public JsonResult GetCitiesList(int StateId)
{
db.Configuration.ProxyCreationEnabled = false;
List<City> CityList = db.Cities.Where(x => x.RegionId == StateId).ToList();
return Json(CityList, JsonRequestBehavior.AllowGet);
}
The view:
<div class="container">
<div class="form-group">
#if (ViewBag.StateList != null)
{
#Html.DropDownListFor(model => model.stateId, ViewBag.StateList as SelectList, "--Select State--", new { #class = "form-control" })
}
</div>
<div class="form-group">
#Html.DropDownListFor(model => model.cityId, new SelectList(" "), "--Select City--", new { #class = "form-control" })
</div>
javascript
<script>
$(document).ready(function () {
$("#stateId").change(function () {
$.get("GetCitiesList", { StateId: $("#stateId").val() }, function (data) {
$("#cityId").empty();
$.each(data, function (index, row) {
$("#cityId").append("<option value=" + row.Id + "'>" + row.Name + "</option>")
});
});
})
});
Javascript debugger in IE
In the below line, you are missing a single quote after value=
$("#cityId").append("<option value=" + row.Id + "'>" + row.Name + "</option>")
It should ideally be something like below-
$("#cityId").append("<option value='" + row.Id + "'>" + row.Name + "</option>")
I have two related models.
public partial class bs_delivery_type
{
public decimal delivery_id { get; set; }
public decimal delivery_city_id { get; set; }
public string delivery_address { get; set; }
public virtual bs_cities bs_cities { get; set; }
}
and the second one:
public partial class bs_cities
{
public bs_cities()
{
this.bs_delivery_type = new HashSet<bs_delivery_type>();
}
public decimal cities_id { get; set; }
public string cities_name { get; set; }
public virtual ICollection<bs_delivery_type> bs_delivery_type { get; set; }
}
and I have such ViewBag's for dropdownlist's:
ViewBag.city = new SelectList(_db.bs_cities, "cities_id", "cities_id");
ViewBag.delivery_adress = new SelectList(_db.bs_cities, "delivery_id", "delivery_address");
When I choose city in first dropdownlist, in the second one there has to be appeared binded list with delivery_adress, where delivery_city_id = cities_id(from first dropdownlist).
How to do that?
Edit:
I tryed method from #Izzy's comment, so here is my actual view:
#model Bike_Store.Models.DeliveryModel
#{
ViewBag.Title = "Checkout";
}
<script src="~/Scripts/bootstrap.min.js"></script>
<script src="~/Scripts/jquery-3.1.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script type="text/javascript">
function GetDelivery(_stateId) {
var procemessage = "<option value='0'> Please wait...</option>";
$("#ddldelivery").html(procemessage).show();
var url = "/Shop/GetDeliveryByCityId/";
$.ajax({
url: url,
data: { cities_id: _stateId },
cache: false,
type: "POST",
success: function (data) {
var markup = "<option value='0'>Select adress</option>";
for (var x = 0; x < data.length; x++) {
markup += "<option value=" + data[x].Value + ">" + data[x].Text + "</option>";
}
$("#ddldelivery").html(markup).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
}
</script>
<h2>Checkout</h2>
#using (Html.BeginForm())
{
#Html.DropDownListFor(m=>m.CitiesModel, new SelectList(Model.CitiesModel, "cities_id", "cities_name"), new {#id = "ddldelivery", #style="width:200px", #onchange="javascript:GetDelivery(this.value);"})
<br />
<br />
<select id="ddldelivery" name="ddldelivery" style="width:200px">
</select>
<br /><br />
}
My controller now looks like this:
public List<bs_cities> GetAllCities()
{
List<bs_cities> cities = new List<bs_cities>();
foreach (var city in _db.bs_cities)
{
cities.Add(city);
}
return cities;
}
public List<bs_delivery_type> GetAllDeliveries()
{
List<bs_delivery_type> deliveries = new List<bs_delivery_type>();
foreach (var delivery in _db.bs_delivery_type)
{
deliveries.Add(delivery);
}
return deliveries;
}
[HttpPost]
public ActionResult GetDeliveryByCityId(decimal cities_id)
{
List<bs_delivery_type> delivery = new List<bs_delivery_type>();
delivery = GetAllDeliveries().Where(m => m.delivery_city_id == cities_id).ToList();
SelectList objDelivery = new SelectList(delivery, "delivery_id", "delivery_address", 0);
return Json(objDelivery);
}
public ViewResult Checkout()
{
DeliveryModel deliveryModel = new DeliveryModel();
deliveryModel.CitiesModel = new List<bs_cities>();
deliveryModel.CitiesModel = GetAllCities();
return View(deliveryModel);
}
The problem now is that i have 2 ddls, but works only first one.
In scrshot you can see I have a list of cities, when I choose a city, in this same ddl appears a list of delivery adresses, and when I choose adress - its desappears. What a magic? Help me please with Ajax.
List of cities
I guesse i fixed it, the problem was in:
#Html.DropDownListFor(m=>m.CitiesModel, new SelectList(Model.CitiesModel, "cities_id", "cities_name"), new {#id = "ddldelivery", #style="width:200px", #onchange="javascript:GetDelivery(this.value);"})
I changes #id = "ddldelivery" to #id = "ddlcity" and it works now
The following guide will show you:
Create a partial view
Takes cityid as input and outputs the delivery address list
Load partial view into your select
Note: Partial view solution may be overkill in this situation, but for similar problems it is actually quite usefull.
PartialView .cshtml
Filename: _deliveryTypePartial.cshtml
#model List<bs_delivery_type>
#foreach(var item in Model)
{
<option value="#item.delivery_id">
#item.delivery_address
</option>
}
Controller Code for Partial View:
public IActionResult _deliveryTypePartial(decimal city_id)
{
List<bs_delivery_type> model = context.bs_delivery_types.Where(row => row.delivery_city_id == delivery_city_id).ToList();
return PartialView(model);
}
And then Finally, for your AJAX
I notice that your two dropdownlists have identical ID's witch will cloud your javascript code and is considered bad practice, so for the purposes of this guide I will call the first dropdownlist:
ddlcity
Now, inside your onchange function for ddlcity:
$('#ddldelivery').load("/ControllerName/_deliveryTypePartial?city_id=" _stateId);
This should load the partial view into your second dropdown list.
PS: As I completed this question you had already used the direct ajax method, I agree that both methods are equally suitable in this case. You can perhaps use the method outlined here if the actual objects you need to populate are a lot more complex.
I want design page like this
up to now I have created like this . I want to know bind check-box front of each row and send those checked/non-checked values with IDs using json and jquery
this last code snippet of that page
<div style="width:50%; float:left;text-align:left"><button id="resetborchure" type="button" class="btn btn-warning submit">Reset Brochure</button> </div>
<div style="width:50%; float:left;text-align:right"><button id="createborchure" type="button" class="btn btn-danger submit">Create Brochure</button> </div>
<script type="text/javascript">
</script>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
#Scripts.Render("~/bundles/jqueryui")
<script type="text/javascript">
var url = '#Url.Action("FetchProductProperties")';
var editUrl = '#Url.Action("Edit")';
var type = $('#Type');
var category = $('#Category');
var country = $('#Country');
var product = $('#Product');
var template = $('#template');
var table = $('#table');
$('#search').click(function () {
table.empty();
$.getJSON(url, { type: type.val(), category: category.val(), country: country.val(), product: product.val() }, function (data) {
$.each(data, function (index, item) {
var clone = template.clone();
var cells = clone.find('td');
cells.eq(0).text(item.ID);
cells.eq(1).text(item.Name);
table.append(clone.find('tr'));
});
});
});
$('#resetborchure').click(function () {
table.empty();
});
</script>
}
Also I want , once I checked and click create brochure button I want send those checked/non-checked values with IDs using json
I have try to put to populate a checkbox with each listed result '<td><input type="checkbox" /></td>' inside cells.eq(1).text(item.Name);
as cells.eq(1).text('<td><input type="checkbox" /></td>'+item.Name); but this is not working
Once I click "Select Information" button Its list down data from AB_Product_Property table , IF I want to populate check-box with each search result row Do I need maintain boolean field in that table also ?? I want to do this without maintain column for that boolean field in AB_Product_Property table
Create a view model(s) to represent what you want to display/edit.
public class OptionsVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class SearchVM
{
public int Asset { get; set; }
public SelectList AssetList { get; set; }
public int Category{ get; set; }
public SelectList CategoryList { get; set; }
.... // other properties and SelectLists for the dropdownlists
public List<OptionsVM> Options { get; set; }
}
and in the GET method, populate the Options with the ID and Name properties
Then in the view
#model SearchVM
....
<form>
#Html.DropDownListFor(m => m.Asset, Model.AssetList)
....
#for(int i = 0; i < Model.Options.Count; i++)
{
#Html.HiddenFor(m => m.Options[i].ID)
#Html.CheckBoxFor(m => m.Options[i].IsSelected)
#Html.LabelFor(m => m.Options[i].IsSelected, Model.Options[i].Name)
}
<button type="button" id="createbrochure">Create Brochure</button>
and in the script
$('#createbrochure').click(function () {
$.getJSON(url, $('form').serialize(), function (data) {
....
});
})
and in the controller method
public ActionResult CreateBrochure(SearchVM model)
{
// To get the ID's of all selected options
IEnumerable<int> selectedOptions = model.Options.Where(o => o.IsSelected).Select(o => o.ID);
....
}
You can create new column for check box instead of appending check box to name column.
Than you can set class to that check box and get the checked or unchecked check box value using jquery.