I am using the MVC version of the Telerik controls with ASP.NET MVC and the razor view engine. I have an AJAX grid. When I click edit I want it to display the data in form style. But the issue is that I want to rearrange the in form controls they way that I want them to display. How would I do something like that? Currently the controls are all beneath each other. I want to create my own layout for editing.
I have a whole lot of other controls on the view, one of them being this grid. My view model object has a list of Children objects and I want to use my grid to populate this list.
The view model for my view:
public class EditGrantApplicationViewModel
{
public string EmployeeNumber { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Other properties
// I want this to be populated from the grid
public IEnumerable<Children> Children { get; set; }
}
My grid's code for the Children list:
#(Html.Telerik().Grid(Model.Children)
.Name("grdChildren")
.Columns(column =>
{
column.Bound(x => x.Id);
column.Bound(x => x.FullName);
}
)
.DataKeys(keys =>
{
keys.Add(x => x.Id);
}
)
.DataBinding(dataBinding =>
{
dataBinding.Ajax()
.Select("_SelectAjaxEditing", "Grid")
.Insert("_InsertAjaxEditing", "Grid")
.Update("_SaveAjaxEditing", "Grid")
.Delete("_DeleteAjaxEditing", "Grid");
}
)
.ToolBar(commands => commands.Insert().ButtonType(GridButtonType.Text))
.Editable(editing => editing.Mode(GridEditMode.InForm))
)
I'm not sure how my editor template must look like? What must it extend? And I can't get it to show in the inline form. I worked through the sample from Brad Wilson but I am not getting it. Can someone please explain what is happening?
Just another questions.. On my other page I have a grid with other HTML controls on the page. If I am busy editing data in the grid, and click insert, how would I prevent the other controls on the page not to be validated?
You can define a custom editor template for your model and arrange the fields as you wish. This code library project shows how.
Related
I am working with a Kendo Chart using the MVC wrappers which I need to add series to at runtime based on data in my Model
I have verified that my model does contain valid data when the chart is about to be rendered
The view's model is a chart widget object which contains a series list
This is simply a list of chart series
Budget Chart Widget
namespace STC.Widgets.Budgeting
{
using System;
using System.Collections.Generic;
using Budget;
using Extensions;
using Helpers;
public class BudgetChartWidget
{
public BudgetChartWidget()
{
SeriesList = new List<IBudgetChartSeries>();
}
public List<IBudgetChartSeries> SeriesList { get; set; }
public string ChartTitle { get; set; }
}
}
**Budget Chart Series**
namespace STC.Widgets.Data.Budgeting
{
using System.Collections.Generic;
public class BudgetChartSeries : IBudgetChartSeries
{
public BudgetChartSeries(string seriesName)
{
Values = new List<IChartValue>();
SeriesName = seriesName;
}
public string SeriesName { get; set; }
public List<IChartValue> Values { get; set; }
}
}
Each chart series then contains values
IChartValue
namespace STC.Widgets.Data.Budgeting
{
using System;
public interface IChartValue
{
string DisplayValue { get; set; }
DateTime Period { get; set; }
double? Value { get; set; }
}
}
Partial View for chart
#using STC.Widgets.Data.Budgeting
#model STC.Widgets.Budgeting.BudgetChartWidget
<div>
#(Html.Kendo().Chart(Model.SeriesList)
.Theme((string) ViewBag.ThemeName)
.Name("BudgetViewer" + 1)
.Series(series =>
{
foreach (var item in Model.SeriesList)
{
series.Column(item.Values).Field("Value").CategoryField("DisplayValue").Name(item.SeriesName);
}
})
.Legend(legend => legend.Position(ChartLegendPosition.Bottom).Visible(true))
.ValueAxis(axis => axis.Numeric()
.MajorGridLines(lines => lines.Visible(true))
.NarrowRange(true)
.Labels(labels => labels.Format("{0:N0}"))
.Line(line => line.Visible(true))
.Crosshair(crosshair => crosshair.Visible(true)
.Tooltip(t => t.Visible(true)))
)
.CategoryAxis(axis => axis
.Labels(labels => labels.Rotation(-90))
.MajorGridLines(lines => lines.Visible(false))
.Crosshair(crosshair => crosshair.Visible(true)
.Tooltip(t => t.Visible(true)))
)
.Tooltip(tooltip => tooltip.Visible(true).Format("{0:N0}").Shared(true)))
</div>
When I run this the series are shown in the bottom legend but no data shows in the graph
I have checked and there are no Javascript errors
When I use Internet Explorer to view the source of the chart I can see that the data is there
I cant see anything wrong with the way I have created each series, I have even tried varying ways of passing the parameters.
The only one that is relevant to my situation is the one I am using
Can anyone see if I have missed something really obvious please?
Paul
I just had a similar issue, in my case the chart was not shown initially but when I clicked on the legend the chart was shown as expected. After inspecting it seems that the chart was not resizing correctly initially.
<div class="chart-wrapper">
#(Html.Kendo().Chart(...)
.....
</div>
$(document).ready(function () {kendo.resize($(".chart-wrapper"));}); seem to fix this.
Following is my model property
[Required(ErrorMessage = "Please Enter Short Desciption")]
[StringLength(200)]
public string ShortDescription { get; set; }
And following is my corresponding View code
#Html.TextAreaFor(model => model.Product.ShortDescription, new { cols = "50%", rows = "3" })
#Html.ValidationMessageFor(model => model.Product.ShortDescription)
And this is how it shows in the browser, the way i want.
Now, since there is a bug in Microsoft's MVC3 release, I am not able to validate and the form is submitted and produces the annoying error.
Please tell me the work around or any code that can be substituted in place of TextAreaFor. I can't use EditorFor, because with it, i can't use rows and cols parameter. I want to maintain my field look in the browser. Let me know what should be done in this case
In the controller action rendering this view make sure you have instantiated the dependent property (Product in your case) so that it is not null:
Non-working example:
public ActionResult Foo()
{
var model = new MyViewModel();
return View(model);
}
Working example:
public ActionResult Foo()
{
var model = new MyViewModel
{
Product = new ProductViewModel()
};
return View(model);
}
Another possibility (and the one I recommend) is to decorate your view model property with the [DataType] attribute indicating your intent to display it as a multiline text:
[Required(ErrorMessage = "Please Enter Short Desciption")]
[StringLength(200)]
[DataType(DataType.MultilineText)]
public string ShortDescription { get; set; }
and in the view use an EditorFor helper:
#Html.EditorFor(x => x.Product.ShortDescription)
As far as the rows and cols parameters that you expressed concerns in your question about, you could simply use CSS to set the width and height of the textarea. You could for example put this textarea in a div or something with a given classname:
<div class="shortdesc">
#Html.EditorFor(x => x.Product.ShortDescription)
#Html.ValidationMessageFor(x => x.Product.ShortDescription)
</div>
and in your CSS file define its dimensions:
.shortdesc textarea {
width: 150px;
height: 350px;
}
I'm trying to bind a list of model objects to a grid using the MvcContrib Grid helper. Obviously, emitting an HTML table is easy enough, but I'm struggling with returning all the selected rows (or all rows and filtering via a Where(x => x.Selected)).
Here's a dummy version of what I mean:
Model:
public class Player
{
[ScaffoldColumn(false)]
public int Id { get; set; }
public string Name { get; set; }
public int JerseyNumber { get; set; }
public string Position { get; set; }
[ScaffoldColumn(false)]
public bool Selected { get; set; }
}
View:
#model democode.Models.Player
#using (Html.BeginForm())
{
#{
var grid = Html.Grid(Model)
.AutoGenerateColumns()
.Columns(c => c.For(p => Html.CheckBoxFor(_ => p.Selected)).InsertAt(0))
.Columns(c => c.For(p => Html.HiddenFor(_ => p.Id)))
grid.Render();
}
<p>
<input type="submit" value="Submit" />
</p>
}
So, what you are looking at is a grid of hockey players with a checkbox before each one, allowing the user to select one or more. On clicking submit, I'd like for it to post the collection back (understanding that all but Selected and Id would be null/default), but I understand the problem is that the records coming across in the POST data have overlapping keys in the list of key-value pairs.
I have successfully worked around this in the past by hand-writing the HTML table and using the strategy Phil Haack outlines here: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
My question is, can I do the same thing using the Grid helper from MvcContrib, or is it more work than what it's worth?
I want to get some options (say payment method cash, credit card etc.) and bind these to radio buttons. I believe there is no RadioButtonList in MVC 3.
Also, once radios are bound I want to show the previously selected option to the user while editing the answer.
As always you start with a model:
public enum PaiementMethod
{
Cash,
CreditCard,
}
public class MyViewModel
{
public PaiementMethod PaiementMethod { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view:
#model MyViewModel
#using (Html.BeginForm())
{
<label for="paiement_cash">Cash</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "Cash", new { id = "paiement_cash" })
<label for="paiement_cc">Credit card</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "CreditCard", new { id = "paiement_cc" })
<input type="submit" value="OK" />
}
And if you want some more generic solution which encapsulates this in a helper you may find the following answer helpful.
This is how I like to bind RadioButtonLists. The view model has a collection of my strongly typed objects. For example, maybe PaymentOptions is a code table. Along with the collection is a SelectedPaymentOptionKey (or Selected*Id if you prefix your primary keys with Id). Initially this key will just be default 0, but on postback, it will hold the value of the selected item.
public class PaymentSelectionVM
{
public ICollection<PaymentOption> PaymentOptions { get; set; }
public int SelectedPaymentOptionKey { get; set; }
}
public ViewResult PaymentSelection()
{
var paymentOptions = db.PaymentOptions.ToList();
return View(
new PaymentSelectionVM {
PaymentOptions = paymentOptions,
//This is not required, but shows how to default the selected radiobutton
//Perhaps you have a relationship between a Customer and PaymentOption already,
//SelectedPaymentOptionKey = someCustomer.LastPaymentOptionUsed.PaymentOptionKey
// or maybe just grab the first one(note this would NullReferenceException on empty collection)
//SelectedPaymentOptionKey = paymentOptions.FirstOrDefault().PaymentOptionKey
});
}
Then in the View:
#foreach (var opt in Model.PaymentOptions)
{
#*Any other HTML here that you want for displaying labels or styling*#
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey)
}
The m.SelectedPaymentOptionKey serves two purposes. First, it groups the Radio buttons together so that the selection is mutually exclusive(I would encourage you to use something like FireBug to inspect the generated html just for your own understanding. The wonderful thing about MVC is the generated HTML is fairly basic and standard so it shouldn't be hard for you to eventually be able to predict the behavior of your views. There is very little magic going on here.). Second, it will hold the value of the selected item on postback.
And finally in the post handler we have the SelectedPaymentOptionKey available:
[HttpPost]
public ActionResult PaymentSelection(PaymentSelectionVM vm)
{
currentOrder.PaymentOption = db.PaymentOptions.Find(vm.SelectedPaymentOptionKey);
....
}
The advantage of this over using SelectListItems is you have access to more of the object's properties in the case that you are displaying a grid/table and need to display many values of the object. I also like that there are no hard coded strings being passed in the Html helpers as some other approaches have.
The disadvantage is you get radio buttons which all have the same ID, which is not really a good practice. This is easily fixed by changing to this:
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey, new { id = "PaymentOptions_" + opt.PaymentOptionKey})
Lastly, validation is a bit quirky with most all of the radio button techniques I've seen. If I really needed it, I would wire some jquery up to populate a hidden SelectedPaymentOptionsKey whenever the radio buttons are clicked, and place the [Required] or other validation on the hidden field.
Another workaround for the validation problem
ASP.NET MVC 3 unobtrusive validation and radio buttons
This looks promising but I haven't had a chance to test it:
http://memoriesdotnet.blogspot.com/2011/11/mvc-3-radiobuttonlist-including.html
You should bind your options to SelectList in ViewModel and set Selected attribute to true for previously selected option
If i have a ViewModel like this:
public class SignupViewModel
{
[Required]
[DisplayName("Email:")]
public string EmailAddress { get; set; }
}
And use EditorFor to render out the form fields:
#Html.EditorFor(model => model.EmailAddress )
It will render <input type="text">. Cool.
But in this particular scenario, i have already retrieved Email from a different source, and i wish to pre-fill the form with this data, and show a label instead of a textbox (as i don't want them to change their email - don't worry about why).
I know i can use [UIHint], but can i do that programatically from the controller?
E.g:
var model = new SignupViewModel();
model.EmailAddress = GetFromMysterySource(); // How do i set a UIHint?
What's the best way to approach this? Should i use a seperate ViewModel altogether, which could mean changing my View from being strongly-typed to being dynamic, or should i not use EditorFor, or should i use a custom editor template?
Suggestions/advise would be greatly appreciated.
You can't apply an attribute at runtime. My suggestion would be to build a bit of logic into your view to control how the view renders the data. You may need to augment your model to indicate to the view which display to choose.
#if (Model.EmailAddressIsFixed)
{
#Html.DisplayFor( m => m.EmailAddress )
#Html.HiddenFor( m => m.EmailAddress ) // only if you need it to post back
}
else
{
#Html.EditorFor( m => m.EmailAddress )
}
If you are doing this in more than one place, then a custom editor template doing the same thing would probably be in order.
#Html.EditorFor( m => m.EmailAddress,
"FixedAddressTemplate",
new { Fixed = Model.EmailAddressIsFixed } )