8

I have a notification system that works. Right now, when there's no notification or not, the notification appears in a <span class='glyphicon glyphicon-inbox' aria-hidden="true"></span> just like stackoverflow. but my desire is when there's a notification, I want the box to be turned into a

<span class="badge badge-notify" style="red">notification count</span> 

again just like a stack overflow.

So I tried removing a particular class that changes the form of the box when if count == 0 and adding the class when count is not zero. I tried setting set interval as well but it just won't work.

Can you please help me?

below is what I have in navbar, I have the notification box and the badge set up.

   <li class="dropdown">
 <a href="#" class="dropdown-toggle notification-toggle" data-toggle="dropdown" role="button" aria-expanded="false" id="button">
<span class='glyphicon glyphicon-inbox' aria-hidden="true"></span>
<span class="caret"></span> 
<span class="badge badge-notify">notification count</span></a>
    <ul class="dropdown-menu" role="menu" id='notification_dropdown'>

              </ul>
            </li>

below is my ajax function to disply notification

 <script>
    $(document).ready(function(){
      $(".notification-toggle").click(function(e){
        e.preventDefault();
        $.ajax({
          type: "POST",
          url: "{% url 'get_notifications_ajax' %}",
          data: {
            csrfmiddlewaretoken: "{{ csrf_token }}",
          },
         success: function(data){
            $("#notification_dropdown").html(' <li role="presentation" class="dropdown-header">alarm</li>');
            var count = data.count
            console.log(count)
            if (count == 0) {
                  $("#notification_dropdown").removeClass('notification');
              var url = '{% url "notifications_all" %}'
              $("#notification_dropdown").append("<li><a href='" + url+ "'>view all</a></li>")
            } else {
                  $("#notification_dropdown").addClass('notification');
              $(data.notifications).each(function(){
                var link = this;
                $("#notification_dropdown").append("<li>" + link + "</li>")
              })
            }
            console.log(data.notifications);
          },
          error: function(rs, e) {
            console.log(rs);
            console.log(e);
          }
        })
      })
    })
    </script>

so what I kinda tried was this,

        <style>
          {% block style %}
    #notification_dropdown{
    }

    #notification_dropdown.notification{
      color: red;
    }


        {% endblock %}
          </style>

and 

    <script>
setTimeout(function(){
   $("#notification_dropdown:visible").addClass('notification');
}, 2000);
    </script>

maybe I set the id wrong...those did nothing unfortunately.

not sure if this is needed, I'll also add function for notification(ajax)

def get_notifications_ajax(request):
    if request.is_ajax() and request.method == "POST":
        notifications = Notification.objects.all_for_user(MyProfile.objects.get(user=request.user)).recent()
        count = notifications.count()
        notes = []
        for note in notifications:
            notes.append(note.get_link.encode('utf-8'))
        data = {
            "notifications": notes,
            "count": count,
        }
        print data
        json_data = json.dumps(data)
        print json_data
        return HttpResponse(json_data, content_type='application/json')
    else:
        raise Http404

So my question)

1) what did I do wrong that the form/color of notification box didn't change when notification is not zero, and how do I achieve what I want which is box to be turned into a

<span class="badge badge-notify" style="red">notification count</span> 

2) I'm able to display count of notification in console with console.log(count), how do I display this count in the navbar?

mike braa
  • 647
  • 1
  • 12
  • 33
  • 1
    You need to use `setInterval()` in the javascript to query the server for notifications every few seconds, otherwise how will your frontend know of any change? – ygesher Apr 17 '16 at 06:56
  • @jegesh I have set it but no differnece – mike braa Apr 19 '16 at 05:22
  • Did I understand correctly that you want to toggle the notification count on `` click? Or do you want it to change even if the user is not doing anything, but there appeared a notification for him? As for now, your code checks whether there is a notification only when the user clicks on the anchor. If you would like continuously to check whether there are notifications for the user, consider using a `setInterval()` that will make an ajax request each X seconds. – iulian Apr 19 '16 at 06:33
  • @iulian I want the notification box to turn red automatically when there's a notification, just like stackoverflow – mike braa Apr 19 '16 at 10:42
  • @mikebraa See my answer, I think I addressed all your issues. The red color not working is just a problem in the HTML. The style attribute needs to be given valid CSS. – ygesher Apr 19 '16 at 10:43

2 Answers2

3

It looks like all the changes you are doing in AJAX success are on #notification_dropdown but in your navbar HTML the <span> elements you want to toggle are never touched. It's the same in your CSS:

    <style>
      {% block style %}
#notification_dropdown{
}

#notification_dropdown.notification{
  color: red;
}


    {% endblock %}
      </style>

