How to save changes to a view model in C#? - c#

I am currently reading values in a configuration file and setting the values to a view model. I am displaying them on the UI in textboxes. I want the user to be able to edit/change the value in the textbox and be able to hit a save button and for the changes to be saved to the view model and the configuration file. I understand there needs to be some type of Get/Post method in the controller but I'm not entirely sure how the controller should look. I am not connecting it to a database at all.
View:
#using (Html.BeginForm())
{
<fieldset>
<div class="row">
<div class="col-md-1">Logging Directory:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.loggingDirectory)</div>
<div class="col-md-1">Archive Directory:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.archiveDirectory)</div>
<div class="col-md-1">Time Between Alarms:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.timeBetweenAlarms)</div>
<div class="col-md-1">Time to Archive Logs:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.timeToArchive)</div>
<div class="col-md-1">Situator IP:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.situatorIP)</div>
<div class="col-md-1 ">Situator Port:</div>
<div class="col-md-2 ">#Html.EditorFor(model => Model.situatorPort)</div>
<div class="col-md-1 ">Clean Up:</div>
<div class="col-md-2 ">#Html.EditorFor(model => Model.timeToCleanUp)</div>
<div class="col-md-1 ">Coorelation Zone:</div>
<div class="col-md-2">#Html.EditorFor(model => Model.coorelationZone)</div>
</div>
<div class="row submitButton">
<button class="btn btn-primary" type="submit">Save</button>
</div>
</fieldset>
}
View Model
public class ConfigurationViewModel
{
public string loggingDirectory { get; set; }
public string archiveDirectory { get; set; }
public string situatorIP { get; set; }
public string situatorPort { get; set; }
public string timeBetweenAlarms { get; set; }
public string timeToArchive { get; set; }
public string sightlogixIP { get; set; }
public string timeToCleanUp { get; set; }
public string coorelationZone { get; set; }
}
Controller:
public ActionResult Index()
{
ConfigurationViewModel cvm = new ConfigurationViewModel();
cvm.loggingDirectory = ConfigurationManager.AppSettings["loggingDirectoryPath"];
cvm.archiveDirectory = ConfigurationManager.AppSettings["archiveDirectoryPath"];
cvm.situatorIP = ConfigurationManager.AppSettings["SituatorIP"];
cvm.situatorPort = ConfigurationManager.AppSettings["SituatorPort"];
cvm.timeBetweenAlarms = ConfigurationManager.AppSettings["TimeIncrementBetweenalarmsInSeconds"];
cvm.timeToArchive = ConfigurationManager.AppSettings["timeIncrementForArchivingLogFilesInHours"];
cvm.sightlogixIP = ConfigurationManager.AppSettings["SightLogixIP"];
cvm.timeToCleanUp = ConfigurationManager.AppSettings["timeIncrementForCleaningUp"];
cvm.coorelationZone = ConfigurationManager.AppSettings["correlationZoneLengthInFeet"];
return View(cvm);
}
[HttpGet]
public ActionResult Edit()
{
return;
}
[HttpPost]
public ActionResult Edit()
{
return;
}

Pass view model in Get Edit method
[HttpGet]
public ActionResult Edit()
{
ConfigurationViewModel cvm = new ConfigurationViewModel();
cvm.loggingDirectory = ConfigurationManager.AppSettings["loggingDirectoryPath"];
cvm.archiveDirectory = ConfigurationManager.AppSettings["archiveDirectoryPath"];
cvm.situatorIP = ConfigurationManager.AppSettings["SituatorIP"];
cvm.situatorPort = ConfigurationManager.AppSettings["SituatorPort"];
//...
return View(cvm);
}
Send updated view model to post edit method and perform action on it
[HttpPost]
public ActionResult Edit(ConfigurationViewModel cvm)
{
ConfigurationManager.AppSettings["archiveDirectoryPath"] = cvm.archiveDirectory;
ConfigurationManager.AppSettings["SituatorIP"] = cvm.situatorIP;
ConfigurationManager.AppSettings["SituatorPort"]= cvm.situatorPort;
//...
return View(cvm);
}
And your razor view which will submit updated data to your Post Edit method
#using (Html.BeginForm("Edit", "Your controller", FormMethod.Post))
{
....
}

