0

[Sorry for long question but it is necessary to explain the problem]

I am working on a learning website and it is supposed to show a list of messages to user if there are any. Something like this:

alt text

When user presses close button, that message must be marked "read" and should not be shown next time. Following code is used to generate those messages:

<% foreach (var m in Model.UserMessages) { %>
    <div class="um" id="m<%=Html.AttributeEncode(m.ID) %>">
        <p class="mh"><%= Html.Encode (String.Format ("From {0} ({1})", m.From, m.Sent)) %></p>
        <p><%= Html.Encode (m.Text) %></p>
        <% using (Html.BeginForm ("CloseMessage", "Home", new { id = m.ID })) { %>
            <input type="submit" value="Close<%= Html.AttributeEncode (m.ID) %>" id="c<%= Html.AttributeEncode (m.ID) %>"/>
        <% } %>
    </div>
<% } %>

After that, following the guidelines, I added the support of http post method in controller which marks the message as read and then refreshes the view (to handle disabled JavaScript):

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CloseMessage (int id) {
    using (var dl = new DL ()) {
        dl.MarkAsRead (id);
    }

    if (Request.IsAjaxRequest ()) {
        return new EmptyResult ();
    }
    else {
        return RedirectToAction ("Index");
    }
}

Then I wanted to add JavaScript support such that only the message goes away (using jQuery). But the problem is that I am generating the buttons and messages programmatically.

So ended up with a weird looking javascript code in viewpage:

<script type="text/javascript">
    $().ready(function() {
        <% foreach (var m in Model.UserMessages) { %>
        $("#c<%=Html.AttributeEncode (m.ID) %>").click(function(event) {
            $.post("Home/CloseMessage/<%=Html.AttributeEncode (m.ID) %>");
            $("#m<%=Html.AttributeEncode (m.ID) %>").slideUp("slow");
            event.preventDefault();
        });
        <% } %>
    });
</script>

This is basically creating the javascript code in a C# loop which actually works but too much for me to digest. Is there any better way of doing this?

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Hemant
  • 19,486
  • 24
  • 91
  • 127
  • An alternative to letting C# write out html and scripting directly like this, is to bind your UserMessage list to a Repeater and then output whatever html you want from that. Any dynamic content, such as your ID, can be set on the ItemDataBound event on the Repeater. – Niels Brinch Jan 31 '12 at 12:57
  • Never mind, I didn't notice that this was regarding MVC. You can read my comment as critique of MVC in that case :) – Niels Brinch Jan 31 '12 at 13:26

3 Answers3

3

You could just create one javascript function that takes the element IDs as parameters :

function myFunction(ID) {
    $.post("Home/CloseMessage/" + ID);
    $("#m" + ID).slideUp("slow");
}

And :

<% foreach (var m in Model.UserMessages) { %>
<div class="um" id="m<%=Html.AttributeEncode(m.ID) %>">
    <p class="mh"><%= Html.Encode (String.Format ("From {0} ({1})", m.From, m.Sent)) %></p>
    <p><%= Html.Encode (m.Text) %></p>
    <% using (Html.BeginForm ("CloseMessage", "Home", new { id = m.ID })) { %>
        <input type="submit" value="Close<%= Html.AttributeEncode (m.ID) %>" 
        id="c<%= Html.AttributeEncode (m.ID) %>" 
        onclick="myFunction('<%=Html.AttributeEncode(m.ID)%>')"/>
    <% } %>
</div>
<% } %>
Çağdaş Tekin
  • 16,592
  • 4
  • 49
  • 58
  • It is indeed a nice and elegant solution. One problem though, it still posts to the server and doesnt prevent a screen refresh. Is there any way to prevent the form post? – Hemant Jul 29 '09 at 13:24
  • Oh, right. I forgot it was a submit button. I usually make the input element's type "button" instead of "submit". But you could also try returning false from your function, that too should give you the behaviour you want. – Çağdaş Tekin Jul 29 '09 at 13:27
2

YOu could uses a CSS selector in your JS to get all buttons with a particular class assigned to them. Then you can wire up you click event to them. So your HTML would be something like this:

<% foreach (var m in Model.UserMessages) { %>
    <div class="um" id="m<%=Html.AttributeEncode(m.ID) %>">
        <p class="mh"><%= Html.Encode (String.Format ("From {0} ({1})", m.From, m.Sent)) %></p>
        <p><%= Html.Encode (m.Text) %></p>
        <% using (Html.BeginForm ("CloseMessage", "Home", new { id = m.ID })) { %>
            <input type="submit" class="MyCloseButton" value="Close<%= Html.AttributeEncode (m.ID) %>" id="c<%= Html.AttributeEncode (m.ID) %>"/>
        <% } %>
    </div>
<% } %>

I've only added the class="MyCloseButton" to your input

Then in your JS (I use mootools but JQuery has CSS selectors too but you will need to port it sorry) you can do something like this:

window.addEvent( "domready", function() {
  $$("input.MyCloseButton").each( function( button ) {
    // Add your code here, you can reference button.id etc for each of your buttons
  }
});

This way you only have to write out one little function in plan JS and not worry about doing it server-side, all you need to do it decorate your buttons with a css class so the JS can find them.

Pete Duncanson
  • 3,208
  • 2
  • 25
  • 35
1

I understand its not pretty, but its not that bad either. I think there are easier ways to do this, however.

You can use jquery's positional selectors from the button's click event handler. Then just assign the same handler to every button.

<script type="text/javascript">
    $(document).ready(function() {        
        $("input[type=submit]").click(function(){
            $(this).parent().slideup("slow");
            $.post("Home/CloseMessage/" + $(this).parent().attr("id"));
            event.preventDefault();
          });
        });
    });
</script>