This is my first experience with lists in general and I have kinda complex scenario. I want to add an object of type Story that has a list of Sentences that can be added dynamically. Sentence has a one-to-one relationship with Image and another one-to-one relationship with Audio (that are optional to add). I managed to add the sentences list to the database along with the story object. But I have no idea where to start with the other two entities.
Here are the models for each entity:
public class Story
{
public int StoryId { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public DateTime Date { get; set; }
public virtual IEnumerable<Sentence> Sentences { get; set; } // one to many Story-Sentence
}
Sentence class:
public class Sentence
{
public int Id { get; set; }
public string SentenceText { get; set; }
public virtual Audio Audio { get; set; } // one to one Sentence-Audio
public virtual Image Image { get; set; } // one to one Sentence-Image
}
Image class:
public class Image
{
[Key]
[ForeignKey("Sentence")]
public int Id { get; set; }
public string ImageSelected { get; set; }
public virtual Sentence Sentence { get; set; }
}
And the Audio class is exactly like the Image class. The view.
@model Story
<div id="editorRows">
@foreach (var item in Model.Sentences)
{
<partial name="_SentenceEditor" model="item" />
}
</div>
<a id="addItem" asp-action="BlankSentence" asp-controller="StoryTest">Add Sentence...</a> <br />
<input type="submit" value="Finished" />
The partial view
@model Sentence
<div class="editorRow">
@using (Html.BeginCollectionItem("sentences"))
{
<span>Name: </span> @Html.EditorFor(m => m.SentenceText);
}
@using (Html.BeginCollectionItem("sentences"))
{
<span>Image: </span> @Html.EditorFor(m => m.Image.ImageSelected);
}
<a href="#" class="deleteRow">delete</a>
</div>
and I have some javascript that add and remove rows dynamically.
Finally in the Controller I'm just saving the model in the database
[HttpPost]
public async Task<IActionResult> AddTwo(Story model/*IEnumerable<Sentence> sentence*/)
{
if (ModelState.IsValid)
{
_db.Story.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
In short, I want to add an Image and Audio along with the sentence. And also be able to access the entire row correctly for editing.
I made a demo based on your codes. You can store data in formData then use ajax pass it to the controller action.
Main View:
@model Story
<form asp-action="AddSentence" method="post">
<div id="editorRows">
<input type="hidden" name="" value="@Model.StoryId" />
@foreach (var item in Model.Sentences)
{
<partial name="_SentenceEditor" model="item" />
}
</div>
<a id="addItem" asp-action="BlankSentence" asp-controller="StoryTest">Add Sentence...</a>
<br />
<input type="submit" id="submit" value="Finished" />
</form>
@section scripts {
<script>
$("#submit").click(function (e) {
e.preventDefault();
var formData = new FormData();
$("input[name='Audio.AudioSelected']").each(function (i) {
var AudioSelected = $(this).val();
formData.append("Sentences[" + i + "].Audio.AudioSelected", AudioSelected);
});
$("input[name='Image.ImageSelected']").each(function (i) {
var ImageSelected = $(this).val();
formData.append("Sentences[" + i + "].Image.ImageSelected", ImageSelected);
});
$("input[name='SentenceText']").each(function (i) {
var SentenceText = $(this).val();
formData.append("Sentences[" + i + "].SentenceText", SentenceText);
});
$.ajax({
method: 'post',
url: "StoryTest/AddSentence",
data: formData,
processData: false,
contentType: false,
success: function () {
}
});
});
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#editorRows").append(html); }
});
return false;
});
$("a.deleteRow").on("click", function () {
$(this).parents("div.editorRow:first").remove();
return false;
});
</script>
}
Partial View:
@model Sentence
<div class="editorRow">
<span>Name: </span> @Html.EditorFor(m => m.SentenceText)
<span>Audio: </span> @Html.EditorFor(m => m.Audio.AudioSelected)
<span>Image: </span> @Html.EditorFor(m => m.Image.ImageSelected)
<a href="#" class="deleteRow">delete</a>
</div>
Controller:
public IActionResult Index()
{
Story story = new Story
{
Sentences = new List<Sentence>
{
new Sentence { Id = 1, SentenceText = "AAA"
, Audio = new Audio{ AudioSelected = "True"}
, Image = new Image{ ImageSelected = "True"} },
new Sentence { Id = 2, SentenceText = "BBB"
, Audio = new Audio{ AudioSelected = "False"}
, Image = new Image{ ImageSelected = "False"}},
new Sentence { Id = 3, SentenceText = "CCC"
, Audio = new Audio{ AudioSelected = "True"}
, Image = new Image{ ImageSelected = "False"}}
}
};
return View(story);
}
[HttpPost]
public async Task<IActionResult> AddSentence(Story model)
{
if (ModelState.IsValid)
{
_db.Story.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
public IActionResult BlankSentence()
{
return PartialView("_SentenceEditor", new Sentence());
}
Result: