MVC UIHint/Parital view with JQuery multiselect, dynamic creation issue - javascript

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;
});

Related

Kendo textbox within dropdownlist

is possible to make textbox within the dropdownlist from Kendo? When user won't find on list what he needs, there should be textbox to set manually some value.
dropdown:
#(Html.Kendo().DropDownList()
.Name("Id")
.DataTextField("StringValue")
.DataValueField("Id")
.SelectedIndex(0))
javascript:
$('#AttributeValue_Id').kendoDropDownList({
dataSource: dataSource,
dataTextField: "Text",
dataValueField: "Value",
optionLabel: '#Html.Raw(T("Product.Attribute.SelectValue"))',
dataBound: function () {
$('#AttributeValue_Id').data('kendoDropDownList').select(0);
}
});
Values in this dropdown depend on another dropdown, where we pick attribute and then this dropdown is getting new data dynamically. Somebody know any solution?
Ok I solved this issue. Solution is:
when the element on dropdown filter is not exist, I can write my string value and redirect to AddNew function. Javascript for no data template:
<script id="noDataTemplate" type="text/x-kendo-tmpl">
<div>
Didn't find the element
</div>
<br />
<button class="k-button" onclick="addNew('#: instance.element[0].id #', '#: instance.filterInput.val() #')">Add new value</button>
AddNew function is get widget element and my value. If user will confirm the script will redirect to action from controller sending needed values - productId from Model, value from parameter and attributeDefinitionId value from first dropdown:
function addNew(widgetId, value) {
var widget = $("#" + widgetId).getKendoDropDownList();
var attributeDefinition = $('#AttributeDefinition').data('kendoDropDownList').value();
var dataSource = widget.dataSource;
if (confirm("Are you sure?")) {
$.ajax({
url: '#Url.Action("AddAttributeValue", "Product")' + '?productId=#Model.Id' + '&value=' + value + '&attributeDefinition=' + attributeDefinition,
cache: false,
}).done(function () {
var grid = $("#attributesGrid").data("kendoGrid");
grid.dataSource.read();
});
dataSource.one("sync", function () {
widget.select(dataSource.view().length - 1);
});
dataSource.sync();
}
};
In controller I'm getting these values and insert them to database:
public ActionResult AddAttributeValue(int productId, string value, int attributeDefinition)
{
if (value != null)
{
try
{
var model = attributeValueRepository.Insert(new ProductAttributeValue()
{
IsCustom = true,
StringValue = value,
AttributeDefinitionId = attributeDefinition,
});
productAttributeRepository.Insert(new ProductAttribute()
{
AttributeValueId = model.Id,
ProductId = productId
});
} catch
{
AddErrorFlashMessage(T("Product.Attribute.AttributeValueError"));
return BadRequest();
}
}
return Ok();
}
Edit
Almost forgot, I set also on dropDown noDataTemplate:
$('#AttributeValue_Id').kendoDropDownList({
dataSource: dataSource,
dataTextField: "Text",
dataValueField: "Value",
optionLabel: '#Html.Raw(T("Product.Attribute.SelectValue"))',
filter: "startswith",
noDataTemplate: $("#noDataTemplate").html(),
dataBound: function () {
$('#AttributeValue_Id').data('kendoDropDownList').select(0);
}
});
Thanks everybody for the tips
Might be late for an answer but kendo dropdownlist supports contains filter for exactly this workflow.
<%= Html.Kendo().DropDownList()
.Name("DropDownList")
.Filter(FilterType.Contains);
%>
Ref: Kendo UI Dropdownlist API
Kendo already have feature within Dropdownlist in case user unable to find his choice option. He can add new element manually in dropdownlist. Please find link below
https://demos.telerik.com/kendo-ui/dropdownlist/addnewitem

Populate DropDownList from another DropDownList

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.

Populate a checkbox front of each row and send checked/non-checked values and IDs using json and jquery

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.

Toggle bit value in database with buttons - Using Entity Framework

