views:

105

answers:

7

I have the following scenario: my website displays articles (inputted by an admin. like a blog).

So to view an article, the user is referred to Home/Articles/{article ID}.

However, the user selects which article to view from within the Articles.aspx view itself, using a jsTree list.

So I what I need to do is to be able to differentiate between two cases: the user is accessing a specific article, or he is simply trying to access the "main" articles page. I tried setting the "Articles" controller parameter as optional (int? id), but then I am having problems "using" the id value inside the controller.

What is the optimal manner to handle this scenario? Perhaps I simply need a better logic for checking whether or not an id parameter was supplied in the "url"?

I am trying to avoid using two views/controllers, simply out of code-duplication reasons.

A: 

Make the parameter required then set a default value in the routing that is a value that isn't a valid index and indicates to your action that it should render the main page.

Lazarus
+1  A: 

this to me sounds like two separate pages and should be treated as such. You have the "Main" view page and the "articles" page.

I would split it into two actions. They should not be much dupliation at all really, both should be doing a call to get the same ModelView and the article will simply get the a extension to that!

Steve
+1  A: 

Define a default value for the Id that you know indicated no value was supplied - usually 0.

public ActionResult Articles([DefaultValue(0)]int Id)
{
  if (Id == 0)
    // show all
  else
    // show selected
..
Clicktricity
thanks, this is exactly what I needed.
Tom Teman
Using the DefaultValue attribute seems like a very confusing way to solve this problem.
Ryan
hmmmm... perhaps I will use 2 views after all. I need the main introductory article page to be more "interesting" HTML wise.
Tom Teman
+1  A: 

First of all, I agree with @Steve :). But if you really want to use

int? id

you can just check in your controller method if the id is set using a simple

if(id == null)

and if so, load all articles from your DB (or something alike) and pass these to your view (either directly, or by using a view model). If the id is set you just load the article having that id from your DB and send that to the view (possibly in a list as well if you dont use view models)?

Than in your view just load all articles in the list with articles supplied to the view. Which contains either all or just one.

Complete dummy code

public ActionResult showArticles(int? id){
   List<Articles> aList;
   if(id == null){
       aList = repository.GetAllArticles().ToList();
   }else{
      aList = new List<Articles>(); 
      aList.add(repository.GetArticleByID(id));
   }

   return View(aList);
}

Your View has something like:

<% Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<List<Articles>>"%>

foreach(Articles a in Model)
    //display article

And you call it using either of the next two options:

html.ActionLink("one article","showArticles","Articles",new {id = aID},null}

html.ActionLink("all articles","showArticles","Articles"}
bastijn
+2  A: 

The easiest solution is to have two different actions and views but name the actions the same.

public ViewResult Articles()
{
   //get main page view model
   return View("MainPage", model);
}

public ViewResult Articles(int id)
{
   // get article view model
   return View(model);
}
Ryan
Ok, but when I try to call the main articles page using:<%= Html.ActionLink("Articles", "Articles", "Home")%>I get an error:The current request for action 'Articles' on controller type 'HomeController' is ambiguous between the following action methods:System.Web.Mvc.ActionResult Articles() on type dr_teman_MVC.Controllers.HomeControllerSystem.Web.Mvc.ActionResult Articles(Int32) on type dr_teman_MVC.Controllers.HomeController
Tom Teman
Does your (default) route specify a default id? I suppose that could be an issue. I would probably name one action Articles and the other Article.
Ryan
A: 

Well, this is the combined solution I am using:

Using same controller, with DefaultValue:

public ActionResult Articles([DefaultValue(0)]int id)

If DefaultValue was used, I refer to "MainArticles" view. If an article ID was provided - I refer to the "Articles" view with the appropriate article passed inside the ViewModel.

In both cases the ViewModel is populated with the data both views need (complete article and category lists).

Thanks everyone for your help!

Tom Teman
+1  A: 

Use separate actions, like:

public ActionResult Articles() ...
public ActionResult Article(int id) ...

Alternatively move it to an Articles controller (urls using the default route will be: Articles and Articles/Detail/{id}):

public class ArticlesController : Controller
{
    public ActionResult Index() ...
    public ActionResult Detail(int id) ...
}

If you still must use it like you posted, try one of these:

public ActionResult Articles(int id = 0)
{
     if(id == 0) {
         return View(GetArticlesSummaries());
     }
     return View("Article", GetArticle(id));
}
public ActionResult Articles(int? id)
{
     if(id == null) {
         return View(GetArticlesSummaries());
     }
     return View("Article", GetArticle(id.Value));
}
eglasius
"Alternatively move it to an Articles controller (urls using the default route will be: Articles and Articles/Detail/{id}):"this is probably the best way to go about it. Thanks!
Tom Teman
@Tom glad to help, upvote + accept :)
eglasius