1

In my ASP.NET MVC 5 application I have the following index view of projects (retrieved from the Projects table in the database).

@using Leepio.Models
@using Microsoft.AspNet.Identity
@model ApplicationTwoViewModel

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create" ,"Projects")
</p>
<table class="table">
    <tr>
        <th>
            @Html.ActionLink("Title", "Index",  new {SortOrder = (ViewBag.SortOrder==null?"Asc":(ViewBag.SortOrder=="Asc"?"Desc":"Asc")), SortBy = "Title"})

        </th>
        <th>
            @Html.ActionLink("Application Deadline", "Index", new {SortOrder = (ViewBag.SortOrder == null ? "Asc" : (ViewBag.SortOrder == "Asc" ? "Desc" : "Asc")), SortBy = "ApplicationDeadline"}) <br/>
            @Html.ActionLink("Hourly Rate (DKK)", "Index", new { SortOrder = (ViewBag.SortOrder == null ? "Asc" : (ViewBag.SortOrder == "Asc" ? "Desc" : "Asc")), SortBy = "HourlyRate" })
        </th>
       <th>
            @Html.ActionLink("Skill requirements", "Index", new { SortOrder = (ViewBag.SortOrder == null ? "Asc" : (ViewBag.SortOrder == "Asc" ? "Desc" : "Asc")), SortBy = "RequiredSkills" })
        </th>


    </tr>

@foreach (var item in Model.Model1) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ApplicationDeadline)<br/>
            @Html.DisplayFor(modelItem => item.HourlyRate)
        </td>
       <td>
            @Html.DisplayFor(modelItem => item.RequiredSkills)
        </td>


        <td>

            @if(item.UserId == User.Identity.GetUserId()) 

            {
                @Html.ActionLink("Edit", "Edit",  new {id = item.ProjectId})

                @Html.ActionLink("Delete", "Delete", new {id = item.ProjectId}) 
            }

            @Html.ActionLink("Details", "Details", new {id = item.ProjectId}) |

            @Html.ActionLink("Apply", "Create", "Applications" , new { id = Model.Model2.ApplicationId }) |
        </td>
    </tr>
}

</table>
@{
    double TotalPage = @ViewBag.TotalPages;
}

<ul class="pagination">
    @for (int i = 1; i <= TotalPage; i++)
    {

        if (i == ViewBag.Page)
        {
            <li class="active"> @Html.ActionLink(i.ToString() + " ", "Index", "Projects", new { SortOrder = (ViewBag.SortOrder == null ? "Asc" : ViewBag.SortOrder), SortBy = (ViewBag.SortBy == null ? "Title" : ViewBag.SortBy), Page = i })</li>
        }
        else
        {
            <li>
                @Html.ActionLink(i.ToString() + " ", "Index", "Projects", new { SortOrder = (ViewBag.SortOrder == null ? "Asc" : ViewBag.SortOrder), SortBy = (ViewBag.SortBy == null ? "Title" : ViewBag.SortBy), Page = i })
            </li>
        }

    }
</ul>

I am using the ApplicationTwoViewModel I have created, which basically has two view models in it:

public class ApplicationTwoViewModel
{
    public IEnumerable<Project> Model1 { get; set; }
    public Application Model2 { get; set; }
}

I am trying to make this ActionLink

 @Html.ActionLink("Apply", "Create", "Applications" , new { id = Model.Model2.ApplicationId }) |

Create a new "Application" from the Create ActionRestult that is in the ApplicationsController:

public ActionResult Create()
{
    return View();
}

