MVC 3 - Adding multiple objects to a entity - c#

I have a classes like this:
public class member
{
public string name {get;set;}
public IList<Note> notes {get;set;}
}
public class note
{
public string text {get;set;}
public datetime created {get;set;}
}
I want to have a page which inserts the member class - which i am fine with. My question lies in how to go about adding multiple notes to the member on the same page?
What would be the best way to go about this? (maybe some ajax solution to show sub forms for the note class)
Can anyone point me in the right direction of some related examples learning material?
Thanks in advance.

I'd create an Ajax form that posts to a method called AddNote(AddNoteViewModel viewModel) on your controller. AddNoteViewModel would contain all the information you need to create a new note. The AddNote Action Method would add the new note, SaveChanges and return a list of notes for the given Member. You can use a partial view for the content that is returned from AddNote.
On the Ajax form you should set UpdateTargetId to the id of the <div> you want to update with the latest list of notes.
Another option might be to use JQuery.
Here is a good example of both: Using Ajax.BeginForm with ASP.NET MVC 3 Razor
UPDATE : I've adapted Darin Dimitrov's example (from the link) to suit your scenario. This is off the top of my head so won't be perfect but it should give you a decent starting point
Model:
public class AddNoteViewModel
{
[Required]
public int MemberId { get; set; }
[Required]
public string Text { get; set; }
}
Controller:
[HttpPost]
public ActionResult AddNote(AddNoteViewModel model)
{
var member = //Get member from db using model.MemberId
member.Notes.Add(new Note{Text = model.Text, Created = DateTime.Now});
//SaveChanges();
var notes = //Get notes for member
return View(notes);
}
View:
#model AppName.Models.AddNoteViewModel
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<div id="result"></div>
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "result" }))
{
#Html.HiddenFor(x => x.MemberId)
#Html.EditorFor(x => x.Text)
#Html.ValidationMessageFor(x => x.Text)
<input type="submit" value="OK" />
}
Using JQuery:
View:
#model AppName.Models.AddNoteViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/index.js")" type="text/javascript"></script>
<div id="result"></div>
#using (Html.BeginForm())
{
#Html.HiddenFor(x => x.MemberId)
#Html.EditorFor(x => x.Text)
#Html.ValidationMessageFor(x => x.Text)
<input type="submit" value="OK" />
}
index.js:
$(function () {
$('form').submit(function () {
if ($(this).valid()) {
$.ajax({
url: this.action,
type: this.method,
success: function (result) {
$('#result').html(result);
}
});
}
return false;
});
});

Related

Edit and update rows in dynamic table in Asp.net mvc4 razor view

I am new to asp.net MVC. I have a dynamic table in my project. Adding dynamic rows in table is achieved with the help of following link
Adding and deleting rows in dynamic table in Asp.net mvc razor view
I need to edit and update the dynamic table.
I have tried following code
My sample model
public class Gift
{
public string Name { get; set; }
public double Price { get; set; }
}
public class GiftViewModel
{
public string Age { get; set; }
public DateTime TheDate { get; set; }
public IEnumerable<Gift> Gifts { get; set; }
}
My sample Controller
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(GiftViewModel model)
{
// do work here
return RedirectToAction("Index");
}
public ViewResult AddNew()
{
return View("_TimeSheetView");
}
}
My sample Partial View
#model HelloWorldMvcApp.Gift
#using (Html.BeginCollectionItem("giftList"))
{
<div>
<span class="drop_medium">
#Html.TextBoxFor(m => m.Name)
</span>
<span class = "drop_medium">
#Html.TextBoxFor(m => m.Price)
</span>
</div>
}
My sample main view
#model HelloWorldMvcApp.GiftViewModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.Age)
#foreach (var data in Model.Gifts)
{
{ Html.RenderPartial("_TimeSheetView", data); }
}
#Html.ActionLink("Add another", "AddNew", null, new { id="addItem" })
<input type="submit" value="Save"/>
}
<script type="text/javascript">
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#dynamic").append(html); }
});
return false;
});
</script>
When I click 'Add Another' button a row is added to the table. After editing the values in the table When I click submit button I receive nothing in the controller. The IEnumerable Gifts variable is null. How to take the table values to the controller. Please help me to fix this is issue. Thanks in advance
Your model's collection property is named Gifts so the partial needs to be
#model HelloWorldMvcApp.Gift
#using (Html.BeginCollectionItem("Gifts")) // not "giftlist"
{
...
}
This will generate inputs with the correct name attributes for binding to a collection (where ## is a Guid)
<input name="Gifts[##].Name" ... />
<input name="Gifts[##].Price" ... />
<input type="hidden" name="Gifts.Index" value="##" />
The problem you're facing is the name of the rendered input isnt matching your model structure. There are a couple of ways out of this:
Make an editor template for the model type
your partial view:
#model IEnumerable<HelloWorldMvcApp.Gift>
#Html.EditorForModel("","Gifts")
and an EditorTemplate for the Gift model:
#model HelloWorldMvcApp.Gift
<div>
<span class="drop_medium">
#Html.TextBoxFor(m => m.Name)
</span>
<span class = "drop_medium">
#Html.TextBoxFor(m => m.Price)
</span>
</div>
Manually create the inputs with the properly parsed name - "Gifts[x].Property"
Obviously the first option is far cleaner and imho preferred.
Hope this works, and helps :)