Related

Dictionary<TEntity, string> and asp.net mvc how to?

I have:
public class Nomenclature
{
public virtual int NomenclatureId { get; set; }
public virtual NomenclatureType NomenclatureType { get; set; }
public virtual IDictionary<NomenclatureAttribute, string> Attributes { get; set; }
}
public class NomenclatureType
{
public virtual int NomenclatureTypeId { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Nomenclature> Nomenclatures { get; set; }
public virtual ICollection<NomenclatureAttribute> NomenclatureAttributes { get; set; }
public NomenclatureType()
{
Nomenclatures = new HashSet<Nomenclature>();
NomenclatureAttributes = new HashSet<NomenclatureAttribute>();
}
}
public class NomenclatureAttribute
{
public virtual int NomenclatureAttributeId { get; set; }
public virtual string AttributeName { get; set; }
public virtual string AttributeType { get; set; }
public virtual NomenclatureType NomenclatureType { get; set; }
}
it's all represents a nomenclature of goods in my application. I'am tryin create new Nomenclature in my app. I use NHibernate. I create controller and create action:
[HttpGet]
public ActionResult Create(string nomenclatureType)
{
if (nomenclatureType == null)
return RedirectToAction("List", "Nomenclature");
ViewData["NomenclatureAttributes"] =
_repositoryNomenclatureType.Get(w => w.Name == nomenclatureType).NomenclatureAttributes.ToList();
return View();
}
[HttpPost]
public IActionResult Create(Nomenclature nomenclature)
{
try
{
if (ModelState.IsValid)
{
_repositoryNomenclature.Create(nomenclature);
return RedirectToAction("List", "Nomenclature");
}
}
catch (Exception)
{
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists see your system administrator.");
}
return View(nomenclature);
}
I need to foreach all NomenclatureAttrributes specified for any Nomenclature Type and create editors for all values and add all to Model.Attributes.
#model Nomenclature
#{
ViewBag.Title = "New nomenclature";
Layout = "_Layout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#foreach (var a in (List<NomenclatureAttribute>)ViewData["NomenclatureAttributes"])
{
<div class="form-group">
<label class="control-label col-md-2">#a.AttributeName</label>
<div class="col-md-10">
**What i should place to this???**
</div>
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
I use Asp.net core web application (.NET Framework)
First Create ViewModel.
public class CreateNomenclatureViewModel
{
//Add other properties if needed
public NomenclatureType SelectedNomenclatureType { get; set; }
public List<NomenclatureAttribute> Attributes { get; set;}
}
Second
[HttpGet]
public ActionResult Create(string nomenclatureType)
{
if (nomenclatureType == null)
return RedirectToAction("List", "Nomenclature");
var viewModel= new CreateMomenClatureViewModel
{
Attributes = _repositoryNomenclatureType.Get(w => w.Name == nomenclatureType).NomenclatureAttributes.ToList()
}
return View(viewModel);
}
Than fix your view
#model CreateNomenclatureViewModel
#{
ViewBag.Title = "New nomenclature";
Layout = "_Layout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#if (Model != null && Model.Attributes != null)
{
for (int i = 0; i < Model.Attributes.Count; i++)
{
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Attributes [i].AttributeName)
#Html.TextBoxFor(modelItem => Model.Attributes [i].AttributeType )
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
if you want to use Nomenclature as ViewModel you can create new Nomenclature on Get Method than pass to view in razor view.
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Attributes.Keys.ElementAt(i).AttributeName)
#Html.TextBoxFor(modelItem => Model.Attributes.Keys.ElementAt(i).AttributeType )
</div>

Binding partial to multiple actionresults

I was wondering if anybody could suggest a way of overcoming the following issue. I have the following models
// AModel
public class AModel
{
public string PropOne { get; set; }
public bool PropTwo { get; set; }
public string Keyword { get; set; }
}
public class AModelViewModel
{
public AModelViewModel()
{
AModel = new AModel();
}
public AModel AModel { get; set; }
}
//BModel
public class BModel
{
public string PropOne { get; set; }
public bool PropTwo { get; set; }
public string Keyword { get; set; }
}
public class BModelViewModel
{
public BModelViewModel()
{
BModel = new BModel();
}
public BModel BModel { get; set; }
}
This is what my controller looks like
public ActionResult PageA()
{
var model = new AModelViewModel();
return View(model);
}
[HttpPost]
public ActionResult PageA(AModel aModel)
{
return View();
}
public ActionResult PageB()
{
var model = new BModelViewModel();
return View(model);
}
[HttpPost]
public ActionResult PageB(BModel bModel)
{
return View();
}
The two views look like this
//PageA
#model WebApplication13.Models.AModelViewModel
#using (Html.BeginForm())
{
<div class="form-group">
<label>Title</label>
<input type="title" class="form-control" id="title" name="Model.PropOne">
</div>
<div class="checkbox">
#Html.CheckBoxFor(x => x.Model.PropTwo)
</div>
#Html.Partial("~/Views/Shared/_RandomView.cshtml")
<button type="submit" class="btn btn-default">Submit</button>
}
//PageB
#model WebApplication13.Models.BModelViewModel
#using (Html.BeginForm())
{
<div class="form-group">
<label>Title</label>
<input type="title" class="form-control" id="title" name="Model.PropOne">
</div>
<div class="checkbox">
#Html.CheckBoxFor(x => x.Model.PropTwo)
</div>
#Html.Partial("~/Views/Shared/_RandomView.cshtml")
<button type="submit" class="btn btn-default">Submit</button>
}
Both views use the following partial view
//_RandomView
<div class="form-group">
<label for="keyword">Keyword</label>
<input type="text" class="form-control" name="Keyword" />
</div>
The problem I have is that because this partial is shared, and the name attribute of the keyword input is 'Keyword', when I submit the form on either page the keyword property is never binded so it's always null. Is there a way I can share this partial, but alter the prefix depending on what page i'm using. Any help would be greatly appreciated.
Instead of using a paritial view.. Create an EditorTemplate in Views\Shared\EditorTemplates named Keyword.cshtml with code something like
#model string
//_RandomView
<div class="form-group">
<label for="keyword">Keyword</label>
#Hmlt.TextBoxFor(a => a, new {class="form-control"})
</div>
then in your views you just call
#Html.EditorFor(a => a.AModel.Keyword,"Keyword")
if you have to use PartialView.. you can pass in an HtmlFieldPrefix via ViewDataDictionary when you call the partial view. To do this, you would call your partial like this.
#Html.Partial("~/Views/Shared/_RandomView.cshtml",
new ViewDataDictionary { TemplateInfo = new TemplateInfo() {
HtmlFieldPrefix = "AModel" } });
then in your partial view you will need to use the #Html.Textbox helper like so.
<div class="form-group">
<label for="keyword">Keyword</label>
#Html.TextBox("Keyword", string.Empty, new { #class="form-control" })
</div>

ASP.NET MVC - "insufficient stack to continue executing the program safely" within a Partial View

I'm trying to Add some doctor records through a Partial View. When I'm trying to test the max length of my fields (for instance by putting 31 letters and the limit is 30), I've got the following error:
insufficient stack to continue executing the program safely
Here's what I'm doing (Model, View and Controller) :
Model :
public class DoctorViewModel
{
public int DoctorId { get; set; }
[Required(ErrorMessage="Champ requis")]
[MaxLength(30)]
public string DoctorLastName { get; set; }
[Required(ErrorMessage = "Champ requis")]
[MaxLength(30)]
public string DoctorName { get; set; }
}
View:
<div id="addNewMedecin">
#Html.Action("AddNewMedecin")
</div>
<div id="listMedecins">
#Html.Action("ListMedecins")
</div>
Partial View :
#using (Html.BeginForm("AddNewMedecin", "Home", FormMethod.Post))
{
<fieldset id="fsMedecins">
<div class="editor-label">
Nom :
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.DoctorLastName)
#Html.ValidationMessageFor(a => a.DoctorLastName)
</div>
<br />
<div class="editor-label">
Prénom :
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.DoctorName)
#Html.ValidationMessageFor(a => a.DoctorName)
</div>
<input id="createNewMedecin" type="submit" name="Enregistrer" value="Enregistrer" />
</fieldset>
}
Controller Actions :
public ActionResult Medecins()
{
return View();
}
[HttpGet]
public ActionResult AddNewMedecin()
{
return PartialView();
}
[HttpPost]
public ActionResult AddNewMedecin(DoctorViewModel dvm)
{
context = new SchedulingDataClassesDataContext();
if (ModelState.IsValid)
{
DBMedecin newMedecin = new DBMedecin()
{
Nom = dvm.DoctorLastName,
Prenom = dvm.DoctorName
};
context.DBMedecins.InsertOnSubmit(newMedecin);
context.SubmitChanges();
return RedirectToAction("Medecins");
}
return View("Medecins");
}
Any idea guys?

