Telerik MVC, Combobox changing the CSS of an item - javascript

My ASP.NET MVC-3 application is using the previous version of Telerik MVC Extensions combobox. I am trying to change the style of an item in the list.
Here is the model:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public bool DisplayBold { get; set; }
public string Value
{
get
{
return string.Format("{0}|{1}", this.Id, this.DisplayBold.ToString());
}
}
}
The Controller:
var people = new List<Person>();
people.Add(new Person { Id = 1, Name = "John Doe", DisplayBold = true });
people.Add(new Person { Id = 2, Name = "Jayne Doe", DisplayBold = false });
ViewData["people"] = people;
return View();
The Combobox:
<% Html.Telerik().ComboBox()
.Name("ComboBox")
.BindTo(new SelectList((IEnumerable<Person>)ViewData["people"], "Id", "Name"))
.ClientEvents(events => events
.OnChange("ComboBox_onChange")
.OnLoad("ComboBox_onLoad")
.OnOpen("ComboBox_OnOpen"))
.Render();
%>
I tried the following and it did change the first item:
var item = combobox.dropDown.$items.first();
item.addClass('test');
However when I tried to change the CSS when it is Ture:
var combobox = $(this).data('tComboBox');
$.each(combobox.dropDown.$items, function (idx, item) {
if (combobox.data[idx].Value.split('|')[1] == 'True') {
alert(item);
$(item).addClass('test');
}
});
It did not work!

This is the version after user373721 marked this as answered
While i was rewriting my previous answer and browsing the forums user373721 marked my old revision as answered.
I am sorry i searched the forum of telerik to see how you could hook into the databinding to influence the css. I could not find a good match to your problem.
One muddy workaround (getting desperated here) could be to add html to the names that should be displayed bold:
public class Person
{
public string NameText { get; }
{
get
{
if(this.DisplayBold) {
return "<b>" + this.Name + "</b>";
} else
return this.Name;
}
}
}
So instead of binding to Name you would bind to NameText.
You may need to take care of html-conversion.
In my last search i found a post that may help. And now i found a post that could be from you
By the way in the forums i have read that there were several bug
fixes that could be important for your goal.
Which telerik mvc-release are you using?

Solution to set style with the new mvc-telerik extensions (kendo)
Hi based on the example at telerik mvc comboBox usage i used a template approach. At jsfiddle you can find a working example for the new telerik mvc extension (kendo).
Use of template to set style based on the underlying datasource:
<script id="template" type="text/x-kendo-tmpl">
# if (data.displayBold) { #
<span style="font-weight: bolder;">${ data.name }</span>
# } else { #
<span style="font-weight: normal;">${ data.name }</span>
# } #
</script>
On document.ready bind the combobox:
// use of templates
$("#titles").kendoComboBox({
dataTextField: "name",
dataValueField: "Id",
// define custom template
template: kendo.template($("#template").html()),
dataSource: musicians
});
The dataSource is an array of objects similar to your person class:
var musicians= [{ id: 1, name: "Melody Gardot", displayBold: true }
, { id: 2, name: "Lynn Withers", displayBold: false }
, { id: 3, name: "Blue Ray", displayBold: true }
, { id: 4, name: "Club de Belugas", displayBold: true }
, { id: 5, name: "Mizzy Li", displayBold: false }
];
hth

Related

How to bind object property to datasource in DropDownList without using ViewData

