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.
Related
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
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.
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);
}
I have a View page which contains a dropdownlist of values populated from a custom constructor class.
The DDL is populated with a SelectList created from a list of custom items as follows:
public class CustomerType
{
public int Id { get; set; }
public string Name { get; set; }
public string ValidationRule { get; set; }
}
In my View I then set this as so:
#Html.DropDownListFor(model => model.Id, (IEnumerable) Model.SelectListOfCustomItems, "Please select...")
What I want to try and do is display the ValidationRule property for each item selected, and show this in a Label.
I figure I have to use JavaScript to do this, but I'm unsure how to actually get that ValidationRule property? I could probably get the selected item using code similar to the below, but I can't see how to drill down to get other data?
var dropDown = document.getElementById("MyDropDownListName");
dropDown.onchange = function () { DisplayValidationRule(this); };
function DisplayValidationRule(ddl)
{
document.getElementById('lblRule').textContent = [Unknown]
}
The bit I'm missing is where I've marked it [Unknown], as I don't have a club how to get this. I was thinking maybe something like ddl.items[ddl.selectedItem].value['ValidationRule'] but that doesn't work for me.
There are many ways to implement it. One way is to generate a set of hidden input fields like id="_CustomerType_id_xxx" with value "ValidationRule", then according to the select result to take the value. Another way is to send ajax request to get the rule. But I think it's not a good way.
Update
OK, below is my full codes, it passed on MVC4.
HomeController
public class HomeController : Controller
{
public ActionResult Index()
{
IList<CustomerType> customerTypes = new List<CustomerType>();
customerTypes.Add(new CustomerType { Id = 1, Name = "Customer Type 1", ValidationRule = "Rule 1" });
customerTypes.Add(new CustomerType { Id = 2, Name = "Customer Type 2", ValidationRule = "Rule 2" });
customerTypes.Add(new CustomerType { Id = 3, Name = "Customer Type 3", ValidationRule = "Rule 3" });
IEnumerable<SelectListItem> selectList =
from c in customerTypes
select new SelectListItem
{
Text = c.Name,
Value = c.Id.ToString()
};
return View(new CustomerVO { SelectListOfCustomItems = selectList, CustomerTypes = customerTypes });
}
}
Index.cshtml
#model MvcApplication1.Models.CustomerVO
#Html.DropDownListFor(model => model.CustomerTypeId, Model.SelectListOfCustomItems, "Please select...")
#foreach (var customerType in Model.CustomerTypes)
{
<input type="hidden" id="_customerType_#customerType.Id" value="#customerType.ValidationRule" />
}
<label id="lblRule"></label>
#section scripts
{
<script>
$(document).ready(function () {
var dropDown = document.getElementById("CustomerTypeId");
dropDown.onchange = function () { DisplayValidationRule(this); };
function DisplayValidationRule(ddl)
{
document.getElementById('lblRule').textContent = $("#_customerType_" + ddl.value).val();
}
});
</script>
}
I've been using this tutorial to create a screen where a user can add additional input fields on a given screen
Instead of using all textboxes Iv'e created a UIHint/Partial view that'll render a multi-select JQuery widget (I'm using this component)
ViewModel for each row item
public class Micros
{
[UIHint("JsonComboBox")]
[AdditionalMetadata("id", "Lab_T_ID")]
[AdditionalMetadata("description", "Description")]
[AdditionalMetadata("action", "LabTestOptions")]
[AdditionalMetadata("controller", "Micro")]
[AdditionalMetadata("noneSelectedText", "Test")]
[AdditionalMetadata("comboboxWidth", "200")]
[DisplayName("Test")]
public Nullable<int> Lab_T_ID { get; set; }
[UIHint("JsonComboBox")]
[AdditionalMetadata("id", "Lab_SD_ID")]
[AdditionalMetadata("description", "Description")]
[AdditionalMetadata("action", "LabSampleDetailOptions")]
[AdditionalMetadata("controller", "Micro")]
[AdditionalMetadata("noneSelectedText", "Sample Details")]
[AdditionalMetadata("comboboxWidth", "300")]
[DisplayName("Sample Details")]
public Nullable<int> Lab_SD_ID { get; set; }
[DisplayName("Result")]
public string Result { get; set; }
}
Partial View/UIHint
#model int?
#{
var values = ViewData.ModelMetadata.AdditionalValues;
}
<select id="#values["id"]" multiple="multiple" style="width:#values["comboboxWidth"]px" > </select>
<script type="text/javascript">
$.getJSON('#Url.Action((string)values["action"], (string)values["controller"])',
null,
function (j) {
var options = '';
for (var i = 0; i < j.length; i++) {
options += '<option value="' + j[i].#values["id"] + '">' + j[i].#values["description"] + '</option>';
}
$('##values["id"]').html(options);
$('##values["id"] option:first').attr('selected', 'selected');
});
setTimeout(function () {
$("##values["id"]").multiselect({
multiple: false,
header: "Select an option",
noneSelectedText: '#values["noneSelectedText"]',
selectedList: 1
});
}, 300);
</script>
The components render fine on the initial page load, but when add the items, they get added... but it seems that the javascript doesn't execute/get added..
Any ideas? Still debugging this issue, will post the fix as soon as I find it, but I'm hoping someone can point me in the right direction
Update
So far I've discovered that (We'll it looks like), the UIHint/Partials don't get rendered at all when the user clicks to add another item. (Otherwise the select will be populated with items, and the JQuery widget will be applied)
I would recommend you to remove all javascript from your partial. Javascript shouldn't be mixed with markup. So your editor template should contain only markup:
#model int?
#{
var values = ViewData.ModelMetadata.AdditionalValues;
}
<span>
<select multiple="multiple" style="width:#values["comboboxWidth"]px" data-url="#Url.Action((string)values["action"], (string)values["controller"])" data-noneselectedtext="#values["noneSelectedText"]" data-value="#values["id"]" data-text="#values["description"]"></select>
</span>
and then in a separate javascript file you will have a function which will be used when the Add another... button is clicked as shown in Steven Sanderson's article:
$('#addItem').click(function() {
$.ajax({
url: this.href,
cache: false,
success: function(html) {
// Here's the place to attach any plugins to the given row:
var select = $('select', html);
$.getJSON(select.data('url'), function(options) {
$.each(options, function() {
select.append(
$('<option/>', {
value: this[select.data('value')],
text: this[select.data('text')]
})
);
});
select.multiselect({
multiple: false,
header: 'Select an option',
noneSelectedText: select.data('noneselectedtext'),
selectedList: 1
});
$('#editorRows').append(select);
});
}
});
return false;
});