No Knockout model binding on automatically filled inputs

I have a knockout model that is automatically created from a C#-ViewModel:
ViewModels:
public class SearchModel
{
public ActualLocationModel Location { get; set; }
}
public class ActualLocationModel
{
public string Address { get; set; }
}
search.js:
function Search(model) {
var self = this;
self._model = model;
ko.applyBindings(self._model, document.getElementById("searchForm"));
$('#submitButton').click(function () {
alert(self._model.ActualLocation.Address); // proof!
});
}
Search.cshtml:
#model ViewModels.SearchModel
<div id="searchForm">
<input data-bind="value: ActualLocation.Address" type="text">
<input type="submit" id="submitButton" value="Find" />
</div>
<script type="text/javascript">
$(function () {
window.search= new Search(#Html.Raw(Json.Encode(Model)));
});
</script>
So, the databinding is working as expected as long as I enter the values by hand. But in my case the values are filled automatically by geolocation. In this case the binding doesn't do what it should be (output is always null). Is there a way to get the Knockout databinding working on automatic filled inputs?
Thanks for any help!
Alright, here are the relevant bits to get this working for you:
<script type="text/javascript">
$(function() {
window.search = new Search(JSON.parse('#Html.Raw(Json.Encode(Model))'));
});
</script>
Notice we're parsing the model which was converted to JSON so we can use the resulting Javascript object in our model. If you want to go straight from JSON to your model, consider the ko.mapping library, though it is no longer actively maintained, I believe.
<form id="searchForm">
<input data-bind="value: Location.Address" type="text">
<input type="submit" id="submitButton" value="Find" />
</form>
Here, notice the change from AdditionalLocation.Address to just Location.Address.
function Search(model) {
var self = this;
self._model = model;
ko.applyBindings(self._model, document.getElementById("searchForm"));
$('#submitButton').click(function () {
alert(self._model.Location.Address); // proof!
});
}
Finally, we fix up the alert to reflect the shape of our data (AdditionaLocation to Location).
Using the above, everything works as expected for me.

Add a new row to my view dynamically

Ok, so I have this class:
public class BackstoreInventoryUtility
{
public BackstoreInventoryInfo Item { get; set; }
public List<ItemListingUtility> ListItemUtility { get; set; }
public BackstoreInventoryUtility()
{
Item = new BackstoreInventoryInfo();
ListItemUtility = new List<ItemListingUtility>();
}
}
And here's the ListItemUtility class:
public class ItemListingUtility
{
public int Quantity { get; set; }
public string Duration { get; set; }
public List<string> AvailableDurations { get; set; }
public ItemListingUtility()
{
AvailableDurations = new List<string>();
}
}
In a view I am building, I am displaying 1 BackstoreInventoryUtility based on a BackstoreInventoryInfo item my user is currently browsing.
The ListItemUtility is a class allowing the user to proceed to certain action, like display for a set time a set quantity.
The view renders like this:
#model MyApp.Utilities.BackstoreInventoryUtility
#using (Html.BeginForm())
{
<div>
#if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
#Html.HiddenFor(_item => _item.Item.BackstoreInventoryID)
<div class="bigFontSize bold formStyle">
<label class="center">Options will eventually be displayed here.</label>
<div>
<div class="float-left">Quantity Allocated:</div>
<div class="float-right">#Html.DisplayFor(_item => _item.Item.Quantity)
#Html.HiddenFor(_item => _item.Item.Quantity)
</div>
<div class="clear"></div>
</div>
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
#for (int i = 0; i < Model.ListItemUtility.Count; i++)
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
#Html.TextBoxFor(_item => _item.ListItemUtility[i].Quantity, new { #class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
</div>
#if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
</div>
}
I'd like my user to dynamically add a new row to the view, and then when the view is submitted, all the rows would be included.
So far I am at the beginning and I am trying this:
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
Where the partial view rendered would be identical to the div used in the table. But I am not sure how could I make this happen, should I use a jQuery call? How might I do this?
EDIT Okay, so I have tried something in jquery which VISUALLY does what I want:
<script type="text/javascript">
$(document).ready(function() {
$("#addUtility").click(function() {
$.get("#Url.Action("AddItemUtilityRow")", {
}, function(data) {
$('#itemUtilityZone').append(data);
});
});
});
</script>
So, as I said, this works but only partially because when the user submits only the default number of items in the list is submitted. How can I make it so that each time the user add a row it adds up to the model and gets later submitted?
Woah! It was more complex than I thought, but thanks to this link : http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ I was able to make the whole thing work!
I first transfered every row created in a partial view like this:
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
#foreach (var utilityRow in Model.ListItemUtility)
{
Html.RenderPartial("ItemUtilityRow", utilityRow);
}
</div>
Which renders like this:
#using HtmlHelpers.BeginCollectionItem
#model MyApp.Utilities.ItemListingUtility
#using (Html.BeginCollectionItem("listItems"))
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
#Html.TextBoxFor(_item => _item.Quantity, new { #class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
Note: for the Html.BeginCollectionItem Html Helper, I had to search a bit for Steven Sanderson's Helper which he mentions in the upper link. You can find it here:
https://github.com/danludwig/BeginCollectionItem
Next, my javascript call looks like this:
$(document).ready(function() {
$("#addUtility").click(function () {
$.ajax({
url: '#Url.Action("AddItemUtilityRow")',
cache: false,
success: function(html) {
$('#ItemUtilityZone').append(html);
}
});
});
});
And the controller method that adds a new row:
[HttpGet]
public ActionResult AddEbayUtilityRow()
{
return PartialView("ItemUtilityRow", new ItemListingUtility());
}
And the rows shows just fine now. The catch is, how do I catch it back in my post method? Well, following Steve Sanderson's blog, I understood that the listItems variable was actually the name of the collection which would be sent back to the post method.
So by adding this parameter to the controller post method:
IEnumerable<EBayListingUtility> listItems
The list is indeed sent back to the post method with the count being what it is supposed to be. Hurray!
We approach this in one of two ways:
1.) Client-side approach - you can use jquery/knockout whatever to append items to your table. This is fine for simple additions, but negates the use of c# in the view.
2.) Server-side approach (and usually used) - Basically, post your viewmodel back to an action that manually adds a list item;
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
[HttpPost]
public ActionResult AddItemUtilityRow(BackstoreInventoryUtility viewModel)
{
viewModel.ListItemUtility.Add(new ItemListingUtility());
return PartialView(viewModel);
}
We have a number of ways using jquery of 'posting' to a different action (the one that simply adds an item). I would consider using jquery's ajax call to accomplish this.
But the premise is the same:
send the data from your page to the server
manipulate the data
reuse the view you created

