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.
Related
I am unable to show the CKEditor UI, for reasons unknown at some places. In one view, it works great.. in another it does not, but uses the exact same code as the one that works. Allow me to clarify:
I am going database first, and connecting an ADO.NET Entity Data Model to a database I have running. Then I made a controller, lets call it "news" with the MVC CRUD template, linking it to my model (database). From here the views are compiled for me, and under the "Create" view I would introduce:
<div class="editor-field">
#Html.TextAreaFor(model => model.content, new { #class="ckeditor", #id="ckeditorbox"})
#Html.ValidationMessageFor(model => model.content)
</div>
where model.content is the column name in the database (read/write to DB works, so this isn't really the issue anyway). This appears as a blank space in my browser. Inspecting the page with Google Chrome I see that it gets a visibility:hidden attribute for some reason.
Now
If I were to make a new controller with no template (Empty MVC controller) and post the exact same code from the Create.cshtml view into my newly created Index.cshtml then CKEditor would display just fine. Anyone got an idea as to why this is?
I was thinking this was an issue with the ckeditor.js script not being initialized, because the initializing is placed in _Layout.cshtml as:
<script src="#Url.Content("addons/ckeditor/ckeditor.js")" type="text/javascript"></script>
However placing this in the Create.cshtml view does not work either.
Suggestions are greatly appreciated
As to why CKEditor did not appear on particular pages, I am not sure. The fix for me however was to set:
<script type="text/javascript">
CKEDITOR.replace('x');
CKEDITOR.editorConfig = function (config) {
ignoreEmptyParagraph = true;
};
in the view
I'm using AngularJS in my MVC application and trying to use the best of both worlds. I really like how you can in MVC set up your validation logic in your viewmodels and generate client side validation with jQuery validation in your razor views with little effort. I use AngularJS to get the SPA behavior with routing etc, but when I create a razor view that I use to inject into a page with:
<div data-ng-view="data-ng-view"></div>
then the jQuery validation stops working, even though the script files is references on the page where the view is injected. See below for my razor view:
#model BandViewModel
<div data-ng-controller="App.BandCreateCtrl">
<form name="startBandForm">
#Html.ValidationSummary(true)
<br />
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name, new { data_ng_model = "band.Name" })
#Html.ValidationMessageFor(m => m.Name)
<br/>
<input data-ng-disabled="startBandForm.$invalid" type="submit" data-ng-click="createBand(band)" value="Create"/>
</form>
</div>
First of all, IMO using Razor to render your templates is fraught with peril, at best. Generally you want to use static HTML for your page and directive templates, and then retrieve and post data as AJAX to your API. The ASP.NET web API is really good at this, actually. If your underlying model has validation, then bad web API calls will throw an exception, which you can catch in your $http or $resource handler and show to the user. Mixing standard HTTP form posts with AngularJS will be... difficult.
To achieve what you want, I think someone (not me) would have to write the AngularJS equivalent to jQuery Unobtrusive Validation library, which itself is using the jQuery Validate plugin. Not trivial. I personally don't see drop-in Angular validation happening soon, especially since it has more to do with markup and less to do with a JS configuration object.
Possibly you could re-initialize the validation when the view has finished loading using the $viewContentLoaded event. But please don't.
It pains me that everywhere I've looked we get the same replies: "HTML should just be a template". Perhaps, but I'm just not ready to delegate everything to JavaScript
Instead of using the anonymous object for passing the HTML attributes, try using the Dictionary
#Html.TextBoxFor(m => m.Name, new Dictionary<string, object>(){{ "data_ng_model", "band.Name" }})
Make sure is
Dictionary<string, object>
And not
Dictionary<string, string>
Otherwise the constructor will confuse it for
object HtmlAttribute
Not as pretty though... but it works for the most part.
Lastly, take in mind that if you include AngularJS after jQuery, then it will use jQuery for selectors.
To anyone still looking for a solution to this problem , there is a simple way to make it work.
var currForm = $("#myForm");
currForm.removeData("validator");
currForm.removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(currForm);
currForm.validate();
You could put this code in $viewContentLoaded or other places which are relevant in your code.
While I understand some of the views here which discourage MVC views being used as a templates for your Angular code. My reason being its not not a natural way of doing things in angular and hence there are chances that you will run into issues like this one. But if you benefit from using MVC views as templates its not a path to hell. IT works and can benefit you as well. I have 2 use cases in my project where using MVC views make a lot of sense.
My project actually has a requirement where a client can turn of the required validation on certain field , for this I use the MVC custom data annotation validator which allows me to add or remove the data annotation at the time of rendering the view using a single class , if you were to build the same flexibility in angular , you would have to write a lot more code. So MVC views work great here for me as Custom Data Annotations are triggerd by the DisplayFor and EditorFor helpers
We are heavily invested in Internationalization and while angular is great at other stuff internationalization is not its strong suite ( at the time of writing this atleast )I still feel the internationalization support in MVC with .RESX works great and has been here long . This again uses the data annotation power of the DisplayAttribute.
TLDR : MVC views as templates may give you a few unexpected problems but are worth it if you are really using the full power of the data annotation pipeline created by Microsoft.
Hopefully someone comes up with a Angular HTML Helpers library in the future this would be much simpler.
Maybe using Angular for validation can be less painful than you think. Using a couple of custom directives, you could easily get something quite close to Razor with markup as simple as:
<validated-input name=username display="User Name" ng-model=model.username required</validated-input>
Check out my sample here and its inspiration here. It only implements required and number at the moment, but customizing it should be easy.
You can use ngval library. It brings data annotations to client side as angularjs validation directives.
I've found myself working on a .Net MVC3 project and sorely missing having Formtastic available in my rails apps.
Are there any good .Net MVC replacements for Formtastic?
I suggest using MVC3s HTML Helpers.
#model mynspace.MyModel
#Html.EditorForModel()
This html helper generates editor fields for every field in your model, choosing the most likely option. Editing the Editor template, you can change the way it is layed out as well. See this question on changing the template
It can of course be used more customizable by each field with things like:
#Html.Label("Daily Rate")
#Html.EditorFor(model => model.DailyRate)
#Html.ValidationMessageFor(model => model.DailyRate, "Please enter a valid Daily Rate (2 d.p.)")
This can be custom layed out by putting tags around it, i.e. things like <p> or <td>
You can also force an input type using options like:
#Html.TextBoxFor(model => model.DailyRate)
#Html.CheckBoxFor(model => model.DailyRate)
#Html.DropDownBoxFor(model => model.DailyRate, ViewBag.DailyRates)
see here for a full list of options
I have a project called FormFactory that lets you do much of the stuff formastic does http://formfactory.apphb.com/
It transforms model objects into PropertyVm objects, which are a viewmodel for an input basically. You could manually create the PropertyVms if you want.
I have to implement pagination, number of Items per page and sorting for my MVC3 application.
In my view page I have a List of Items that is coming from the Model. I will be displaying a number of Items (defined by the user) each inside div, per each page. I'm not building a table.
I know that mvccontrib can build a table with paging, and not sure if that pagination can be styled at all, cause I have to style mine. What is a good approach to solve this?
Check out PagedList (available as a nuget package). I found it really mvc-friendly so far.
For server side paging, I use https://github.com/dvsspr/Halaman.
It is not documented yet, but it is very flexible and it uses fluent configuration.
One very simple use case for you.
#Controller
public ActionResult Index(AdminPageInfo page, SortInfo sort)
{
PagedData<Account> model = accountRepository.Get().AsPagedData(page, sort);
return (model.IsValid) ? View(model) : View("_Error");
}
#View
#model PagedData<Account>
#{
ViewBag.Title = "Account Index";
}
<p>
Total records: <strong>#Model.TotalRows</strong>
<br />
Total pages: <strong>#Model.TotalPages</strong>
<br />
Total records in current page: <strong>#Model.TotalRowsInPage</strong>
</p>
#Html.Pager(Model)
#Html.Sizer(Model) #* Page size changer helper =) *#
#*
Put a dot after the closing brace and you will see the available options.
*#
<h3>Sorter</h3>
<li>
<ul>#(Html.SortLink<Account>(zz => zz.Id, Model, "User Id"))</ul>
<ul>#(Html.SortLink<Account>(zz => zz.UserName, Model, "User Name"))</ul>
<ul>#(Html.SortLink<Account>(zz => zz.FullName, Model, "Full Name"))</ul>
</li>
#foreach (var acc in Model) {
<p>
User name: #acc.UserName
<br />
Full name: #acc.FullName
</p>
}
That's it.
IMHO, the codebase can be hacked easily - just in case you want to implement hybrid paging.
With plain HTML, you can use the Webgrid help in WebMatrix. It has paging support out of the box with MVC.
http://msdn.microsoft.com/en-gb/magazine/hh288075.aspx
Otherwise, any of the major Javascript components (jqgrid, extjs, etc.) can do paging, all depending on what your model source is. If you implement an AJAX datasource, then you need to pass the paging properties back and forth, so the paging can be done server-side efficiently (depending on your database backend).
If you're looking for a primarily client-side solution, I have never had any problems with jQuery's Tablesorter with Paginator plugin: http://tablesorter.com/docs/example-pager.html
If you want to do this server side, the general approach is that you are going to cache the entire result-set server side*, then make (ajax or otherwise) calls to the server specifying which records are needed. You'll pass a starting index and a number of records to return via querystring or form field (as well as some identifier for the query to make sure you're getting the right result set).
*As mrjoltcola points out in the comments, to cache or not to cache is a case by case issue, for larger result sets it may make more sense to make more db calls with smaller result sets. An alternate solution to the big-result-set problem is capping their result-set size and displaying a message advising them to make their query more specific if the threshold is exceeded. I would advise going with your best judgement initially and optimizing if needed.
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...