I have an html table that holds categories of things. Each row consists of a category id and name that are looped in from the model. There are also two buttons in each row. One that I want to use to set the state of the row to enabled and the other to set it to disabled:
http://codepen.io/cavanflynn/pen/waPXww - This codepen shows how I'm working the buttons on the front end
<table id="categoryList" class="table">
<thead>
<tr>
<th>Category ID</th>
<th>Category Name</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Categories)
{
<tr>
<td>#item.id</td>
<td>#item.name</td>
<td>
<button class="btn btn-success categoryEnabled">Enabled</button>
<button class="btn btn-danger categoryDisabled" style="display: none;">Disabled</button>
</td>
</tr>
}
</tbody>
</table>
When I say set to enabled or disabled I mean change the bit value for that row in an SQL table column called state. So, I basically just need the buttons to toggle that bit value for the row in which it is clicked. I have been struggling to figure out the method to do this.
Example controller code to help out:
public ActionResult SaveCategory(int categoryId, String categoryName)
{
var connection = new CategoryDBEntities();
if (categoryId > 0)
{
var rsc = connection.QualityCategories.Find(categoryId);
rsc.name = categoryName;
}
else
{
QualityCategory rsc = new QualityCategory();
rsc.name = categoryName;
connection.QualityCategories.Add(rsc);
}
connection.SaveChanges();
return Redirect(System.Web.HttpContext.Current.Request.UrlReferrer.AbsolutePath);
}
(This code is used to save category changes)
Any help would be appreciated. Thanks!
Assuming your Category object looks something like:
public enum StatesTypes
{
Disabled = 0,
Enabled = 1
}
public class Category
{
public int Id { get; set; }
public StatesTypes State { get; set; }
public String Name { get; set; }
}
Perhaps then you could have a SaveCategory method within your Controller which returns back a Json object.
public JsonResult SaveCategory(StatesTypes state, int categoryId)
{
CategoryJsonViewModel ret = new CategoryJsonViewModel();
ret.Response = //Use EF here - perhaps call a service object to save the category?
if(ret.Response == BaseJsonResponseTypes.Success)
{
if(state == StatesTypes.Disabled)
{
ret.Message = "Category successfully disabled";
}
else
{
ret.Message = "Category successfully enabled";
}
}
else
{
ret.Message = "Critical error :-(";
}
return Json(ret);
}
Dependency classes (up to you how you do this):
public enum BaseJsonResponseTypes
{
NotSet = 0,
Error = 1,
Success = 2
}
public class BaseJsonViewModel
{
public BaseJsonResponseTypes Response { get; set; }
public String Message { get; set; }
}
public class CategoryJsonViewModel : BaseJsonViewModel
{
//Maybe you could include some implemention here.
}
Your jQuery would look something like:
$(".disable").on("click", function(){
$(this).hide();
$(this).next().show();
var id = $(this).data('val');
SaveCategory(0, id);
});
$(".enable").on("click", function(){
$(this).hide();
$(this).prev().show();
var id = $(this).data('val');
SaveCategory(1, id);
});
function SaveCategory(state, id) {
var obj = {
state: state,
categoryId: id
};
$.ajax({
type: "POST",
url: "/category/savecategory",
contentType: "application/json; charset=utf-8",
data : JSON.stringify(obj),
dataType: "json",
success: function(data) {
//Do something cool with the data object?
alert(data.Message);
}
});
}
data-val attribute has been added, as shown (this passes through the category id):
<button class="disable" data-val="#item.id">Disable</button>
<button class="enable" style="display:none;" data-val="#item.id">Enable</button>
You need to give your buttons some way to pass the specific row identifier to the back-end, so that it knows which row to effect.
<button class="disable" id="Row1Disable">Disable</button>
Then you will need to hook up your jquery post method to your controller action, something something like this:
$(".disable").on("click", function(){
$(this).hide();
$(this).next().show();
$.post("#Url.Action("DisableRow", "MyController")", { "Id": $(this).attr('id') }, function (response) {
if (response.success) {
// Handle successful disabled post.
alert('Row Disabled!');
} else {
// Handle error.
alert('Error disabling row!');
}
});
});
$(".enable").on("click", function(){
$(this).hide();
$(this).prev().show();
$.post("#Url.Action("EnableRow", "MyController")", { "Id": $(this).attr('id') }, function (response) {
if (response.success) {
// Handle successful disabled post.
alert('Row Enabled!');
} else {
// Handle error.
alert('Error enabling row!');
}
});
});
Once the posts are hooked up to proper controller actions, you should be good to go. Here's the codePen based on your original one: http://codepen.io/anon/pen/xGPzZW?editors=101
Edit exemplifying ID uniqueness in loop:
using for loop:
for(int i = 0; i < rows.Count(); i++)
{
string id = "Row" + i.ToString() + "Enable";
// do the rest...
}
using while / foreach loop:
int i = 0;
foreach (var row in Model.Rows)
{
string id = "Row" + i.ToString() + "Enable";
i +=1;
// do the rest...
}

MVC 4 - Cascading Dropdown Lists - Issue with Ajax JavaScript Call

I have an MVC 4 app with a View containing two dropdown lists. The user selects a value in the first dropdown and then an Ajax call is made to populate the second dropdown based on the contents of the first.
My JavaScript code looks as follows and gets called when the user selects an item in the first dropdown:
function GetAutoModel(_manufacturerId) {
var autoSellerListingId = document.getElementById("AutoSellerListingId").value;
$.ajax({
url: "/AutoSellerListing/GetAutoModel/",
data: { manufacturerId: _manufacturerId, autoSellerListingId: autoSellerListingId },
cache: false,
type: "POST",
success: function (data) {
var markup = "<option value='0'>-- Select --</option>";
for (var x = 0; x < data.length; x++) {
**if (data[x].Selected) {**
markup += "<option selected='selected' value=" + data[x].Value + ">" + data[x].Text + "</option>";
}
else
markup += "<option value=" + data[x].Value + ">" + data[x].Text + "</option>";
}
$('#autoModel').html(markup).show();
},
error: function (reponse) {
alert("error : " + reponse);
}
});
}
The Ajax call works correctly. However, the data that gets returned for the second dropdown contains a selected item and I'm trying to detect the selected item (via the 'if' statement), and render the HTML appropriately. The problem is that 'Selected' doesn't seem to be a property of 'data' because each value evaluates to false, even though one of the values is true.
Am I doing something wrong? Or is there a better way to do this?
The following is the controller code:
[HttpPost]
public ActionResult GetAutoModel(int manufacturerId, int autoSellerListingId)
{
int modelId = 0;
// Get all the models associated with the target manufacturer
List<AutoModel> modelList = this._AutoLogic.GetModelListByManufacturer(manufacturerId);
// If this is an existing listing, get the auto model Id value the seller selected.
if (autoSellerListingId > 0)
modelId = this._systemLogic.GetItem<AutoSellerListing>(row => row.AutoSellerListingId == autoSellerListingId).AutoModel.AutoModelId;
// Convert all the model data to a SelectList object
SelectList returnList = new SelectList(modelList, "AutoModelId", "Description");
// Now find the selected model in the list and set it to selected.
foreach (var item in returnList)
{
if (item.Value == modelId.ToString())
item.Selected = true;
}
return Json(returnList);
}
Try this instead (add modelId to constructor of SelectList, and remove the foreach block):
// Convert all the model data to a SelectList object
SelectList returnList = new SelectList(modelList, "AutoModelId", "Description", modelId);
return Json(returnList);

Categories