0

For my company, we are building a web application for an invoicing system which utilises a notification bar, similar to that of Facebook, whereby the user can drop down a menu which displays the status of transactions on the system while a total number of outstanding transactions displays next to it. Please see the image for details.

http://img210.imageshack.us/img210/4379/alertdrop.png

We are retrieving the data for this using the following code for the table within the drop-down menu:

<table id="alertDropTable" style="margin-top:10px;">

    @{
        Dictionary<string, int> dic = ViewBag.statusCount;
        if (dic != null)
        {
            for (int i = 0; i < dic.Count; i++)
            {
                if (dic.Values.ElementAt(i) > 0)
                {
                    <tr>
                        <td width="100%" style="padding-bottom:4px; padding-top:4px; padding-left:10px; padding-right:10px; vertical-align:middle;">
                            You have <strong><a style="background-color:#878787; border-radius:5px; color:White; padding:3px;">@dic.Values.ElementAt(i)</a></strong> @dic.Keys.ElementAt(i) transaction(s).
                        </td>
                    </tr>
                }
            }
        }
    }

</table>

And the following for the span, which displays the total:

<div style="float: left;">
    <a href="javascript:;" onmouseover="document.alert.src='@Url.Content("~/Content/images/alert_notification_hover.png")'" onmouseout="document.alert.src='@Url.Content("~/Content/images/alert_notification.png")'" onclick="toggle('notificationDrop');"><img src="@Url.Content("~/Content/images/alert_notification.png")" name="alert" alt="alert"/></a>

            @{
                Dictionary<string, int> dicheader = ViewBag.statusCount;
                int diccount = 0;
                if (dicheader != null)
                {
                    for (int i = 0; i < dicheader.Count; i++)
                    {
                        if (dicheader.Values.ElementAt(i) > 0)
                        {
                            diccount = diccount + @dicheader.Values.ElementAt(i);
                        }
                    }
                }
            }

    </div>

    <div id="alertTotalDiv" style="float:left;margin-top:6px; margin-left:5px;"><span id="alertTotal" style="vertical-align:middle; background-color:#878787; border-radius:5px; color:White; font-family:Georgia; font-size:20px; padding:3px; padding-left:5px; padding-right:5px;margin-top:0px;">@diccount</span></div>

This code is currently stored in the global "_layout.cshtml" file. Please excuse the roughness of the code, this is very much an early version. However, this is working fine in terms of retrieving the data on a page load. However, the system requires this information to be automatically updated every few seconds without refreshing the entire page. In essence, making a call to the controller to bring back the current data and update the <table> and <span> with the current values.

I have been asked to create an Ajax function which retrieves the data from the "AlertController" and updates the view accordingly. Please find the contents of this controller below:

public class AlertController : Controller
{

