I'm fairly new to mvc, and have started learning asp.net mvc 5 and django
I want to create an application where the user can create a new view at runtime. So lets say I create a feature in the web app for a user to add a new page where they can fill out a form, say the title maybe text, or fields they want to display on the view, and when the user saves it that info gets saved to the db and creates a new view.
My questions are:
can you create dynamic views at runtime?
how do you create the proper url to route to that new page?
if the 1st two are possible can you use a model or viewModel to then display the content from the db for that page?
Any advice on if this can be done would be appreciated. Thanks
I think you need to make a page that saves the user configuration in db as per user demand.
From my end, I suggest the following approach to do it.
Whatever you gets the data from database which are returns like as below snap.
Make one Action in controller and assign those data in one datatable/list in action.
public ActionResult LoadContent()
{
dynamic expando = new ExpandoObject();
var model = expando as IDictionary<string, object>;
/*Let say user insert the detail of employee registration form. Make the
database call and get the distinct detail of particular inserted form by Id
or whatever. As an example below datatable contains the data that you fetch
during database call.*/
DataTable objListResult =
HeaderViewActionHelper.GetFinalResultToRenderInGenericList(id);
if (objListResult != null && objListResult.Rows.Count > 0)
{
foreach (DataRow row in objListResult.Rows)
{
model.Add(row["DisplayName"].ToString(),
row["DisplayNameValue"].ToString());
/*If you want to handle the datatype of each field than you can bifurcation
the type here using If..else or switch..case. For that you need to return
another column in your result from database i.e. DataType coloumn. Add in
your model as, model.Add(row["DisplayName"].ToString(),
row["DisplayNameValue"].ToString(), row["DataType"].ToString());*/
}
}
/* return the model in view. */
return View(model);
}
In the view you can go through the loop over modal and render the detail.
#model dynamic
#using (Html.BeginForm("SubmitActionName", "ControllerName")
{
<div class="table-responsive">
<table>
#if (Model != null)
{
foreach (var row in Model)
{
}
}
</table>
</div>
}
For more detail, Please go through this link.
Related
This is probably a very simple problem, but I am extremely new to C# / MVC and I have been handed a broken project to fix. So it's time to sink or swim!
I have an array of strings that is being passed from a function to the front end.
The array looks something like
reports = Directory.GetFiles(#"~\Reports\");
On the front end, I would like it to display each report, but I am not sure how to do that.
This project is using a MVC, and I believe the view is called "Razor View"? I know that it's using an HTML helper.
In essence, I need something like
#HTML.DisplayTextFor(Model.report [And then print every report in the array]);
I hope that makes sense.
If you want to display the file name array you can simply use a foreach:
#foreach(var report in Model.Reports){ #report }
Note that you should add the Reports property to your view model:
public class SampleViewModel
{
public string [] Reports { get; set; }
}
You could use ViewData or TempData but I find that using the view model is the better way.
You can then populate it:
[HttpGet]
public ActionResult Index()
{
var model = new SampleViewModel(){ Reports = Directory.GetFiles(#"~\Reports\")};
return View(model);
}
And use it in the view as you see fit.
Here is a simple online example: https://dotnetfiddle.net/5WmX5M
If you'd like to add a null check at the view level you can:
#if(Model.Reports != null)
{
foreach(var report in Model.Reports){ #report <br> }
}
else
{
<span> No files found </span>
}
https://dotnetfiddle.net/melMLW
This is never a bad idea, although in this case GetFiles will return an empty list if no files can be found, and I assume a possible IOException is being handled.
I have two different pages, from which a user can click on a 'details' link and go to the details page.
On the details page, I have a 'back' button, which leads the user to the originating page, being one of the two original pages of course.
There is also one extra issue: in one of the return links, I must specify an extra anonymous object.
my view code right now is:
#{
MvcHtmlString backLink = null;
if (Model.ReturnPage == MatchResultReturnPage.Search)
{
backLink = Html.ActionLink("GoBack", "Search", new {search = true});
}
else
{
backLink = Html.ActionLink("GoBack", "Dashboard");
}
}
In the controller I now look in the url.referrer if it contains 'dashboard', then I set the Model.ReturnPage to 'Dashboard'.
Is there a cleaner way of doing this?
Put the ReturnLink as a property on your model and set it inside the controller, which will alleviate the need for you to put that logic in the view.
There are certainly cleaner ways, but as your code is currently, it is very easy to understand what you are trying to do.
I would say keep it as is and simply put a #region wrapper around it and hide it when you don't need to work with it:
#region get referrer page
MvcHtmlString backLink = null;
if (Model.ReturnPage == MatchResultReturnPage.Search)
{
backLink = Html.ActionLink("GoBack", "Search", new {search = true});
}
else
{
backLink = Html.ActionLink("GoBack", "Dashboard");
}
#region
The only thing I would suggest is to have this check in the Controller, rather than the view and simply putting the result of your check either in model property, or in the ViewBag.
To gain access to Helpers in your controller, do the following:
var URL = new UrlHelper(this.Request.RequestContext).Action("MyAction", "MyController", new { id = 123 });
you should probably implement the Back button entirely in JavaScript.
using the history object
<a href=”javascript:history.back()”> [Back]</a>
I'm wanting to capture the old values within a model so I can compare with the new values after submission, and create audit logs of changes a user makes.
My guess is doing it with hidden input boxes with duplicated old value properties would be one way. But wondering if there are any other good alternatives?
Thanks
In the save method, just go and get the original object from the database before saving the changes, then you have your old and new values to compare against? :)
This sounds like standard auditing. You should not worry about what has changed just capture EVERYTHING and who made the change. Unless there is some sort of real time reporting that needs to be done.
Possible auditing implementations:
CQRS, in a nutshell it tracks every change to a given object. The downside is it's an architecture that is more involved to implement.
The Rolling ledger. Each insert is a new row in the database. The most current row is used for display purposes, but with each update, a new row is inserted into the database.
Yet another approach is to save it off into an audit table.
All get the job done.
You could also store the original model in the view bag and do something like this...
// In the controller
public ActionResult DoStuff()
{
// get your model
ViewBag.OriginalModel = YourModel;
return View(YourModel);
}
// In the View
<input type="hidden" name="originalModel" value="#Html.Raw(Json.Encode(ViewBag.OriginalModel));" />
// In the controller's post...
[HttpPost]
public ActionResult DoStuff(YourModel yourModel, string originalModel)
{
// yourModel will be the posted data.
JavaScriptSerializer JSS = new JavaScriptSerializer();
YourModel origModel = JSS.Deserialize<YourModel>(originalModel);
}
I didn't get a chance to test this, just a theory :)
Exactly what mattytommo says is the preferred method all around
Instantiate new view model for creating a new entity
public ActionResult Edit(int id) {
var entity = new Entity(id); // have a constructor in your entity that will populate itself and return the instance of what is in the db
// map entity to ViewModel using whatever means you use
var model = new YourViewModel();
return View(model);
}
Post changes back
[HttpPost]
public ActionResult Edit(YourViewModel model) {
if (ModelState.IsValid) {
var entity = new YourEntity(model.ID); // re-get from db
// make your comparison here
if(model.LastUserID != entity.LastUserID // do whatever
... etc...
}
return View(model);
}
I have a view that I pass a viewmodel object to that contains an IQueryable<> object.
This object is used to populate an mvccontrib grid. The view also contains other partial views that allow the user to filter the data within the grid.
Once the grid is filtered I would like the user to be able to export the Iqueryable object to another controller actionresult method which then calls another viewmodel that exports the data to Excel.
Here is the snippet of the view that calls the Export actionresult() method:
#using (Html.BeginForm("Export", "Home", FormMethod.Post, new { Model }))
{
<p>
<input class="button" value="Export to Excel" type="submit" />
</p>
}
Model does contain the IQueryable object.
When I debug the code I can view the viewmodel object, and of course in order to populate the IQueryable I must enumerate the object.
I have also created another viewmodel object that, once the Model object is passed back to the actionresult method attempts to enumerate the IQueryable object by either using the .ToList() method or the AsEnumerable() method.
But in all cases the IQueryable object is pass to the controller as a null object.
Here is the action result method that is being called from the view:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Export(PagedViewModel<NPSProcessed> NPSData)
{
string message = "";
NPSData Query = new Models.NPSData(NPSData);
Query.IData = NPSData.Query.ToList();
// Opening the Excel template...
FileStream fs =
new FileStream(Server.MapPath(#"\Content\NPSProcessedTemplate.xls"), FileMode.Open, FileAccess.Read);
MemoryStream ms = new MemoryStream();
ee.ExportData(fs, ms, Query.IData, message);
// Sending the server processed data back to the user computer...
return File(ms.ToArray(), "application/vnd.ms-excel", "NPSProcessedNewFile.xls");
}
Any assistance would be greatly appreciated.
Thanks
Joe
You cannot pass complex objects around like this: new { Model }. It would have been to easy :-). You will have to send them one by one:
new { prop1 = Model.Prop1, prop2 = Model.Prop2, ... }
Obviously this could get quite painful. So what I would recommend you is to send only an id:
new { id = Model.id }
and then inside your controller action that is supposed to export to Excel use this id to fetch the object from wherever you fetched it initially in the GET action (presumably a database or something). If you want to preserve the paging, and stuff that the user could have performed on the grid, you could send them as well to the server:
new { id = Model.id, page = Model.CurrentPage, sortColumn = Model.SortBy }
Another possibility (which I don't recommend) consists into saving this object into the session so that you can fetch it back later.
Yet another possibility (which I still don't recommend) is to use the MVCContrib's Html.Serialize helper which allows you to serialize an entire object graph into a hidden field and it will be sent to the server when the form is submitted and you will be able to fetch it as action argument.
The simple answer is: don't put IQueryable properties in your model. The model should be purely simple objects and validation attributes. Keep the queryability in your controller.
I am working through sample MVC Nerdinner tutorial and using it on the AdventureWorks database. I have created an Edit action in the CategoryController to edit Product Category in AdventureWorks. The only updateable field in this table is the Name (the other fields - ID, RowGUID and UpdateDate are autogenerated). So my edit form View has only 1 field for the Name (of Product Category). My "Save" action for the edit is below: -
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection){
ProductCategory p = awRepository.GetProductCategory(id);
try
{
//UpdateModel(p);
p.Name = Request.Form["Name"];
awRepository.Save();
return RedirectToAction("Details", new { id = p.ProductCategoryID });
}
catch
{
foreach (var err in p.GetRuleViolations())
{
ModelState.AddModelError(err.PropertyName, err.ErrorMessage);
}
return View(p);
}
}
If I use the code as above, everything works as long as the Name I enter is valid (thus there is no exception). If I introduce an error (which is raised by GetRuleViolations if the Name is blank or for testing purposes is a particular "Test" string) I get a NullReferenceException (Object reference not set to an instance of an object) on this line in the View (Category/Edit.aspx) when the Edit View is redrawn (to show the user the error and allow him to correct)
<%= Html.TextBox("Name") %>
If I update my ProductCategory using UpdateModel(p) instead of using the Request.Form variable, everything works fine; Valid data is saved and invalid data redraws the view showing the error message.
My question is: what is the difference between UpdateModel and manual updating my variable by reading the values from Request.Form collection? The Nerdinner tutorial seems to suggest that both are equivalent. So I am surprised that one works smoothly and the other raises an exception.
Sounds like this:
http://forums.asp.net/p/1396019/3006051.aspx
So, for every error you add with
ModelState.AddModelError() and call
the View again, MVC Framework will try
to find an AttemptedValue for every
error it finds. Because you didn't add
them, MVC will throw an exception.
Normally you don't need to add these
values: AttemptedValues are
automaticaly populated when you use
DefaultBinding (by calling
UpdateModel() or by passing the object
to bind as an Action Method paramter:
public ActionResult
Create(FormCollection Form,
YourObjectType yourObject).
Looks like the following is done automatically by UpdateModel, but not done manually by yourself?
if (Form["Name"].Trim().Length == 0)
{
ModelState.AddModelError("Name", "Name is required");
//You missed off SetModelValue?
ModelState.SetModelValue("Name", Form.ToValueProvider()["Name"]);
}