I am tinkering with the MVCContrib Grid and am stuck on how to format a row of data in the grid based on the data.
For example, say we have a grid of products, where each product has data fields like name, price, and discontinued. I'd like to highlight all product rows that are discontinued.
One workaround would be to use jQuery on the client-side to apply a CSS class to those rows where the discontinued cell is TRUE, but that seems like a brittle solution. I'm hoping there's a way to do it on the server-side via the Html.Grid method call.
Thanks
Hello Scott: Try something like the following to add RowAttributes -
#Html.Grid(Model)
.WithModel(new CustomerGridModel())
.Sort(ViewData["sort"] as GridSortOptions)
.Attributes(id => "grid", style => "width: 100%;")
.RowAttributes(data => new MvcContrib.Hash(
#class => data.Item.Discontinued ? "discontinued" : ""))
This will add a class attribute to the tr element. Then, create a class along the lines of:
tr.discontinued td {background-color: red;}
Sorry for the long code snippet...
Related
I am trying to implement a very simple grid with ability to add a new row via a toolbar button. None of the samples provided in the documentation seem to work as expected.
#model Models.MyModel
#{
ViewBag.Title = "Simple Grid";
}
<div class="container-fluid">
<div class="row"></div>
<div class="row">
<div class="col-xs-11">
#(Html.Kendo().Grid<Models.MyModel>()
.Name("myGrid")
.ToolBar(toolbar => {
toolbar.Create().Text("Add New Row");
})
.Columns(columns => {
columns.Bound(p => p.Name).Width(200);
columns.Bound(p => p.Header1).Width(100);
columns.Bound(p => p.Header2).Width(100);
})
.Scrollable()
.Editable(ed => ed.Mode(GridEditMode.InLine).CreateAt(GridInsertRowPosition.Bottom))
.HtmlAttributes(new { style = "height:350px;" })
.DataSource(ds => ds
.Custom()
.Schema(schema => schema
.Model(model => {
model.Id("Id");
model.Field("Name", typeof(string));
model.Field("Header1", typeof(string));
model.Field("Header2", typeof(string));
})
)
)
)
</div>
</div>
</div>
The above is in a simple index.chtml page which uses a layout page and injects all the jQuery and KendoUI scripts.
When the page is loaded get a JS error Unable to get property 'nodeName' of undefined or null reference
Not sure why that happens, but hitting continue, displays an empty grid as expected.
Clicking the "Add new Row" toolbar button results in another JS error (same as above)
Question:
Am I missing a configuration option on the grid? Per documentation, this is supposed to work "out of the box". All I want to achieve is a simple grid which adds a new empty row everytime I click the "Add" button.
While it can be a bit tough to see at first, using the Custom() configuration requires more than just setting the .Schema() configuration. You can refer to samples in in the Custom DataSource documentation article for references to this. Every sample has at least the Transport field defined with a read configuration set so that the DataSource knows where to read the data.
Since you're doing CRUD operations here I also recommend that you set the other transport fields (Create, Read, Update, Destroy) to ensure that you can work with your controller and communicate with your backend properly.
As a quick note, if you search around the Telerik site for the error unable to get property 'nodeName' of undefined or null reference you start to get the hint that this error is due to an incorrect configuration of a component. It's not specific to the Grid, but it does pop up from time-to-time and the issue almost always boils down to configuration options.
Now, to fix this particular issue I'd recommend working with Ajax binding rather than custom binding. Ajax binding is super simple to work with and is perfect for this kind of scenario where you want a simple Grid added to the page without the more advanced configuration that comes from working with a more advanced feature. Plus you can work with your client-side model to set up validation etc., for your server-side (no need to set the schema manually).
Or, alternatively just set the transport field configuration to valid ActionResults or URLs to push information back and forth properly.
When first implementing any new product I always recommend following documentation closely and starting off with the smaller examples and building form there. If you're looking to work with a custom data source then I say start with a bigger configuration and remove pieces until you get the minimal piece that you're looking for.
I'm creating an MVC app. One of the pages requires a field containing a dynamic list of checkboxes. The actual number of checkboxes to display is controlled by the number of rows in a SQL table. Each row will be a checkbox. I've figured out how to do this and display them on the page using the following code. This example contains a dynamic list of currencies, with each currency represented by a checkbox. This example displays a label next to each checkbox describing what the currency is (e.g., USD). However, what's really wanted is to display an image (which would be contained in the model as a byte[] type) instead. I'm not sure how to accomplish this since MVC doesn't support anything like an #Html.ImageFor. This might be simpler than I'm making it out to be but I can't figure out how to place an image where the #Html.LabelFor current resides.
#for (int i = 0; i < Model.CurrencyList.Count; i++)
{
<div>
#Html.HiddenFor(x => x.CurrencyList[i].AdvertiserCurrencySupportId)
#Html.CheckBoxFor(x => x.CurrencyList[i].Checked)
#Html.LabelFor(x => x.CurrencyList[i].Checked, Model.CurrencyList[i].Currency)
</div>
}
You should just be able to use a basic image tag, inserting data where needed
<img src="#Model.CurrencyList[i].ImageUrl">
Since you've got the byte array, looks like you want to use inline images, so you should be able to do that with plain HTML:
<img src="data:image/png;base64,#Convert.ToBase64String(Model.CurrencyList[i].ImageBytes)">
But you're probably better off with a plain image pointing to a URL, since inline images can eat up a lot of bandwidth since you're pulling them every time the page loads, so you don't get any browser caching.
I need to show a drop down list with ability to select multiple items. I am able to bind this with a Dictionary object and it works fine with plain text values. However, i need to include img tags as well in the values.
This is basically a list of people along with their public profile picture. However, the list shows the html tags instead of showing the actual image.
Here is the razor code in my view:
#Html.ListBox("MyFriends", new MultiSelectList(Model.Connections.Distinct(), "key", "value"), new { #class = "list-item"})
The output is shown as:
<img src="http://localhost/api/person/1211/dp" title="avatar" /> John Smith
<img src="http://localhost/api/person/1212/dp" title="avatar" /> Person Two
<img src="http://localhost/api/person/1213/dp" title="avatar" /> Person 3
<img src="http://localhost/api/person/1214/dp" title="avatar" /> Rick Rude
But i want to actually show display picture followed by the name. Any solution?
The problem you are facing is not strictly connected with Razor or even with ASP.NET MVC.
It is connected with the fact that DOM prohibits you from placnig an tag directly inside tag (so it's an HTML-related constraint).
There is a possibility to use CSS styling to add images to your select list:
How to add a images in select list
This will though most probably not solve your problem because the styles are added statically, whereas your list is dynamic.
All in all I think you should consider using some alternative for Html.Listbox like for example jQuery Combobox.
I wonder if there way to customize td inner HTML of grid, for example I need to bound some field, and make clickable.
Didn't found any other way, except adding onclick attribute.
Columns(column => column.For(x => x.GotToPursue).Attributes(#onclick => "align-right")
Any suggestions?
The only way to this I've found is to write your on extension method, working with already formed innerhtml to change it.
I need to be able to edit a table of data in the browser.
I have seen in MVCContrib there is a HTML helper to render out a table. Useful... but what about if I want the user to be able to edit that table? From what I can see it does not help there.
What is the best way to approach this?
Traditional FORM with a TABLE inside? If so is MVC smart enough to parse the posted data back into a collection of rows? How would that work anyway?
Or perhaps it should just switch to edit mode when a row is clicked (using javascript etc) then when user moves to a different row an AJAX action is called to submit just the one row. I can imagine the logic could get complex here - this would presumably still use a form but would I have to insert it into the DOM dynamically?
I also need to be able to add rows to this table. I do not require paging support.
Is there an off the shelf solution out there?
Should I go back to web forms? :)
Take a look at Phil Haack's blog where he describes how to model bind to a list.
Maybe this can help?
I've got the same problem, and I have found a solution for it. Don't think it's the most beautiful, but it's ideal for me, because one of my requirements was be able to edit table data using jQuery's Jeditable plugin.
So I generate a table using MVCContrib's Grid<> extension:
Html.Grid<Somenamespace.Line>( Model.InvoiceLines )
.Attributes( id => "InvoiceGrid" )
.Columns( column => {
column.For( li => li.LineItem.ItemDescription ).Attributes( name => ".LineItem.ItemDescription", #class => "click" );
column.For( li => li.LineItem.InvoiceUnitNetPrice ).Named( "Unit net price " ).Attributes( name => ".LineItem.InvoiceUnitNetPrice", #class => "click" );
column.For( li => li.LineItem.InvoiceQuantity ).Attributes( name => ".LineItem.InvoiceQuantity", #class => "click" );
})
.Render();
//rest of the code
Html.Submit("_submit", "Save");
Right now You can edit in place values, but it doesn't upgrade corresponding model.
All the magic happens after user clicks submit button:
$(document).ready(function() {
$('#_submit').click(function(e) {
e.preventDefault();
$('#InvoiceGrid tbody tr').each(function(index) {
var hidden = $('<input />').attr({ type: 'hidden', name: 'InvoiceLines.Index', value: index });
$(this).children('td:first-child').before(hidden);
$(this).children('td:not(:first-child)').each(function() {
$(this).append($('<input />').attr({ type: 'hidden', value: $(this).text(), name: 'InvoiceLines[' + index + ']' + $(this).attr('name') }));
});
});
$('form').submit();
});
//editable stuff
$('.click').editable(function(value, settings) {
return (value);
}, { submit: 'OK' });
});
In every TD I create hidden input, with value from that TD, in every row input with Index, and the most important here is 'name' attribute: Name of collection in Model[here goes index].rest.of.path, so in this particular case (example):
InvoiceLines[2].LineItem.ItemDescription
Hope it'll help, because rich grid isn't always an answer ;)
Regards
Mateusz
I would checkout one of the javascript UI libraries first:
ExtJS Grid
Yahoo DataTable
Flexigrid
WebForms are easier when it comes to quickly developing rich UI's like editable grids.
Last night I implemented a simple solution: form + table inside, using input fields in the cells with naming convention as described in Phil Haack's blog (thanks to #BengtBe for link).
It's working but its a bit fiddly (e.g. adding rows with jquery requires me to work out the next unused index).
So I am still looking for more solutions.
One I have discovered is the extjs library which provides a very rich grid. I have yet to work out whether there is an easy way to post back the grid data to one of my controller actions yet though...