1

I am converting my way of thinking and programming from Webforms to MVC3. For the record, it hurts my head.

So I am making a calendar that has a month view (page only shows the days and events for that month). I'm putting two buttons Previous and Next to go from month to month. In Webforms, any change would just happen on Calendar.aspx. However, it seems that I'll have to make two ActionResults (both HttpPost). One goes to a Previous view and another to a Next view. Just to get the same effect. I was hoping to just update the calendar on the Index ActionResult/View but I don't see how to do that. My Index view is below.

So long story short, is there a way to update the Calendar and return to the Index view? Or do I have to make two other Views (Previous and Next) to show the new month when I hit the corresponding buttons (Previous and Next)?

@model HTMLMVCCalendar.Models.MonthModel

@{
    ViewBag.Title = "Home Page";
    int month = Model.Month;
    int year = Model.Year;
    int numberOfDays = DateTime.DaysInMonth(year, month);
    int startDay = (int)(Convert.ToDateTime(month + "/1/" + year).DayOfWeek);
    int startCount = 1;
}

<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
</p>
<div>  
    <table id="calendar">
        <thead>
            <tr>
                <th id="monthHeader" colspan="7">
                    <h3>
                    @{
                        @Convert.ToDateTime(month + "/1/" + year).ToString("MMMM");
                    }
                    </h3>
                </th>
            </tr>
            <tr>
                <th>Sun</th>
                <th>Mon</th>
                <th>Tues</th>
                <th>Wed</th>
                <th>Thur</th>
                <th>Fri</th>
                <th>Sat</th>
            </tr>
        </thead>
        <tbody>
            <tr>
            @for (int i = 0; i < startDay; ++i)
            {
                @:<td><div><span>&nbsp;</span></div><div><span>&nbsp;</span></div></td>
            }
            @for (int j = startDay; j < ((numberOfDays + startDay) + 1); ++j)
            {
                <td>
                    <div><span>@startCount</span></div>
                    <div>
                        <span>
                            @{
                                var todaysEvents = Model.AllDays.ToList().FindAll(d => d.CalDate.Day == startCount);
                                foreach(HTMLMVCCalendar.Models.CalendarModel eventsToday in todaysEvents)
                                {
                                    foreach(HTMLMVCCalendar.Models.EventModel eventToday in eventsToday.CalEvents)
                                    {
                                        <text>
                                            &nbsp;@eventToday.DayCode:<br />
                                            &nbsp;@eventToday.Subject:<br />
                                            &nbsp;@eventToday.EventDesc<br /><br />
                                        </text>
                                    }                                 
                                }
                            }
                        </span>
                    </div>
                </td>
                            if ((j + 1) % 7 == 0)
                            {
                    @:</tr><tr>
                }
                ++startCount;
            }
            </tr>
        </tbody>
    </table>
    <div>
        <input id="previous" type="submit" value="submit" />
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <input id="next" type="submit" value="submit" />
    </div> 
</div>

UPDATE: I've tried this and I think it should work. Not sure why it didn't?

    <div>
        @using (Html.BeginForm("Previous", "Home"))
        {
            <input id="previous" type="submit" value="Previous" />
        }
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        @using (Html.BeginForm("Next", "Home"))
        {
            <input id="next" type="submit" value="Next" />
        }
    </div> 


public ActionResult Index()
{
    ViewBag.Message = "Welcome to ASP.NET MVC!";

    int month;
    int year;

    int.TryParse(ViewBag.Year, out year);
    int.TryParse(ViewBag.Month, out month);
    Calendar monthEventsCal = new Calendar();

    HTMLMVCCalendar.Models.MonthModel allMonthEvents = monthEventsCal.monthEvents(year, month);
    return View("Index", allMonthEvents);            
}

[HttpPost]
public ActionResult Previous(HTMLMVCCalendar.Models.MonthModel prevMonth)
{
    Calendar monthEventsCal = new Calendar();
    var newMonth = monthEventsCal.previousMonth(prevMonth.Year, prevMonth.Month);

    ViewBag.Month = newMonth.Item2;
    ViewBag.Year = newMonth.Item1;
    return RedirectToAction("Index");
}

