First of all, welcome to the finale of this tutorial series!
Last session, I showed you how to add a new function to the blog: adding comments. It’s pretty much a stand-alone feature. This session, I will show you how to add categories to your blog system. It’s a more difficult task, as its code will be more closely intertwined with the existing code.
Category Model
Step 1: Category Class
You know the drill: the model always comes first. One more new class on Parse.com: the Category
class. It should be very simple. We only need one custom field:
- String
name
Let’s just create two dummy categories to make it easier for testing:
Step 2: Category Field in the Blog Class Table
Now, add a new column in the Blog class table on Parse.com and make it a pointer to Category
.
Let’s also link them to several blog posts.
Step 3: Comment Object
Next, the Comment
object in blog.js
. Again, it can be very simple.
Category = Parse.Object.extend('Category', {});
Step 4: Comments Collection
And the collection:
Categories = Parse.Collection.extend({ model: Category })
Add a Category Dropdown in the Write a Blog Form
Step 1: Fetch Categories
Like in Comments, let’s fetch comments when we render WriteBlogView
.
render: function(){ ... var self = this, categories = new Categories(); categories.fetch().then(function(categories){ attributes.categories = categories.toJSON(); self.$el.html(this.template(attributes)).find('.write-content').wysihtml5(); }); }
Step 2: Find the Current Category
Next, we need to find the current category when there’s an existing category for the blog post you are editing. We can just loop through the attribute.categories
array and set selected
to TRUE
for the correct category.
categories.fetch().then(function(categories){ attributes.categories = categories.toJSON(); // Get current selected category if (attributes.category) { attributes.categories.forEach(function(category, i){ if (category == attributes.category) { attributes.categories[i].selected = true; } }); } console.log(attributes); self.$el.html(self.template(attributes)).find('.write-content').wysihtml5(); });
Step 3: Render Categories in a Dropdown Menu
And in HTML, render categories in a <select>
menu:
<div class="form-group"> <label for="category">Category</label> <select> {{#each categories}} <option value="{{objectId}}" {{#if selected}}selected{{/if}}>{{name}}</option> {{/each}} </select> </div>
Step 4: Record the Selected Category
Now let’s record it in the submit()
function in WriteBlogView
:
submit: function(e) { ... this.model.update({ title: data[0].value, category: data[1].value, summary: data[2].value, content: data[3].value }); }
And then in the Blog.update()
function:
Blog = Parse.Object.extend('Blog', { update: function(data) { ... var category = new Category(); category.id = data.category; this.set({ 'title': data.title, 'category': category, ... }).save(null, { ... }); } })
Notice that you can’t just put down the category id, but you need to create a new category
instance, and set its id to the category id you get from the form.
Give it a test, and hopefully you can see it correctly recorded in the database:
Category Pages
Now let’s move on to create a sidebar that links to different categories.
Step 1: Clean Up and Make a Template
First, take out all the static HTML code for the sidebar:
<div class="col-sm-3 col-sm-offset-1 blog-sidebar"></div>
And then make a new template div for the template of category menu:
<script id="categories-tpl" type="text/x-handlebars-template"> <div class="sidebar-module"> <h4>Categories</h4> <ol class="list-unstyled"> {{#each category}} <li><a href="#/category/{{objectId}}">{{name}}</a></li> {{/each}} </ol> </div> </script>
Step 2: CategoriesView
Then, make a View for this list of categories:
CategoriesView = Parse.View.extend({ template: Handlebars.compile($('#categories-tpl').html()), render: function() { var collection = { category: this.collection.toJSON() }; this.$el.html(this.template(collection)); } })
Step 3: Render CategoriesView
Because the list of categories is on every page, we can create a shared variable in BlogRouter.initialize()
:
initialize: function(options){ this.blogs = new Blogs(); this.categories = new Categories(); }
And then render it in the .start()
function:
start: function(){ ... this.categories.fetch().then(function(categories){ var categoriesView = new CategoriesView({ collection: categories }); categoriesView.render(); $('.blog-sidebar').html(categoriesView.el); }); }
Give it a try, and now it renders:
Because we now have a shared variable, we can also use it in WriteBlogView.render()
:
Step 4: Add Router
Now we just need to make /category/{{objectId}}
work.
Let’s add this router pattern first:
routes: { ... 'category/:id': 'category' }
Then, write the category
function. It’s fairly straightforward, just a combination of the things you’ve learnt so far:
category: function(id) { // Get the current category object var query = new Parse.Query(Category); query.get(id, { success: function(category) { // Query to get the blogs under that category var blogQuery = new Parse.Query(Blog).equalTo("category", category).descending('createdAt'); collection = blogQuery.collection(); // Fetch blogs collection.fetch().then(function(blogs){ // Render blogs var blogsView = new BlogsView({ collection: blogs }); blogsView.render(); $container.html(blogsView.el); }); }, error: function(category, error) { console.log(error); } }); }
Now everything’s working.
Conclusion
Wow, I can’t believe it took me this long to finish the whole series. In the past ten sessions, you’ve learnt a lot about Parse.js and web development in general. Together we’ve built a blog system with full functions: add, edit, delete, login, comments, and in this session, categories.
I hope you find this series helpful. Please share with me your feedback, suggestions, and content you want me to cover in the future. And if you made any web projects using techniques from this tutorial, I would love to see them, too.
Comments