Decided to make proper changes when adding tags so I can actually test some things. I pulled out the DB and entity framework stuff into a proper repository with interface. Not sure what all there is to say about it but I'm happy enough that I have proper tags. A splash of .js to just to do some crud stuff for adding tags to a post and to make the drawer properly work and filter based on tags.
So it came together like this with a controller:
public async Task<IActionResult> GetBlogPostsByTags([FromBody] List<string> tags)
{
List<BlogPost> posts = await _context.GetBlogPostsByTagsAsync(tags);
MarkdownPipeline pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
posts.ForEach(post => { post.Body = Markdown.ToHtml(post.Body, pipeline); });
return PartialView("_PostsPartial", posts);
}
Repository now properly separated:
public async Task<List<BlogPost>> GetBlogPostsByTagsAsync(List<string> tags)
{
var posts = await _context.Posts
.Include(x => x.Tags)
.Where(x => x.Tags.Any(tag => tags.Contains(tag.DisplayName)))
.OrderByDescending(x => x.PostTime)
.ToListAsync();
return posts;
}
And the splash of frontend. I think I like how I have the close button, but I haven't seen many done this way before. Fun to learn about stopPropagation because the click event was bubbling up to the tag from the close/remove "x".
document.querySelectorAll('.remove-tag').forEach(tag => {
tag.addEventListener('click', async function(e) {
e.stopPropagation();
tag.parentElement.classList.remove('dark:bg-blue-800');
tag.parentElement.classList.add('dark:bg-blue-950');
await refreshPosts();
});
});
async function refreshPosts(){
const selectedTags = [];
document.querySelectorAll('.category-tag').forEach(tag => {
if (tag.classList.contains('dark:bg-blue-800')) {
selectedTags.push(tag.getAttribute('name'));
}
});
const response = await fetch('/Blog/GetBlogPostsByTags', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]')?.value
},
body: JSON.stringify(selectedTags)
});
if (response.ok) {
const postsHtml = await response.text();
const mainContent = document.querySelector('main[role="main"]');
if (mainContent) {
mainContent.innerHTML = postsHtml;
}
} else {
console.error('Failed to fetch blog posts:', response.statusText);
}
}