Form not saving data to model

I am using MVC in order to build a blog. What I want is to save post comments to its corresponding place in the database but it does not work.
My post model is as follows:
public class Post
{
[Key]
public int PostId { get; set; }
public string Title { get; set; }
public DateTime CreatedDate{get;set;}
public DateTime UpdateDate { get; set; }
public string Body { get; set; }
public ICollection<Comment> Comments { get; set;}
public ICollection<Tag> Tags { get; set; }
}
My Comment model is as follows:
public class Comment
{
[Key]
public int CommentId { get; set; }
public int PostId { get; set; }
[ForeignKey("PostId")]
public virtual Post Post{get; set;}
public string CommentCreateDate { get; set; }
public string CommentUpdateDate { get; set; }
public string CommeterName { get; set; }
public string EmailAddress { get; set; }
public string CommentText { get; set; }
public bool Approved { get; set; }
}
I have the following Action Methods:
[HttpGet]
public ActionResult CreateComment()
{
return View();
}
[HttpPost]
[ValidateInput(false)]
public ActionResult CreateComment(int id, string name, string email, string txt, bool aproved = false)
{
Post post = GetPost(id);
Comment comment = new Comment();
comment.Post = post;
comment.CommentCreateDate = DateTime.Now.ToString();
comment.CommeterName = name;
comment.EmailAddress = email;
comment.CommentText = txt;
comment.Approved = aproved;
db.Comments.Add(comment);
db.SaveChanges();
m_commentList.Add(comment);
return RedirectToAction("CreateComment", new { id = id });
}
And in my view I am trying this:
#model Blog.Models.Comment
#{
ViewBag.Title = "CreateComment";
}
<h2>Create a Comment</h2>
#using (Html.BeginForm())
{
<fieldset>
<legend>Enter Comment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CommeterName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.CommeterName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CommentText)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.CommentText)
</div>
<p>
<input type="submit" value="Create comment" />
</p>
</fieldset>
}
I get no exception but none of the data from the form is being saved. Only the data that is set in the action result, that is, CommentCreateDate and Approved. I am not sure what I am doing wrong.
I have tried a second option which is to include the id of the comment in BeginForm() as follows:
#using (Html.BeginForm(new {id = Model.CommentId}))
{
<fieldset>
<legend>Enter Comment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CommeterName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.CommeterName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.EmailAddress)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.EmailAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CommentText)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.CommentText)
</div>
<p>
<input type="submit" value="Create comment" />
</p>
</fieldset>
}
This will give me a null reference exception even if I use the new keyword :
System.NullReferenceException: Object reference not set to an instance of an object.
Why is this happening? Can anybody help?
Thank you
Your action signature should be:
public ActionResult CreateComment(Comment model)
The names generated for the form fields will bind back to the properties of the same model class. There's no way for the framework to know, for example, that the CommenterName property should match up to the name parameter of the action.
Your second example makes very little sense - you're trying to write out the ID but you have never set one. In fact, you don't even pass a Comment to the view with the form, which is why you get a NullReferenceException:
[HttpGet]
public ActionResult CreateComment()
{
return View();
}
Also, you should be careful with what fields you expose to your models and actions. For example, a user could easily force their comment to be approved just by adding the following markup via their browser's development console:
<input type="hidden" name="approved" value="true" />
Anything that is either in your model properties or a parameter to your action can be set by the user.
An altogether better option would be to use a dedicated model class for the form:
public class CreateCommentViewModel
{
public string Name { get; set; }
public string Email { get; set; }
public string Text { get; set; }
}
Then to map this to your Comment in your action with:
[HttpPost]
public ActionResult CreateComment(CommentViewModel model)
{
var comment = new Comment();
comment.CommenterName = model.Name;
// etc...
}
This prevents the user from being able to set things like Approved and CreatedDate.

