Как я могу отображать и скрывать кнопки «Нравится/Не нравится» с помощью Javascript в моем веб-приложении?

Я прохожу онлайн-курс CS50W в Гарварде и создаю веб-приложение, похожее на Twitter. Когда пользователь видит сообщение, мне нужно показать пользователю кнопку «Нравится» или «Не нравится» в зависимости от того, понравилось оно пользователю или нет. Также есть счетчик, показывающий, скольким пользователям понравился пост на данный момент. Я могу обновить счетчик, если пользователю понравилась или не понравилась публикация, но у меня проблема с кодом, отображающим или скрывающим кнопки.

Вот мой код:

модели.py

class Post(models.Model):
    """ Model representing a post. """
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True)
    no_of_likes = models.IntegerField(default=0)

    def __str__(self):
        return f"Post {self.id} by {self.user.username} on {self.timestamp}"
    
class Like(models.Model):
    """ Model representing a like. """
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.user} likes {self.post}"

urls.py

path("", views.index, name = "index"),
path("like/<int:post_id>", views.like, name = "like"),
path("unlike/<int:post_id>", views.unlike, name = "unlike"),

view.py

def index(request):
    """ Home page. """
    posts = Post.objects.all().order_by('-timestamp')
    paginator = Paginator(posts, 5)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    likes = Like.objects.all()  
    
    # Make a list of liked posts.
    liked_posts = []
    try:
        for like in likes:
            if like.user.id == request.user.id:
                liked_posts.append(like.post.id)
    except:
        liked_posts = []

    return render(request, "network/index.html", {
        "posts": posts,
        "page_obj": page_obj,
        "likes": likes,
        "liked_posts": liked_posts,             
    })

@login_required
def like(request, post_id):
   
    post = Post.objects.get(pk=post_id)
    user = User.objects.get(pk=request.user.id)
    like = Like.objects.create(user=user, post=post)
    like.save()

    # Update no of likes.
    post.no_of_likes = Like.objects.filter(post=post).count()
    post.save()
    return JsonResponse({"message": "successfully liked", "no_of_likes": post.no_of_likes})

@login_required   
def unlike(request, post_id):
    
    post = Post.objects.get(pk=post_id)
    user = User.objects.get(pk=request.user.id)
    like = Like.objects.filter(user=user, post=post)
    like.delete()

    # Update no of likes.
    post.no_of_likes = Like.objects.filter(post=post).count()
    post.save()
    return JsonResponse({"message": "successfully unliked", "no_of_likes": post.no_of_likes})

index.html с Javascript

{% if post.id not in liked_posts %}
    <button type = "button" class = "btn btn-primary" id = "like{{ post.id }}" onclick = "like('{{ post.id         }}')">Like</button>
{% else %}
    <button type = "button" class = "btn btn-primary" id = "unlike{{ post.id }}"onclick = "unlike('{{ post.id }}')">Unlike</button>
{% endif %}

function like(id) {
        fetch(`/like/${id}`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "X-CSRFToken": "{{ csrf_token }}",
            },
        })
        .then(response => response.json())
        .then(data => {
            document.querySelector(".no-of-likes").innerHTML = data.no_of_likes + " likes";
        })
        .then(() => {
            document.getElementById("unlike" + id).style.display = "block";
            document.getElementById("like" + id).style.display = "none";
        });
    }

function unlike(id) {
        fetch(`/unlike/${id}`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "X-CSRFToken": "{{ csrf_token }}",
            },
        })
        .then(response => response.json())
        .then(data => {
            document.querySelector(".no-of-likes").innerHTML = data.no_of_likes + " likes";
        })
        .then(() => {
            document.getElementById("unlike" + id).style.display = "none";
            document.getElementById("like" + id).style.display = "block";
        });
    }   

Я могу обновить счетчик, не обновляя страницу, но с кнопками дело обстоит иначе. Вот что происходит:

--> Когда я нажимаю кнопку «Нравится», счетчик обновляется. Мне нужно обновить страницу, чтобы изменить кнопку на «Не похоже». Согласно спецификации это должно быть сделано асинхронно (предполагается, с помощью Javascript!), без перезагрузки страницы.

--> То же самое и с кнопкой «Не похоже». Когда я нажимаю на него, счетчик обновляется, но мне приходится перезагрузить страницу, чтобы изменить кнопку на «Нравится».

Я хочу изменить кнопки без перезагрузки страницы. Пробовал размещать блоки кода в разных местах, пробовал использовать условия «если еще», но все равно застрял здесь. Любая помощь приветствуется! Извините за беспорядочный код Javascript, новичок здесь.

Почему бы просто не сделать одну кнопку вместо двух и одну спрятать. Здесь вы создаете только одну кнопку, поскольку шаблон отображается на стороне сервера.

willeM_ Van Onsem 08.05.2024 21:22
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это потому, что вы отображаете только одну кнопку. Добавьте два: один вы скрываете, другой показываете:

<button type = "button" class = "btn btn-primary" id = "like{{ post.id }}" onclick = "like('{{ post.id }}')" style = "display: {% if post.id not in liked_posts %}block{% else %}none{% endif %};">Like</button>
<button type = "button" class = "btn btn-primary" id = "unlike{{ post.id }}" onclick = "unlike('{{ post.id }}')" style = "display: {% if post.id in liked_posts %}block{% else %}none{% endif %};">Unlike</button>

Но я не понимаю, почему вам вообще нужно использовать две кнопки: просто используйте одну кнопку, которую вы используете как для лайка, так и для антилайка в зависимости от контекста.


Примечание. Не храните агрегаты в модели: определяйте агрегаты при необходимости: хранение агрегатов в модели усложняет обновление и синхронизацию данных. Вы можете использовать .annotate(…) [Django-doc] для генерации счетчиков, сумм и т. д. для каждого объекта, когда это необходимо.

Ваш код работает, спасибо. Хотя я пытался изменить код, как вы предложили! Я создал одну кнопку и написал функцию toggleLike(postId) для изменения .innerText на «Нравится» или «Не нравится», используя условия «если еще». Эта часть работает нормально. Но когда я обновляю страницу, на кнопках отображается только «Нравится». Я пытался изменить кнопки с помощью document.addEventListener('DOMContentLoaded', function() {// Logic here}, но безуспешно! Можете ли вы дать мне какие-либо предложения относительно того, как это исправить. Ваше здоровье!

srikk407 09.05.2024 18:59

@ srikk407: можно ли задать новый вопрос с новым состоянием представлений и шаблона?

willeM_ Van Onsem 09.05.2024 19:03

Я не сильно изменил «views.py», только «кнопку HTML и код JavaScript». Вот ссылка на мой новый код: stackoverflow.com/questions/78456711/…

srikk407 09.05.2024 21:52

Другие вопросы по теме