ajax call triggering multiple times at once

The scenario is that Im basically trying to create 4 dropdowns, that repopulate when one is changed. Aka the filters are cascading, each other.
So I decided to put it in an Ajax call. And basically, it takes the params, decides what select lists should be returned. Then replaces the old 4 dropdowns with new dropdowns. (replaces current partial with new partial)
Except, for some reason I am getting it calling the controller once.. then twice.. then 4 times.. etc. As if the old ones are not being removed/ replaced. just hidden?..
Visually, I see what Id expect. THe dropdowns change selection options.
below is the code. (p.s. sorry if some variable names are typos, they have been changed for posting on here)
Controller:
public class Filter
{
public IEnumerable<SelectListItem> List1;
public IEnumerable<SelectListItem> List2;
public IEnumerable<SelectListItem> List3;
public IEnumerable<SelectListItem> List4;
}
public ActionResult GlobalFilter(String l1, String l2, String l3, String l4)
{
Filter filter = new Filter();
filter.List1 = ...selectList
filter.List2 = ...selectList
filter.List3 = ...selectList
filter.List4 = ...selectList
return PartialView(filter);
}
view:
<div id="filterPartial">
#Html.Action("GlobalFilter", "Header")
</div>
partial view:
#model ns.Controllers.HeaderController.Filter
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
#using (Ajax.BeginForm("GlobalFilter", "Header", new AjaxOptions { UpdateTargetId = "filterPartial" }))
{
#Html.DropDownList("l1", Model.List1, new { })
#Html.DropDownList("l2", Model.List2, new { })
#Html.DropDownList("l3", Model.List3, new { })
#Html.DropDownList("l4", Model.List4, new { })
}
<script type="text/javascript">
$('#l1').change(function () {
$(this).parents('form').submit();
});
</script>
Move this outside your partial:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
I also moved the change script outside the partial as well and changed it to handle dynamic content like so:
<script type="text/javascript">
$('#filterPartial').on('change', '#l1', function () {
$(this).closest('form').submit();
});
</script>