    /// <summary>
    /// Gets or sets the user service contract.
    /// </summary>
    /// <value>The user service contract.</value>
    public IUserServiceContract UserServiceContract { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="BaseController"/> class.
    /// </summary>
    protected AlertController()
    {
        this.UserServiceContract = Gateway.Instance.Resolve<IUserServiceContract>();

    }

    /// <summary>
    /// Get the AlertTypes
    /// </summary>
    /// <returns></returns>
    public virtual void ViewAlerts()
    {
        Gateway.Instance.Logger.LogInfo(string.Format("Overview Controller View:    Fetching list of alerts."));

        try
        {
            if (this.UserServiceContract != null)
            {
                var allAnnouncements = this.UserServiceContract.GetAnnoucements();
                var userAlertSettings = this.UserServiceContract.GetUserAlert();


                ViewBag.statusCount = userAlertSettings;
                ViewBag.announcements = allAnnouncements.ToList();
            }
        }
        catch (Exception ex)
        {
            Gateway.Instance.Logger.LogInfo(ex);
            throw new Exception(string.Format("Home Controller View Error:  {0} occured while fetching alerts.", ex.Message), ex);
        }
    }

I am stumped, but have been given the following example of an Ajax function, which is used to perform a different task entirely, to aid me:

$.ajax({
    url: '@Url.Action("ViewAlerts", "Alerts")',
    data: { ownerIds: ownerIds },
    traditional: true,
    dataType: 'json',
    success: function (result) {
        for (i = 0; i < ownerIds.length; i++) {
            $(".ownersTable tr[id='tablerow+" + ownerIds[i] + "']").remove();
        }
    },
    error: function (xhr, ajaxOptions, thrownError) {
        $("#confirmDiv").alertModal({
            heading: '@Language.Fail',
            body: '@Language.AlertRemoveOwnerFailed' + thrownError
        });
    }
});

Thus far, the only thing I've managed to get working was a set interval function, which makes an alert every 5 seconds!

Any guidance on this?

Pang
  • 9,564
  • 146
  • 81
  • 122
Nick Chambers
  • 181
  • 1
  • 13

3 Answers3

1

Okay, this is what you are going to need to do, first of all:

$.ajax({
    url: '@Url.Action("ViewAlerts", "Alerts")', 
    data: { ownerIds: ownerIds },
    traditional: true,
    dataType: 'json',
    success: function (result) {
        for (i = 0; i < ownerIds.length; i++) {
            $(".ownersTable tr[id='tablerow+" + ownerIds[i] + "']").remove();
        }
    },
    error: function (xhr, ajaxOptions, thrownError) {
        $("#confirmDiv").alertModal({
            heading: '@Language.Fail',
            body: '@Language.AlertRemoveOwnerFailed' + thrownError
        });
    }
});

Why is Url.Action in quotes? Just put @Url.Action ... I don't see a reason for the quotes and it may be breaking your call.

Second of all don't use ViewBag. It shouldn't be really used to hold 'data' (in my opinion). You should use a model. So this is what you can do:

Create the part of the page you want to update into a "Partial View" that is strongly typed to a model that contains the status and announcements and whatever else you need.

Use Jquery to 'Load' the Controller method that will get the data and return this 'Partial View', and load it into a Div on your screen example:

$('#div-to-load-into').load('/myactionurl/', function () { //Do something when done });

So your controller is then called, gets the data and returns the partial view with the model you created!

Good luck!

---Edit--- YOU should do above because it would be easier and faster (and if you do get to redesign you will quickly understand that, but here is an idea of how to do what you want)

This is what you need to know,

url is the action that the Ajax is going to call. You need to put url: '/controller/action'

data is the data you are sending to that action { parameter-name: parameter } it looks like in your case your sending no data because your only polling for a refresh, so you don't need to include this

dataType is the data you are expecting to be returned, it could be 'html' or 'json' , in your case your returning nothing because your using the 'ViewBag'

success will be called if the ajax call was successfully called with no errors with the result of that in 'result', in your case you have no result, because your using the view bag.

Now I can't promise this will work because I have never tried it:

function(result)
{
var updated_values = @Html.Raw(Json.Encode(ViewBag.AlertStatus)) 
}

Inside your success function on the ajax try this, this may or may not work. I honestly don't know if the ViewBag will have the updated values or not. At this point you only need to replace the table values with your new ones! you'll have to do all that in javascript, and I suggest looking at functions like 'appendTo', 'replaceWith', 'html' ect that are in Jquery to figure out how to do that.

$.ajax({
    url: 'controller/action', 
    success: function (result) {
        var alert_info = @Html.Raw(Json.Encode(ViewBag.AlertStatus)) 
    },
    error: function (xhr, ajaxOptions, thrownError) {
        //error handling
    }
});
AwDogsGo2Heaven
  • 952
  • 11
  • 26
  • Thanks so much for your response, I had raised the issue of the ViewBag to the team, so I will look to implement a model in its place. I had also suggested a Partial View, but was somewhat turned down. However, I do believe you are right in suggesting this as the most appropriate way. Thanks a lot for your help, it is greatly appreciated. – Nick Chambers Aug 31 '12 at 15:27
  • no problem, if you have more questions feel free to ask. don't forget to mark an answer though. – AwDogsGo2Heaven Aug 31 '12 at 15:37
  • I have spoken to my team, and it appears that they want to persist with the ViewBag and the current structure for the purpose of an upcoming demo, after which we may reconsider the design. To this end, could you possibly assist me with the Ajax call? I have never worked with it before so I'm not entirely sure what I am doing. The example that I was given was for the functionality of removing 'owners' from the system. I am unsure of what to provide for the url, data and success fields. I'm sorry if this seems simple, but like I said, I'm a novice! Thanks again so much for your ongoing help. – Nick Chambers Aug 31 '12 at 16:05
0
(function checkStatus() {
  $.ajax({
    url: '@Url.Action("ViewAlerts", "Alerts")', 
    data: { ownerIds: ownerIds },     
    traditional: true,     
    dataType: 'json',
    success: function (result) 
    {         
      for (i = 0; i < ownerIds.length; i++) 
       {             
         $(".ownersTable tr[id='tablerow+" + ownerIds[i] + "']").remove();
       }     
    },
    complete: function() {
      // Schedule the next request when the current one's complete
      setTimeout(checkStatus, 5000);
    }
  });
})();

How to fire AJAX request Periodically?

Community
  • 1
  • 1
VJAI
  • 32,167
  • 23
  • 102
  • 164
  • Thanks a lot for the quick response! Superb, that is a much better way for the request to be rescheduled. The rest of the Ajax function, however, is geared towards different functionality and was only handed to me as an example. As such, the url, data and success fields are not accurately reflecting this functionality. Would you be kind enough to give me some further assistance on the matter? Thanks so much in advance! – Nick Chambers Aug 31 '12 at 15:25
0

In order to get data from an action to JavaScript, your action must return data. Right now your "action" is not really an action, as it returns void and sets ViewBag properties, which are not accessible from JavaScript. So your action needs to be something more like:

[HttpGet]
public ActionResult Alerts()
{
    IEnumerable alerts;
    // ...code which gets data into alerts
    return JsonResult(alerts);
}

Obviously I don't know your domain, so I don't know how you would structure the data coming back, but the basics are there. The $.ajax call would point to the action route (in this case, probably '/Alert/Alerts'). The success function would have the data argument with the appropriate array of objects. From there you'd update your DOM with the data.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122