// POST: Applications/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ApplicationId,ProjectId,UserId,CoverLetter")] Application application)
{
    if (ModelState.IsValid)
    {
        db.Applications.Add(application);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(application);

Application Model:

public class Application
{
    public int ApplicationId { get; set; }
    public int ProjectId { get; set; }
    public string UserId { get; set; }
    public string CoverLetter { get; set; }
}

So right now I am trying to make the "Apply" ActionLink so the Create view receives the ProjectId from the Projects Index view, like the Edit/Delete/Details receives the ProjectId and am not sure how to do this.

Right now it works but you have to manually insert the ProjectId and the UserId (the one logged in, should be User.Identity.GetUserId() but I am not sure where to add it in this context), and I want them to be retrieved behind the scenes so the user only has to write the CoverLetter and then creates the application.

AGB
  • 2,230
  • 1
  • 14
  • 21
crystyxn
  • 1,411
  • 4
  • 27
  • 59

1 Answers1

2

You need to add a parameter in the Create() method and the initialize you model with that value and pass it to the view

public ActionResult Create(int projectId)
{
    Application model = new Application()
    {
        ProjectId = projectId 
    };
    return View(model);
}

and modify you link to pass the value (currently its adding the value as a html attribute, not a route value (note the 5th parameter)

@Html.ActionLink("Apply", "Create", "Applications" , new { projectId = item.ProjectId }, null)

Then in the view, include a hidden input for ProjectId so its submitted to the POST value

@Html.HiddenFor(m => m.ProjectId)

Note that your UserID value should be added in the POST method before you save the Application (not in the model or in the view)

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ProjectId, CoverLetter")] Application application)
{
    if (ModelState.IsValid)
    {
        applicatio.UserId = User.Identity.GetUserId(); // set the user here
        db.Applications.Add(application);
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(application);
}

Side note: Its not really clear what the purpose of your ApplicationTwoViewModel model is for. All you ever use in the view is IEnumerable<Project> so your Model2 property seems unnecesary

  • What if my Create is like this: public ActionResult Create([Bind(Include = "ApplicationId,ProjectId,UserId,CoverLetter")] Application application) { if (ModelState.IsValid) { db.Applications.Add(application); db.SaveChanges(); return RedirectToAction("Index"); } return View(application); } should I get rid of the Bind? – crystyxn May 07 '16 at 12:31
  • What you really should be doing is using a view model - `ApplicationVM` which contains just properties `ProjectId` and `CoverLetter`. But if your using the data model, then it should be just `[Bind(Include="ProjectId, CoverLetter")]` because those are the only 2 properties you need –  May 07 '16 at 12:34
  • I'm afraid I don't really get what you are trying to say. Are you saying I should create a new view model or use ApplicationViewModel ? – crystyxn May 07 '16 at 12:41
  • Do you already have a class name `ApplicationViewModel`? (the one you showed is `ApplicationTwoViewModel` which seems a bit pointless since you never use or need the `Model2` property. The view you have shown could be just `@model IEnumerable` and just change the loop to `@foreach (var item in Model) {`) –  May 07 '16 at 12:43
  • Yes I have a view model called "Application" , which the Create view is currently using. Should I just use it and drop the ApplicationTwoViewModel ? How should I modify the Create() like you said earlier (delete ApplicatoinId and UserId) ? – crystyxn May 07 '16 at 12:48
  • `Application` is a data model. Not a view model. A view model would be `public class ApplicationVM { public int ProjectId { get; set; } public string CoverLetter { get; set; } }` and in the GET method initialize an instance of `ApplicationVM` and set its `ProjectId` property instead of `Application`. Then in the `Create.cshtml` view, its `@model ApplicationVM` –  May 07 '16 at 12:51
  • And finally, in the POST method, its `public ActionResult Create(ApplicationVM model)` then you initialize a new instance of `Application`, set its properties based on the view model, plus set the value of `UserId` based on the current user and then save the data model. –  May 07 '16 at 12:53
  • I think I got it to work somehow inside the controller Create() method: retrieving the UserId like so it works: application.UserId = User.Identity.GetUserId(); but how do I retrieve the selected project's projectId ? – crystyxn May 07 '16 at 12:53
  • I would also recommend you read the answers to [What is ViewModel in MVC?](http://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc) –  May 07 '16 at 12:54
  • Sorry, I did not understand what you meant by _"but how do I retrieve the selected project's projectId"_. But I assumed you have worked it out –  May 07 '16 at 12:56
  • Actually I haven't. I was confused about your suggestion to use "new { projectId = item.ProjectId }". In the projects index there some ActionLinks like Create New, Edit, Delete, Details. The Create() method in the ProjectsController still uses the Bind attribute with all the fields in the model (UserId, ProjectId etc) I basically initialized the userId as such: project.UserId = User.Identity.GetUserId(); and so it was quite easy because of the User.Identity. But I need this but for the current Project. So I need to find something similar to FindById(); – crystyxn May 07 '16 at 13:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/111290/discussion-between-stephen-muecke-and-crystyxn). –  May 07 '16 at 13:04