I have a DropDownList column in my kendo Grid. I want to change my ViewData["genres"] in DataSource to GenreViewModel property "AllGenres". How can I do that? Don't forget that I use js version of Grid(not mvc).
I think it should looks like:
dataSource: #Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize("AllGenres"))
or
dataSource: "AllGenres"
against of:
dataSource: #Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(ViewData["genres"]))
My DropDownList:
function genreDropDownEditor(container, options) {
$('<input required name="' + options.field + '"/>')
.appendTo(container)
.kendoDropDownList({
autoBind: false,
dataTextField: "GenreName",
dataValueField: "GenreId",
dataSource: #Html.Raw(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(ViewData["genres"]))
});
<script type="text/kendo" id="genresTemplate">
#if(data.Genre != null)
{ #
#: Genre.GenreName #
# } #
My GenreViewModel:
public class GenreViewModel
{
public int GenreId { get; set; }
public string GenreName { get { return Enum.GetName(typeof(Genre), GenreId); } }
public static List<GenreViewModel> AllGenres
{
get
{
return Enum.GetValues(typeof(Genre)).Cast<Genre>().Select(v => new GenreViewModel
{
GenreId = ((int)v)
}).ToList();
}
}
}
Stephen Muecke gave the answer in the comments.(don't know how to choose his comment as an answer correctly)
dataSource: #Html.Raw(Json.Encode(Model.AllGenres)).
but I did not declare a model in my view, so
I used my static property:
dataSource: #Html.Raw(Json.Encode(Number2.Models.GenreViewModel.GetAllGenres))

How to implement search with two terms for a collection?

So, currently I have a collection of items where I want the user to be able to search using the name and some random text from the collection.
Here's what I have done so far:
public IEnumerable<Item> items = new[]
{
new Item { ItemId = 1, ItemName = "Apple", ItemDescription = "crispy, sweet", ItemPairing = "Walnut"},
new Item { ItemId = 2, ItemName = "Pear", ItemDescription = "slightly tart", ItemPairing = "Cheese"},
new Item { ItemId = 3, ItemName = "Banana", ItemDescription = "good source of potassium", ItemPairing = "Honey" },
new Item { ItemId = 4, ItemName = "Chocolate", ItemDescription = "Sweet and rich melting flavor", ItemPairing = "Wine"}
};
public ActionResult Index()
{
return View("Index");
}
public ActionResult Search(string search)
{
return View("Index", items.Where(n => n.ItemName.StartsWith(search)));
}
Here's the search part on the view:
<p>
<b>Search for Name:</b>
#Html.TextBox("ItemName", "", new { #class = "form-control" })
<b>Search for Text:</b>
#Html.TextBox("ItemText", "", new { #class = "form-control" })
<input id="search" type="submit" value="Search" onclick="search(ItemName,ItemText)" />
</p>
<script>
function search(ItemName, ItemText) {
$("search").click(function () {
$.ajax({
type: "POST",
url: "/Items/Search",
data: {ItemName, ItemText},
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
});
}
</script>
So that it can look something like this:
I want it so that when the user types in Apple for Name and Crispy for text, they can find the Apple item from my collection. I also want it so that if they type in either Name or Text, it will still return a matched item.
I'm not sure how to do that.
Remove the onclick attribute from the submit button and change it to
<button type="button" id="search">Search</button>
and change the script to
var url = '#Url.Action("Search", "Items")';
$("#search").click(function () {
$.post(url, { ItemName: $('#ItemName').val(), ItemText: $('#ItemText').val() }, function(data) {
$('#result').html(data);
});
})
Note you may want to consider making it $.get() rather than $.post()
and change the controller method to accept the inputs from both textboxes
public PartialViewResult Search(string itemName, string itemText)
{
var items = ??
// filter the data (adjust to suit your needs)
if (itemName != null)
{
items = items.Where(x => x.ItemName.ToUpperInveriant().Contains(itemName.ToUpperInveriant())
}
if (itemText != null)
{
items = items.Where(x => x.ItemDescription.ToUpperInveriant().Contains(itemText.ToUpperInveriant())
}
// query you data
return PartialView("_Search", items);
}
Side note: Its not clear what the logic for search is - i.e. if you enter search text in both textboxes, do you want an and or an or search
Assuming the view in the view in the question is Index.cshtml, then it will include the following html
<div id="result">
#Html.Action("Search") // assumes you want to initially display all items
</div>
and the _Search.cshtml partial would be something like
#model IEnumerable<Item>
#foreach (var item in Model)
{
// html to display the item properties
}
While CStrouble's answer will work, if you're into AJAX calls and want to sort this in a single page, you might consider using AJAX and JQuery calls.
With AJAX & JQuery:
var search = function(itemName, itemText) {
//note that if you want RESTful link, you'll have to edit the Routing config file.
$ajax.get('/controller/action/itemName/itemText/', function(res) {
//do stuff with results... for instance:
res.forEach(function(element) {
$('#someElement').append(element);
});
});
};
And the action:
public JsonResult SearchAction(itemName, itemText) {
List<Item> newItems = items.Where(x => x.ItemName.ToUpperInveriant().Contains(itemName.ToUpperInveriant())
|| x.ItemDescription.ToUpperInveriant().Contains(itemText.ToUpperInveriant())
|| x.ItemPairing.ToUpperInveriant().Contains(itemText.ToUpperInveriant()));
return Json.Encode(newItems.ToArray());
}
Note that I'm not home, so there might be some syntax errors.
Alright, so say I want to return a partial, instead of a simple array:
first, change the action to:
public ActionResult SearchAction(itemName, itemText) {
List<Item> newItems = items.Where(x => x.ItemName.ToUpperInveriant().Contains(itemName.ToUpperInveriant())
|| x.ItemDescription.ToUpperInveriant().Contains(itemText.ToUpperInveriant())
|| x.ItemPairing.ToUpperInveriant().Contains(itemText.ToUpperInveriant()));
return PartialView("~/Views/Partials/MyPartialView.cshtml", newItems);
}
And you create a partial view in the directory you specified, taking the model that you transferred (newItems)
#model IEnumerable<Path.to.Item>
<h3>Results</h3>
<ul>
#foreach(var item in model)
{
<li>#item.Name - #item.Description</li>
}
</ul>
and now when you receive the jquery response:
$.ajax({
type: "POST",
url: "/Items/Search",
data: {ItemName, ItemText},
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
//since data is the HTML partial itself.
//if you have firebug for Mozilla firefox, you can see what I'm
//talking about
You may want to consider converting both strings to upper or lower, depending on your search requirements. The below code assumes the search is case-insensitive.
public ActionResult Search(string search)
{
string upperSearch = search.ToUpperInvariant();
return View("Index", items.Where(n =>
n.ItemName.ToUpperInvariant().StartsWith(search) ||
n.ItemDescription.ToUpperInvariant().Contains(search)));
}
with the Or condition it will match if either the text or the name matches. If they match different items (like Apple and Tart), you may need to consider what happens in that use-case, but in this case it will return both Apple and Pear.

Populate second model in same view in MVC : Prevent postback

I have a page in MVC, where i need to display the details of the records.
The records needed to be fetched from 2 tables for which i have Model separately.
Now, for this page needing both the models, I have created another model which have those 2 model referred.
[Please note, following nomenclature's are only for example purposes.]
public class CombinedModel
{
public Model1 objModel1 { get; set; }
public Model2 objModel2 { get; set; }
}
In the view [Details.cshtml], I have following code:
#model Application.Web.Models.CombinedModel
<div>
#Html.Label("Label text: ", htmlAttributes: new { #class = "control-label" })
#Html.DisplayFor(model => model.objModel1.Property1, new { htmlAttributes = new { #class = "form-control" } })
</div>
And a popup code
<div id="Modal">
<div>
#Html.Label("Popup label text:", htmlAttributes: new { #class = "control-label" })
#Html.DisplayFor(vmodel => vmodel.objModel2.Property2, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
The page loads with the data in from the first model successfully from controller action.
I needed Data in the popup code, only when user clicks on particular record, from where View will send ID and will display record for that particular ID from the second model.
In Controller:
public class ControllerNameController : Controller
{
[HttpGet]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
[ValidateInput(false)]
public ActionResult Details(int? Id, string strType, string strVersionID)
{
var Details1 = db.Table1.FirstOrDefault(rd => rd.SomeID == Id);
CombinedModel modelCombined = new CombinedModel();
Model1 objectM1 = new Model1();
objectM1.Property1 = Details1.Column1;
var VersionDetails = db.Table2.FirstOrDefault(rvd => rvd.somePrimaryKeyID == Convert.ToInt32(strVersionID));
if (VersionDetails != null)
{
Model2 objectM2 = new Model2();
objectM2.vCreatedOn = VersionDetails.Property2;
modelCombined.objModel2 = objectM2;
ViewBag.VersionID = VersionDetails.VersionID;
}
modelCombined.objModel1 = objectM1;
return View(rmodel);
}
}
The page landing URL is:
function JavascriptFunctionInParentView(IDToPass, strTypeToPass)
{
top.location.href = "#Url.Action("Details", "ControllerName")"
+ "?Id=" + IDToPass
+ "&strType='" + strTypeToPass + "'"
+ "&strVersionID='0'";
}
SO, when first time page loads, we have strVersionID as Zero. So, it will not enter in VersionDetails block and fill only Model1 data.
Now, when we are Details page, there is a grid, from which, I need to populate the version details in the popup, for which I have working code as following:
function recordDoubleClickRuleVersion(args) {
top.location.href = "#Url.Action("Details", "ControllerName")"
+ "?Id=" + #Url.RequestContext.HttpContext.Request.QueryString["Id"]
+ "&strType=" + '#Url.RequestContext.HttpContext.Request.QueryString["strType"]'
+ "&strVersionID=" + args.data.VersionID;
}
// ....
$(function () {
if ('#(ViewBag.VersionID)' == "") {
$("#Modal").ejDialog("close");
}
if ('#(ViewBag.VersionID)' != "") {
$("#Modal").ejDialog(
{ enableModal: true, enableResize: false, close: "onDialogClose", width: "60%" });
$("#Modal").ejDialog("open");
}
})
My problem is, when i call this Version details popup, page postbacks and then data comes.. I know i have given #Url.Action to it so it is behaving like this way.
I needed it to be by complete Client-side code and I tried following code as well. It open's popup but doesn't fill value in it.
$.ajax({
type: "GET",
data: ({
"Id": #Url.RequestContext.HttpContext.Request.QueryString["Id"],
"strType": '#Url.RequestContext.HttpContext.Request.QueryString["strType"]',
"strVersionID": args.data.VersionID }),
url: '#Url.Action("RuleDetails", "Rules")',
})
.done(function (RuleVersionDetails) {
// 1. Set popup
$("#Modal").ejDialog(
{ enableModal: true, enableResize: false, close: "onDialogClose", width: "60%" });
// 2. Open popup
$("#Modal").ejDialog("open");
});
Can you please tell me the solution for this ?
You can change you Details() Action to return a Json object, and then fill the dialog with it.
$.ajax({
type: "GET",
data: ({
"Id": #Url.RequestContext.HttpContext.Request.QueryString["Id"],
"strType": '#Url.RequestContext.HttpContext.Request.QueryString["strType"]',
"strVersionID": args.data.VersionID }),
url: '#Url.Action("RuleDetails", "Rules")',
})
.done(function (jsonData) {
// **TODO: file dialog with properties of jsonData**
// 1. Set popup
$("#Modal").ejDialog(
{ enableModal: true, enableResize: false, close: "onDialogClose", width: "60%" });
// 2. Open popup
$("#Modal").ejDialog("open");
});
The best approach for your case is using bootstrap modal.Go here and check its documentation and how to config. If you aren't familiar with bootstrap or modal, it really worth to learn.
But remember when you want sent data to modal section, make it dynamic based on your item id in grid.

Get selected value of Kendo grid on another View

So yesterday I started learning javascript and web development for a project at work. We are using a MVC pattern and I am having issues figuring out exactly how the javascript classes work with the views. Any help on this will be appreciated. Like I said, my knowledge is very limited. I do, however, know C# and WPF (MVVM) so maybe some of that knowledge will help me here.
We use Kendo controls. Some of the javascript for our kendo grid is below.
grid.js:
function onChange(e) {
//get currently selected dataItem
var grid = e.sender;
var selectedRow = grid.select();
var dataItem = grid.dataItem(selectedRow);
var y = $.ajax({
url: "/api/ServiceOrderData/" + dataItem.id,
type: 'GET',
dataType: 'json'
});
}
$("#serviceOrderList").kendoGrid({
groupable: true,
scrollable: true,
change: onChange,
sortable: true,
selectable: "row",
resizable: true,
pageable: true,
height: 420,
columns: [
{ field: 'PriorityCodeName', title: ' ', width: 50 },
{ field: 'ServiceOrderNumber', title: 'SO#' },
{ field: 'ServiceOrderTypeName', title: 'Type' },
{ field: 'ScheduledDate', title: 'Scheduled Date' },
{ field: 'StreetNumber', title: 'ST#', width: '11%' },
{ field: 'StreetName', title: 'Street Name' },
{ field: 'City', title: 'City' },
{ field: 'State', title: 'ST.' },
{ field: 'IsClaimed', title: 'Claimed'}
],
dataSource: serviceOrderListDataSource
});
I am wanting to be able to use the value from the onChange function:
var dataItem = grid.dataItem(selectedRow);
in the following view.
ESRIMapView.cshtml:
<body class="claro">
<div id="mainWindow" data-dojo-type="dijit/layout/BorderContainer"
data-dojo-props="design:'sidebar', gutters:false"
style="width:100%; height:100%;">
<div id="leftPane"
data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'left'">
<br>
<textarea type="text" id="address" />*THIS IS WHERE I WILL USE dataItem! dataItem.StreetNumber (syntax?) to be exact</textArea>
<br>
<button id="locate" data-dojo-type="dijit/form/Button">Locate</button>
</div>
<div id="map"
data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'center'">
</div>
</div>
</body>
Right now my ESRIMapView is loaded when the user clicks on a button on the index.cshtml screen which contains the grid that I am trying to get the value from.
<li>#Html.ActionLink("Map", "ESRIMapView", "Home", null, new { #class = "k-button" })</li>
This is my "Home" controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Services.Description;
using Alliance.MFS.Data.Client.Models;
using Alliance.MFS.Data.Local.Repositories;
namespace AllianceMobileWeb.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult ServiceOrderMaintenance()
{
ViewBag.Message = "Your contact page.";
return View();
}
public ActionResult ESRIMapView()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
I realize this is probably a very elementary question, but any help would be appreciate. And please be as detailed as possible with your responses :)
Since you create your link before returning the (initial) view to the user, you need a bit of trickery to change it. I recommend the following: set an id on your a element and change its href attribute; on your controller, set a parameter corresponding to the street number and pre-fill the view:
Controller:
public ActionResult ESRIMapView(string streetNumber)
{
ViewBag.Message = "Your contact page.";
ViewBag.StreetNumber = streetNumber;
return View();
}
View containing the li (note the Id on the a element):
<li>#Html.ActionLink("Map", "ESRIMapView", "Home", null, new { #class = "k-button", id="myMapaction" })</li>
View containing the textarea (ESRIMapView ):
<textarea type="text" id="address" />#ViewBag.StreetNumber</textArea>
grid.js:
function onChange(e) {
//get currently selected dataItem
var grid = e.sender;
var selectedRow = grid.select();
var dataItem = grid.dataItem(selectedRow);
//change the link
var actionElem = $("#myMapaction");
var url = actionElem.attr("href");
if (url.indexOf("=") === -1) { //first time selecting a row
url += "?streetNumber=" + dataItem.StreetNumber;
} else {
url = url.substring(0, url.lastIndexOf("=") +1) + dataItem.StreetNumber;
}
actionElem.attr("href", url);
//change the link
var y = $.ajax({
url: "/api/ServiceOrderData/" + dataItem.id,
type: 'GET',
dataType: 'json'
});
}
This script simply adds the street number parameter in the query string. When the user selects a row for the first time, the streetNumber parameter is not present in the query string. After the first time, the parameter is there and we must change only the value.
Please note that this solution has its limitations: it does not work if you have other parameters in the query string (the logic for adding/editing the parameter must be changed).

Knockout.js Select value binding to object

I am failing with Knockout select list binding when using an object as a select list value. It works fine if I use a string, but I want to bind objects.
I have a Gift object and it has a Title, Price and Company. I have a select list of companies and each company has an Id and Name. The initial selection however is not the correct in the select list.
Please see fiddle: http://jsfiddle.net/mrfunnel/SaepM/
This is important to me when binding to MVC3 view models. Though I admit it may be because I am doing things the wrong way.
If I have the following model:
public class Company
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class GiftModel
{
public Company Company { get; set; }
public string Title { get; set; }
public double Price { get; set; }
}
How do I select a Company that is bindable in my controller? Do I need to add a CompanyId property to the GiftModel and bind to that or write custom binder. Am I missing something fundamental here?
Thanks in advance.
You need to do a lot of stuff.
An CompanyId in your ViewModel is the only way to bind and make this observable.
You can not make a object observable only it´s values
<form class="giftListEditor" data-bind="submit: save">
<!-- Our other UI elements, including the table and ‘add’ button, go here -->
<p>You have asked for <span data-bind="text: gifts().length"> </span> gift(s)</p>
<table>
<tbody data-bind="foreach: gifts">
<tr>
<td>Gift name: <input data-bind="value: Title"/></td>
<td>Price: $ <input data-bind="value: Price"/></td>
<td>Company: <select data-bind="options: $root.companies, optionsText: 'Name', optionsValue: 'Id', value: CompanyId"/></td>
<td>CompanyId: <span data-bind="text: CompanyId"></span></td>
<td>Delete</td>
</tr>
</tbody>
</table>
<button data-bind="click: addGift">Add Gift</button>
<button data-bind="enable: gifts().length > 0" type="submit">Submit</button>
</form>​
your model
// Fake data
var initialData = [
{ Title: ko.observable('Item 1'), Price: ko.observable(56), CompanyId: ko.observable(1) },
{ Title: ko.observable('Item 2'), Price: ko.observable(60), CompanyId: ko.observable(2) },
{ Title: ko.observable('Item 3'), Price: ko.observable(70), CompanyId: ko.observable(2) }
];
var initialCompanies = [
{ Id: 1, Name: "Comp 1" },
{ Id: 2, Name: "Comp 2" },
{ Id: 3, Name: "Comp 3" }
];
var viewModel = {
gifts: ko.observableArray(initialData),
companies: initialCompanies,
addGift: function() {
this.gifts.push({
Title: "",
Price: "",
Company: { Id: "", Name: "" }
});
},
removeGift: function($gift) {
viewModel.gifts.remove($gift);
},
save: function() {
console.log(ko.toJS(viewModel.gifts));
}
};
ko.applyBindings(viewModel, document.body);​
To make an object observable, use the foeach binding. If you have such a scenario:
var model = {
myObj : ko.observable();
}
if you try to bind to myObj.label it won't work:
<span></span>
however, using the foreach binding:
<span data-bind="foreach: myObj"></span>
ko iterates through the properties as it would through an array in the usual javascript manner and things will work.

Categories

Resources