[HttpPost]
public ActionResult Next(HTMLMVCCalendar.Models.MonthModel nextMonth)
{
    Calendar monthEventsCal = new Calendar();
    var newMonth = monthEventsCal.nextMonth(nextMonth.Year, nextMonth.Month);

    ViewBag.Month = newMonth.Item2;
    ViewBag.Year = newMonth.Item1;
    return RedirectToAction("Index");
}
dotnetN00b
  • 5,021
  • 13
  • 62
  • 95
  • I realized my mistake. I really need to get out of the Webforms mentality. Ugh. Note to self (again): Razor values leave scope once page is rendered to browser. – dotnetN00b Apr 12 '12 at 21:12
  • Query string or hidden labels to the rescue! – dotnetN00b Apr 12 '12 at 21:25
  • ViewBag data is lost after RedirectToActions. If you do do it that way, I would use TempData. Alternatively, you could have both previous, and next post to index, determine which button was pressed, then redirect to the index get action where you pass in the new month. – DMulligan Apr 13 '12 at 00:18

2 Answers2

1

Suggested potential way to do it

public virtual ActionResult Index(int? month)
{
    int relativeMonth = 0;
    if(month.HasValue)
        relativeMonth = month.Value;

    //Create ViewModel fetching calender data using relativeMonth value.
    //For example if relativeMonth is 0 fetch current month view model.
    //If relative month is -3 fetch calendar data from three months ago.

    if(Request..IsAjaxRequest()) //Optional. If you want ajax.
        return PartialView("Calendar", model);

    return View(model);
}

Using this method your next and and previous buttons are now links where Previous will link to relativeMonth - 1 and next is relativeMonth + 1. I don't think making them posts is necessary since you're not actually submitting any form data (using this method I'd make next and previous links styled like buttons).

If you do want to enable ajax, I'd stick your calendar view in a seperate cshtml and call it as a partial view in index.cshtml.

DMulligan
  • 8,993
  • 6
  • 33
  • 34
  • I take it that month and Model are actually the same? – dotnetN00b Apr 12 '12 at 20:11
  • @dotnetN00b month in my code is an optional integer that could contain the relative month you want in relation to the current date. model would be an initiation of your HTMLMVCCalendar.Models.MonthModel, where you'll use the month variable to make sure you build the correct one. – DMulligan Apr 12 '12 at 20:19
  • I'm trying to keep it simple for now, so I don't need ajax. However, after banging my head for the last couple of days. I see where you are going with this. I have two questions though. One, why the virtual keyword? Two, the month variable is passed from the routes or querystring? Or something else? – dotnetN00b Apr 17 '12 at 19:13
  • 1
    @dotnetN00b I use T4MVC so I'm in the habit of always marking actions virtual (http://stackoverflow.com/questions/6238068/should-i-make-my-asp-net-mvc-controller-actions-virtual). Based on your followup question to this you might like T4MVC. I'd make month an optional value at the end of your route(but it would work in querystring too). – DMulligan Apr 17 '12 at 19:45
  • Actually I made month and year optional parameters in the routes. So I'm glad I'm picking up at least the basic concepts. – dotnetN00b Apr 17 '12 at 22:55
  • @dotnetN00b That works, a month year URL with the true date will be easier to read than my relative integer example. However, when you provide the routing you can only have one optional parameter. So if you do it by month and year I wouldn't let either parameter be optional. – DMulligan Apr 18 '12 at 03:26
  • [My routing code](http://stackoverflow.com/q/10193709/428757). Did I do that wrong? The routing that is. – dotnetN00b Apr 18 '12 at 03:33
0

There are a couple of ways to do this, the simplest is just do an ajax call and update your view with the returned data http://bob-the-janitor.blogspot.com/2010/12/easy-ajax-with-aspnet-mvc-and-jquery.html if you want to get fancy you can even return a view from the ajax call and replace you current view as shown here http://bob-the-janitor.blogspot.com/2011/11/more-ajax-with-mvc-using-partial-views.html

Bob The Janitor
  • 20,292
  • 10
  • 49
  • 72