0

I have a problem with a mvc4 project.

I try sending some text from a view to a controller which then add it in a model and then I try to retrieve the text in another view.

The first views code looks like this:

@{
    Html.BeginForm("Result", "Search", "test");
}

<div>
</div>

The controller code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TheLibrary.Models;

namespace TheLibrary.Controllers
{
    public class SearchController : Controller
    {
        public ActionResult Result(string text)
        {
            var searchCriteria = new Search { searchCriteria = text };

            ViewBag.Search = searchCriteria;
            ViewData["ViewSearch"] = searchCriteria;
            TempData["TempSearch"] = searchCriteria;
            return View();
        }
    }
}

My models code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TheLibrary.Models
{
    public class Search
    {
        public string searchCriteria { get; set; }
    }
}

The second views code:

@Model TheLibrary.Models.Search
@{
    var text = (ViewBag.Search as TheLibrary.Models.Search).searchCriteria;
}

<div>
    @text
</div>

The result I get is "TheLibrary.Models.Search" rather than the expected "test".

What am I doing wrong?

Michael Tot Korsgaard
  • 3,892
  • 11
  • 53
  • 89

2 Answers2

1

@Model should not be capitalized. Then you can just write:

@model TheLibrary.Models.Search
<div>
    @Model.SearchCriteria
</div>

To be clear, the first @model should not be capitalized when you are declaring the type. After that you capitalize it to reference it.

Also I am not sure why you are using the ViewBag and not just passing in the model to the view.

return View(search);

Also I don't recommend this naming scheme:

var searchCriteria = new Search{ searchCriteria = text };

becuase then you have write searchCriteria.searchCriteria. Instead call it what it is:

var search = new Search...
MondayPaper
  • 1,569
  • 1
  • 15
  • 20
1

With all due respect, you really need to work through some MVC tutorials. I'm not usually a fan of Microsoft documentation (especially their insipid 'quickstarts') but their MVC tutorials are actually quite good. That said, let me clear some things up for you.

Firstly, the whole point of creating a view model (Search in this case) is so that you don't have to use ViewBag or ViewData, to give you a guaranteed way of accessing your model's data. ViewBag and ViewData are virtually the same thing. The main point here though is the idea to move away from these in favour of using strongly-typed views.

So, that begs the question, what is a strongly-typed view? Put simply, a strongly-typed view is a view that requires a specific type to be passed to it. You specify a strongly-typed view by using the @model directive at the very top of the view. In your case, this would be:

@model TheLibrary.Models.Search

Note: The lowercase m. Do not confuse this with @Model which is a way of accessing your strongly-typed model.

Being as we're telling our view exactly what type we're going to pass to it, we can use @Model to access the properties of the model, like so:

<div>
    @Model.searchCriteria
</div>

(Please also note that guidelines suggest using Pascal Case for properties.)

Now, let's clear up the issue with your controller. Firstly, as we're already using our model, as discussed above, there is no point in using ViewBag or ViewData. TempData serves an altogether different purpose and has no bearing on your question. (See this answer for when you might want to use it.) In that sense, it is also not needed. Lastly, you would simply pass the model to the view itself:

public ActionResult Result(string text)
{
    var model = new Search { searchCriteria = text };

    return View(model);
}

Now, your complete view would look like this:

@model TheLibrary.Models.Search

<div>
    @Model.searchCriteria
</div>

However, as stated at the start of this answer, I really believe you should look into a few tutorials because there are things here that you're still not aware of (such as passing searchCriteria directly to your view without any kind of validation).

Update per comments

Your view wants to look something like this:

@using (Html.BeginForm("Result", "Search", FormMethod.Get))
{
    @Html.TextBox("text")
    <input type="submit" value='Search' />
}

There are a couple of things to note.

Firstly, the FormMethod.Get is ensuring this form's data is sent via GET instead of POST. This is what puts text into the query string in your result's URL. So it makes it look something like:

http://somewebsite.com/Search/Result?text=test

This is good because it means if the user refreshes the page, they're not asked to resubmit the form and it also acts in the same way every time the page is accessed from that URL.

Secondly, @Html.TextBox("text"). The string you supply to the TextBox() method is the name that is given to that textbox, and it is also the name that is used to lookup the data in that textbox. That means it has to match the name of the parameter in your Result action (or part of a model, but let's keep things simple for now), so this part is important for it to work correctly.

So the idea here is that for you to get the correct data for your searchCriteria in your Result view, using your example, is that the user would type test into the textbox. They'd then click the Search button which would get MVC to call the Result action. When doing so, it would use what's called 'model binding' to match the value of the text textbox to the text parameter in your action. That means when we get here:

public ActionResult Result(string text)
{
    // ...
}

text will have the value of whatever someone has typed into the textbox. You then pass that to your model which is then passed to the view.

Community
  • 1
  • 1
John H
  • 14,422
  • 4
  • 41
  • 74
  • I'm in this very moment going through the nice tutorial. Had a hard time finding one, that doesn't just jump half way in. But on a more urgent note. I've found out that my `Html.BeginForm` doesn't send informations to the `Controller`. You whould be a great help if you could give an answer on why. Cause that is really the only thing keeping me from having this a solution ^^ – Michael Tot Korsgaard Nov 07 '13 at 19:23
  • 1
    @MichaelTotKorsgaard Good stuff, and I agree, it's a little difficult finding ones that don't assume a lot up front. Edit: Nevermind, I apparently can't read. I'll take a look now. – John H Nov 07 '13 at 19:25
  • 1
    @Html.BeginForm(){ } //notice the bracket location – MondayPaper Nov 07 '13 at 19:39
  • Side question: Can you have `Html.TextBox` check if it's empty? – Michael Tot Korsgaard Nov 07 '13 at 20:12
  • 1
    @MichaelTotKorsgaard You can do that with JavaScript on the client-side, but to do that on the server, you can check it in your action: `if (String.IsNullOrEmpty(text)) { // do something }`. This is why you should look into using viewmodels, because the typical pattern is to model bind to your view model, then check `ModelState.IsValid` to see if the form was filled in correctly or not. From there, you can pass the model back to the view to get the user to fill in the form correctly. – John H Nov 07 '13 at 20:19