Using a asp mvc system, I would like to perform some kind of user notification that a record was added into the database.
Current paradigm... navPage -> modPage -> datagridPage -> newDatabasePage -> (record added action) This is where it stops, the user is not notified that record is added or failure, it simply reloads the view.
Currently - i am using some text in a viewbag variable after the record was added and based on this value, display a simple javascript message box. But I think there is a better way.
Was thinking that a modal popup implemented through jquery would accomplish this task, but i was educated this was not an optimal solution without using a messaging framework (signalR etc).
Another approach was to use an additional partial page - another partial page????
Any different options that i have missed here would be greatly appreciated.
You can add some beautiful notifications using sweetalert2, here is the documentation
Are you using Bootstrap (or similar)?
Can you not just pass some information into a hidden-as-default alert div?
Assuming you're using EF, when you save an object you can capture the new Id so you can just check for this.
Instead of reloading the view, redirect to the action that creates the initial view and change it so you can pass in the new Id:
int newId = _context.SaveChanges();
return RedirectToAction("GridView", new { Id = newId });
Then change the signature of the Action:
public async Task<ActionResult> GridView(int? Id)
You can then just check if the Id has a value and do something with that information. Then just add that to the ViewModel for the page and display the alert if required:
if (Id.HasValue)
{
viewModel.displayAlert = true;
}
If you don't want to reload the view in full then you can accomplish the same thing using Ajax calls.
Related
What I'm trying to achieve is to customise what happens during the HttpPost/OnSubmit of a form created with Umbraco Forms, whilst still maintaining the default behaviour of the form and any subsequent workflow.
In my specific scenario, I need to be able to analyse the input of the form and based on that input I will send the user to a different "thank you" page, whilst also storing some elements of the originally submitted form in TempData.
I've tried the following:
I've tried to create a DocType controller (RenderMvcController), but this only allows you
to override the HttpGet, and not Post.
I cannot use a SurfaceController as I lose the functionality of the
module, Umbraco Forms.
I've tried to use a custom workflow, but this runs asynchronous to
the user's journey and I cannot change their experience.
There isn't much useful documentation available for this at all and I'm finding this task more difficult than I expected it to be.
In order to add a custom procedure after the submission of the form, and based on this procedure change the user journey you must do the following:
Create a new controller and inherit from UmbracoFormsController and override the OnFormHandled method
public class CustomUmbracoFormsController : UmbracoFormsController
{
protected override void OnFormHandled(Form form, FormViewModel model)
{
// Find the field in the form, then search for a matching value
var field = form.AllFields.FirstOrDefault(x => x.Alias == "deliveryOptions");
var fieldValue = model.FormState[field.Id.ToString()].GetValue(0).ToString();
// Add the value to the TempData
TempData["deliveryOptions"] = fieldValue;
}
}
The above is a basic implementation that doesn't account for NULL
Update the reference to UmbracoFormsController in /Views/Partials/Forms/Form.cshtml with your new controller from above.
...
#using (Html.BeginUmbracoForm<CustomUmbracoFormsController>("HandleForm"))
...
In the above example we analyse the form data and store some information in the TempData, we can set the form to redirect to a generic thank you page in which we can analyse the values in the TempData and change the view the user sees.
Also, if you are making changes to the Form values and what these to be updated, you'll need the Record Guid, which you can retrieve from TempData["Forms_Current_Record_id"] in conjunction with a new RecordStore object.
I am using MVC3, C#, Razor, EF4.1, SQLServer 2008.
I have a parent form with a dropdown for "Suppliers". I wish to add a "quick add" link/button that enables the user to quickly add a supplier to the DB which is then available in the dropdown for selection. At present this is achieved by
Parent Page -> Add Supplier Page -> Parent Page(Page Refresh)
Of course on return to the parent page, it refreshes and removes all non saved data - which is a problem. It would be better to have a popup window which then saves the suppliers and then just refreshes the dropdown portion of the parent page form. So I believe I am seeking an approach to:
Parent Page -> Popup(Modal) -> DB Save -> Refresh DropDown in Parent Page (Ajax???) -> close Modal popup.
I would appreciate guidance on the above, as I am a little stuck on the best practice and hopefully simple approach to this.
Many thanks.
I normally do something like this:
Create an 'Add' button that will display a popup. (I use jQuery dialogs. They are simple, free, and easily to implement by just calling .dialog on a div). Inside this dialog have the appropriate fields needed to create a new supplier. Have a 'Save' button in this dialog and have it wired up to a AJAX post. (Again this is very simple using jQuery)
If you do use jQuery its as simple as submitting that form to your controller action that will then call you data access layer to save the new supplier entity. When the AJAX call comes back successfully you can reload the contents of the supplier grid with another AJAX post. All the 'Magic' comes from implementing AJAX really which will allow for you to retain the users input and not reload the whole page. The AJAX call that is executed after the user enters in a new Supplier and clicks save would look something like this:
In your JavaScript:
$.ajax({
url: "ControllerName/SaveNewSupplier",
Data: $('#YourAddNewSupplierFormName').serialize(),
Type: "POST"
Success: function(result) {
// this is what will get called after a successful save and return of your action method on your controller. This is where you will put the call to repopulate the supplier list with the updated list.
UpdateSupplierList(); // This function is created below
}
});
In your controller:
Public JsonResult SaveNewSupplier (NewSupplierModel newSupplier)
{
// save your new supplier through your data access layer
// if save is successful then return
Return Json({success = true}, JsonRequestBehavior.AllowGet)
}
Then to repopulate the initial div that contains all the suppliers do something like this:
In JavaScript:
function UpdateSupplierList()
{
$.ajax({
url: "ControllerName/GetAllSuppliers",
Type: "GET"
Success: function(result) {
$('#SuppliersDiv').html(result.suppliers)
}
And in your controller:
// remember that a lot of this is pseudo code and your will have to implement it better for your situation. But basically its just:
Public JsonResult GetAllSuppliers()
{
var suppliers = db.GetSuppliers()
return Jason({suppliers = suppliers}, JsonRequestBehavior.AllowGet);
}
EDIT: If you are updating a SelectList via jQuery then this article is almost identical to what I explained but goes into much more detail on updating the select list. Hope this helps.
http://www.joe-stevens.com/2010/02/23/populate-a-select-dropdown-list-using-jquery-and-ajax/
How do I use the modelstate in ASP.NET MVC to see if the user has made changes or not?
For example:
A form gets loaded and the user has the ability to make changes on the form.
After making changes, users clicks apply or cancel button.
Clicking the button brings control back to my controller where I do validation, then commit to database.
What if the user clicks the apply button without making changes? I want the controller to be able to detect that nothing was altered by the user and send the appropriate message.
Is there a way to detect this using modelstate? Or is there another way to do this?
Thanks
We use automapper to take our domain entities and map them to view models. Something like this:
var viewModel = Mapper.Map<DomainEntityClass, ViewModelClass>(entity);
Then, on post back of the view/page, we can test if they are different by loading the entity again and comparing it to the mapped view model:
var entity = Repository.Get<DomainEntityClass>(id);
var mappedEntity = Mapper.Map<DomainEntityClass, ViewModelClass>(entity);
// code to compare mappedEntity to incoming viewmodel
//
// or build an equality comparer to test them
The idea is that if the mapping configuration hasn't changed, the Mapper.Map call (given the same entity) should return matching view model objects if nothing has changed.
For help building equality comparers, check here - http://msdn.microsoft.com/en-us/library/ms132151.aspx
I'm using MVC3 (razor) and i'm trying to get the following working.
I have a list of snippets. These snippets have some general settings and then have a translation for an unknown ammount of languages.
Now i'm trying to do the following:
On the 'Create' page (url: Screen) of a snippet i set the general settings. under that there is a list of filled translations (empty at the start). When you press the 'Opslaan' button, i want the form to save the general settings and the list of translations.
When i push the 'Add' button i want to submit the complete viewmodel (settings + list of translations) to an other page where i can fill in a translation. After i filled in a translations, i want to return to this page (url: Screen). Here, a translation is filled in the list.
Now i'm doing someting wrong, because i cant get the viewmodel to submit to the 2nd page.
this is my code:
button 'add translation':
#Html.ActionLink("Add", "CreateTranslation", new { oSnippeteditviewmodel = this.Model }, null)
SnippetController:
public ActionResult Create()
{
SnippetEditViewModel oItem = new SnippetEditViewModel();
oItem.lSnippetsPerLanguage = new List<SnippetPerLanguageEditViewModel>();
return View(oItem);
}
[HttpPost]
public ActionResult Create(SnippetEditViewModel Snippeteditviewmodel)
{
if (ModelState.IsValid)
{
Snippeteditviewmodel.Bookmark = Snippeteditviewmodel.Bookmark.Replace(' ', '_');
_repoSnippet.CreateSnippet(Snippeteditviewmodel);
return RedirectToAction("Index");
}
return View(Snippeteditviewmodel);
}
public ActionResult CreateTranslation(SnippetEditViewModel oSnippeteditviewmodel)
{
return View(oSnippeteditviewmodel);
}
And in the controller, action CreateTranslation the object 'oSnippeteditviewmodel' stays null.
annyone who has a simular problem? Or a solution?
First, you should try to generate action link like this
#Html.ActionLink("Add", "CreateTranslation", this.Model, null)
In this case mvc will try to pass correctly serialized model values for your link and if your model is simple enough, CreateTranslations will get its model correctly. But, I would not do it that way. Generated link is static. What if user changes Snippet values on client side? When it comes to adding Translation, all the changed form values will be lost (Link will pass initial, server generated values). So, you should try one of the followings
Create the form with two buttons, one for CratingTranslation and one for Saving. When creating translation, dynamically change form's action and method parameters to GET the CreateTranslation action. This way, form will serialize all its current Snippet settings and pass to desired action, and you get the current snippet model passed to CreateTranslation action.
Use ajax. Just dynamically inject translation creation input fields into same page. That's simple and more user friendly (no bundle of navigations), and more http traffic is reserved (Passing all the translations and snippet to second page, and then returning all of these + 1 translation could get you in trouble). I would reccomend this approach. This is far more simple than first or your approaches.
I am not getting you properly but if you wanna add data by "create" controller then you don't need to specify object in "oSnippeteditviewmodel". You can get all form data by
Request.Form["controlName"]
and fill the Snippeteditviewmodel data member by above and save that.
Situation: In some project management software written in asp.net I have a create project page (working fine). I need to add to this the ability to add tasks from a list of templates to this project pre-creation BUT the list of available tasks is dependent on some values sitting in the create form.
My abstract solution is this:
I have a "Create" view and an "Add Tasks" View - both strongly typed to a composite viewModel defined in the controller
My Create method checks which button was used to call it - if the
button was "Add Tasks" it then renders the AddTasks view, passing the model in from the create view, again all in the same controller.
The AddTasks View posts to the Create view with one of two buttons, one loads the view and the other causes an actually DB save.
My Problem is this:
The different views use different properties of the same model, but in passing this model between them, the data is reset (in any case reload or save).
I am guessing this is happening from auto binding of data - though I thought fields not present on the form would not overwrite existing model data passed down.
There is hardly any code in the controller manipulating the model at present - It is only passed from view to view in these cases.
This is the controller code:
// POST: /Project/Create/<viewModel>
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id,id")] ProjectViewModel model)
{
if (model.SubmitValue == "Create")
{
try
{
model.Project.Id = Guid.NewGuid();
model.Save(this.User.Identity.Name);
return this.RedirectToAction("Details", new {id = model.Project.Id});
}
catch (Exception e)
{
this.ModelState.AddModelError(e.ToString(), e.ToString());
}
return View(model);
}
if(model.SubmitValue == "AddTasks")
{
return this.View("AddTasks",model);
}
return this.View(model);
}
//POST: /Project/AddTasks/ + model
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddTasks([Bind(Include = SelectedCarrierTasks")]ProjectViewModel model)
{
return View(model);
}
The Question is: How do I maintain the state of the model across these views until it finally save it?
I would prefer to avoid any hackish (TempData) or JS dependant solutions, but I am not closed to these if they are really the best solution.
Thanks,
Adam Tolley
One simple solution is to persist the ViewModel object in a Session variable and bind the View from this source.I ts certainly not the most elegant solution. Another option, and probably less elegant one is persist this model data in the database, with some temporary/unsaved flag.
The problem is that when you display the add tasks view you're not providing fields for your "Project" object therefore the ModelState loses the data related to the project, you will need to provide this fields to ensure you're not loosing that data.
You don't need to display this fields they can be of type hidden and they will preserve the value. Just make sure that if you will be binding to a view model you will need to name this fields correctly like this Model.Project.Property.
Perhaps I am trying to solve the wrong problem (ala Bruce Eckel). I am going to try to move to a structure that needs this sort of fuzzy boundary less. I don't want to adopt a REST paradigm only to shoe-horn it into a stateful application.
Possibly these controls belong on the same page, and I can use some JQuery goodness to put in a tab pane for easiness on the eyes.
Thanks to those who answered, I found each useful and will try to remember to up-vote them as soon as I have some more rep.
I can't comment on other peoples questions at the moment, but the only real option is the session if you want to persist an objects state during web requests, or serializing it and placing it in a hidden field.
Or a final option would be to change the way your pages work so you can save the object after each request...
If your using nHibernate then you might want look into the Conversations pattern, but this just essentially saves the nHibernate session into the asp.net session anyway...