The CSS selectors used (#notification_dropdown) do not apply the CSS properties to the <span> elements which matter.

One of the ways to fix this -

Change your navbar HTML to:

 <li class="dropdown">
 <a href="#" class="dropdown-toggle notification-toggle" data-toggle="dropdown" role="button" aria-expanded="false" id="button">
<span id="no_notify" class='glyphicon glyphicon-inbox' aria-hidden="true"></span>
<span class="caret"></span> 
<span id="notify_count" class="badge badge-notify">notification count</span></a>
    <ul class="dropdown-menu" role="menu" id='notification_dropdown'>

              </ul>
            </li>

Change: added id attribute to both no notify and notify count badge span elements.

And

Change your Ajax script to:

 <script>
    $(document).ready(function(){
      $(".notification-toggle").click(function(e){
        e.preventDefault();
        $.ajax({
          type: "POST",
          url: "{% url 'get_notifications_ajax' %}",
          data: {
            csrfmiddlewaretoken: "{{ csrf_token }}",
          },
         success: function(data){
            $("#notification_dropdown").html(' <li role="presentation" class="dropdown-header">alarm</li>');
            var count = data.count
            console.log(count)

            if (count == 0) {
                  $("#notify_count").html(count).hide();
                  $("#no_notify").show();

                  $("#notification_dropdown").removeClass('notification');
              var url = '{% url "notifications_all" %}'
              $("#notification_dropdown").append("<li><a href='" + url+ "'>view all</a></li>")
            } else {
              $("#no_notify").hide();
              $("#notify_count").html(count).show();

                  $("#notification_dropdown").addClass('notification');
              $(data.notifications).each(function(){
                var link = this;
                $("#notification_dropdown").append("<li>" + link + "</li>")
              })
            }
            console.log(data.notifications);
          },
          error: function(rs, e) {
            console.log(rs);
            console.log(e);
          }
        })
      })
    })
    </script>

Change: added $("#no_notify") and $("#notify_count") related lines to show()/hide() those spans based on count

And change your setTimeout to:

 <script>
   setTimeout(function(){
        $(".notification-toggle").click();
   }, 2000);
 </script>

$(".notification-toggle").click(); triggers a click on <a> in navbar, which does the Ajax call. If the count from Ajax response is zero we hide notify_count span and show no_notify else do the reverse.

Triggering a click on the <a> tag seems like a good idea. If in future you want the count update to happen only on user action (a click on anchor tag) and do not want the call to happen periodically all you have to do is get rid of setTimeout logic.

It is a best practice to add an error callback too for your AJAX call, just in case if the POST call to get_notifications_ajax fails.

Community
  • 1
  • 1
LearnerEarner
  • 1,020
  • 6
  • 12
  • thank you very much your solution works perfectly, and I think I understand most of them but I added error tho, error: function(rs, e) { console.log(rs); console.log(e); – mike braa Apr 20 '16 at 02:39
  • I think you are talking about the ways to deal with error situation.It depends on the user experience strategy you want to adopt. You can decide to always indicate failure(may be by overlapping glyphicon-inbox with glyphicon-exclamation-sign)or if you decide to treat the failure as non-critical just leave your current `error` callback as is and hope for next calls to succeed.What if the call never succeeds? Well..it again depends on what you want to do. May be show `!` only if 3 consecutive requests fail. You have bunch of options and you can CHOOSE THE ONE THAT MAKES MOST SENSE TO YOUR USERS – LearnerEarner Apr 20 '16 at 03:55
0

One simple way to accomplish this is to use setInterval() to query the server every few seconds for new notifications. The code would look something like this:

HTML

<li class="dropdown">
 <a href="#" class="dropdown-toggle notification-toggle" data-toggle="dropdown" role="button" aria-expanded="false" id="button">
  <div id='inbox-wrapper'><span class='glyphicon glyphicon-inbox' aria-hidden="true"></span><div>
  <span class="caret"></span> 
  <span class="badge badge-notify">notification count</span></a>
    <ul class="dropdown-menu" role="menu" id='notification_dropdown'></ul>
</li>

JS

<script>
  setInterval(function(){
    $.ajax({
          type: "POST",
          url: "{% url 'get_notifications_ajax' %}",
          data: {
            csrfmiddlewaretoken: "{{ csrf_token }}",
          },
         success: function(data){
            $("#notification_dropdown").html(' <li role="presentation" class="dropdown-header">alarm</li>');
            var count = data.count
            console.log(count)
            if (count == 0) {
                  $("#notification_dropdown").removeClass('notification');
              var url = '{% url "notifications_all" %}'
              $("#notification_dropdown").append("<li><a href='" + url+ "'>view all</a></li>")
              // here we change the inbox appearance
              $("#inbox-wrapper").html("<span class='glyphicon glyphicon-inbox' aria-hidden='true'></span>");
            } else {
                  $("#notification_dropdown").addClass('notification');
              $(data.notifications).each(function(){
                var link = this;
                $("#notification_dropdown").append("<li>" + link + "</li>")
              // change the inbox appearance
              $("#inbox-wrapper").html("<span class='badge badge-notify' style='background-color:red'>" + count + "</span>");
              })
            }
            console.log(data.notifications);
          },
          error: function(rs, e) {
            console.log(rs);
            console.log(e);
          }
        });
  }, 5000);
</script>

I made the following changes to your code:

1) I added a div with an id to wrap the inbox, in order to make HTML swapping simpler (as well I fixed the style attribute for the 'active' inbox span)

2) I move the code from the inbox click event to a setInterval function. That way, you don't have to send the AJAX request when opening the inbox drop-down, because you already have done so, every 5 seconds!

3) I added the notification count to the inbox element

ygesher
  • 1,133
  • 12
  • 26
  • hello as I read your code, I kinda understand it but when I tried basically copied and pasted your code I'm unable to unfold notification box...I saw the console but no error...sorry couldn't be more detailed...maybe I'll post the image what it looks like – mike braa Apr 20 '16 at 02:35
  • I just meant to give you a push in the right direction, obviously you'll have to adapt the HTML and the code to your specific circumstances. – ygesher Apr 20 '16 at 15:33