Model (complex type) won't submit to action

I have some problem posting a form with 'complex type' model:
I have a Model:
public class CircleEditViewModel
{
[Key]
public int CircleId { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public bool IsSystem { get; set; }
public class UserInCircle
{
public UserInCircle(User user)
{
this.UserId = user.UserId;
FullName = user.FullName;
}
public int UserId { get; set; }
public byte[] Picture { get; set; }
public string FullName { get; set; }
public bool isInCircle { get; set; }
}
public List<UserInCircle> Users { get; set; }
}
My first problem was that at post event, my Users where null.. so i followed a few posts on here (like MVC- Model Binding on a Complex Type) to use a for instead of a foreach,but since i did so, my form won't post anymore:
View:
#model Wims.Website.ViewModels.CircleEditViewModel
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse('form');
});
</script>
#using (Ajax.BeginForm(Html.ViewContext.RouteData.Values["Action"].ToString(), null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SaveDone(data)" }, new { id = "editform" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Circle</legend>
#Html.Label(DateTime.Now.ToString());
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
if (Model.Users != null)
{
for (int i = 0; i < Model.Users.Count; i++)
{
<div class="userDetail">
<div>
<div>
#Html.DisplayFor(model => Model.Users[i].isInCircle);
</div>
<div class="iconDiv">
#Html.Image("~/Content/Images/defaultUser.jpg", Model.Users[i].FullName, null);
</div>
<div>
#Html.TextBoxFor(model => Model.Users[i].FullName)
#Html.HiddenFor(model => Model.Users[i].UserId)
</div>
</div>
</div>
<div style="clear: both"></div>
}
}
#Html.GenerateSecureDataControls(model => model.CircleId)
<input type="submit" value="Save" />
}
My view is rendered as a partial loaded thru ajax (not sure it makes any difference here).
Any idea why it won't post? If i remove all the '[]' like 'Users[0].FullName' to Users0.FullName i will post, but of course it won't be bound.
Thanks for your help
Edit just in case needed: Action:
[HttpPost]
public ActionResult Edit(CircleEditViewModel circleData, FormCollection collection)
{
if (ModelState.IsValid)
{
using (var logic = new CircleLogic())
{
Circle circle = logic.GetCircleById(circleData.CircleId, WebMatrix.WebData.WebSecurity.CurrentUserId);
if (circle == null)
{
return HttpNotFound();
}
else
{
circle.Name = circleData.Name;
logic.UpdateCircle(circle, GetSelectedUser(collection));
}
return PartialView("_CircleAndUsers", GetData(logic, circle.CircleId));
}
}
return this.Json(new { success = false, viewdata = RenderRazorViewToString("_CircleAndUsers", circleData) });
}
Pablo Romeo was right, i added a default ctor and it worked.

Categories

Resources