How to add DatePicker to my MVC3 C# site

I've been trying to figure out how to do this all afternoon, but can't seem to get a grip on how to get DatePicker to work with my site.
Also, if possible I would like to remove the Time part of the DateTime, as I'm only interested in the Date the entry gets posted.
I've included the code so far below, I've pretty much removed everything I've attempted so far so it is back to the beginning. I've referenced the JQuery files at the top of _Layout.cshtml which I think is correct.
I've looked at many guides on the web, but struggled to make much sense of them, although I will keep reading.
I've added these lines to the top of my _Layout.cshtml (I've also unpacked and copied the JQuery folders to the locations referenced below as well)
<link href="#Url.Content("~/Content/jquery-ui/redmond/jquery-ui-1.8.21.custom.css")" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-1.8.11.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/ui/minified/jquery.ui.core.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/ui/minified/jquery.ui.datepicker.min.js")" type="text/javascript"></script>
My View code looks like this:
#model dale_harrison.Models.News
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>News</legend>
<div class="editor-label">
#Html.LabelFor(model => model.News_Entry)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.News_Entry)
#Html.ValidationMessageFor(model => model.News_Entry)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.News_Date)
</div>
<div class="editor-field">
#*#Html.EditorFor(model => model.News_Date)*#
#Html.EditorFor(model => model.News_Date, new object{ id = "news_date" } )
#Html.ValidationMessageFor(model => model.News_Date)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
<script type="text/javascript">
$(document).ready(function () {
$("#news_date").datepicker({ dateFormat: 'dd/mm/yy' });
});
</script>
My Model is here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace dale_harrison.Models
{
public class News
{
public int ID { get; set; }
public string News_Entry { get; set; }
public DateTime News_Date { get; set; }
}
public class NewsDBContext : DbContext
{
public DbSet<News> News_Entries { get; set; }
}
}
Many thanks in advance to anyone for helping
Try this first:
Add your html code without the html helper:
<input type="text" id="news_date" name="news_date" />
Or, if you want to add the id with the Html helper:
#Html.EditorFor(model => model.News_Date, new { id = "news_date" } )
And then add this javascript code:
$(document).ready(function () {
$("#news_date").datepicker({ dateFormat: 'dd/mm/yy' });
});
You can try this first to check if you have set it all the configuration in your project correctly. You should solve your problem with those lines.
After you get this working in your site, read about html helpers and how to encapsulate code so you do not have to rewrite this everytime.
Hope it helps!
On the Views/Shared/EditorTemplates folder you need to create a template for DateTime types, so every time you use #Html.EditorFor MVC display whatever you define on your Template
You may also wish to hide the time from the text box so that you see "08/11/2007" as opposed to "08/11/2007 00:00:00".
To do this, add "[DataType(DataType.Date)]" to your news class as below;
public class News
{
public int ID { get; set; }
public string News_Entry { get; set; }
[DataType(DataType.Date)]
public DateTime News_Date { get; set; }
}
EditorFor neglects extra attributes. Either use TextBoxFor or write EditorFor template...

Categories

Resources