In the last session, you refactored your entire blog system. Now that everything’s cleaned up, you are ready to speed up for some new adventures. In this session, we are going to do more around the router, and add three more functions to our blog system: delete, logout, and single blog view.
1. Delete
In part 6, we covered the edit function. Chances are, you would also want to delete one of your blog posts. There are two places you can put this function: add it in the BlogsAdminView
, or send it to a URL and deal with it in the Router
.
I will show you the Router way. It’s more commonly used, and it makes the code better structured.
Step 1: Add URL Pattern
As usual, let’s add a URL pattern first:
routes: { '': 'index', 'admin': 'admin', 'login': 'login', 'add': 'add', 'edit/:id': 'edit', 'del/:id': 'del' }
Step 2: Link to Delete
Then, update the link in the admin page:
<a class="app-link" href="#/del/{{objectId}}">Delete</a>
Step 3: del Function
Now, let’s add a new del
function to the Router to handle it. It’s pretty straightforward: find the blog post using the id
we passed in from the URL, and destroy it.
Try to challenge yourself and write it without reading my code. You should be getting a really good grasp of Parse.js at this point.
del: function(id) { var query = new Parse.Query(Blog); query.get(id).then(function(blog){ blog.destroy().then(function(blog){ alert('Deleted!'); }) }); }
Notice that you can use the .then()
function here instead of passing an object as we did previously:
query.get(id, { success: function(blog) { ... }, error: function(blog, error) { ... } });
It’s an easy way to add callback functions in Parse.js, making your code cleaner and more readable. Go to Parse.com to see full documentation on promises.
Let’s give it a test run, and double-check the database to see if it’s working.
Congrats, it’s working!
Step 4: Redirect Back to the Admin Page
If you pay attention to the URL, you will see that after you click out of the alert box, the URL is still /del/
and the post you just deleted is still there. We want to send users back to the admin page after deletion, and the page should refresh and reflect the change they just made.
You can achieve all of that by doing a redirect:
del: function(id) { var self = this, query = new Parse.Query(Blog); query.get(id).then(function(blog){ blog.destroy().then(function(blog){ self.navigate('admin', { trigger: true }); }) }); }
Notice that because this time you call navigate
from within the router, you can just store the router as self
, and call self.navigate()
.
Step 5: Check Login
Finally, we need to make sure you are the only one who can delete your blog posts. Let’s check login for the function. This should be the same as the edit
function.
del: function(id) { if (!Parse.User.current()) { this.navigate('#/login', { trigger: true }); } else { ... } }
2. Logout
Like delete, logout can also be handled by the router. And it also starts with adding the URL pattern:
routes: { ... 'logout': 'logout' },
The logout function itself is really easy in Parse.js. Just call Parse.User.logOut()
and then redirect to the /login
page:
logout: function () { Parse.User.logOut(); this.navigate('#/login', { trigger: true }); }
And finally, let’s add a button to #admin-tpl
:
<a href="#/logout" class="add-blog btn btn-lg">Logout</a>
As you can see, styling is really not the focus of this tutorial. Feel free to fix the padding and style it however you’d like.
3. Single Blog View
Now let’s move on to make some new functionalities.
Up to this point, we are showing the whole blog article on the home page. While some people do prefer this style, most blogging systems support the idea of having a snippet summary upfront, and if the visitors click into the articles, they then can see the content on a separate page, with possibly some commenting area around it.
I will walk you through creating this detailed single blog view in this session, and we will be focusing on building commenting in the next one.
Step 1: Add a Summary Column
First, let’s add a column for the summary in the Blog table:
Step 2: Include Summary in WriteBlogView
Now, let’s add it to the Blog.update()
function. You can change the function to take a data object which contains the title, summary, and content, to avoid memorizing the order of the variables.
update: function(data) { // Only set ACL if the blog doesn't have it ... this.set({ 'title': data.title, 'summary': data.summary, 'content': data.content, ... }).save(null, { ... }); }
Add one more <textarea>
for summary in #write-tpl
:
// Put this form-group in between the form-group for title and content <div class="form-group"> <label for="summary">Summary</label> <textarea name="summary" class="form-control" rows="3">{{summary}}</textarea> </div>
And change the WriteBlogView.submit()
function accordingly:
submit: function(e) { ... this.model.update({ title: data[0].value, summary: data[1].value, content: data[2].value }); }
Now, since we’ve added a new variable in the template, we need to give it a default empty value in the WriteBlogView.render()
function:
render: function(){ ... if (this.model) { ... } else { attributes = { form_title: 'Add a Blog', title: '', summary: '', content: '' } } ... }
And if you are using the wysihtml5 plugin for the content, you will notice previously we are targeting all the <textarea>
elements:
this.$el.html(this.template(attributes)).find('textarea').wysihtml5();
Let’s give the content textarea a class and target only that with the wysihtml5 plugin.
In #write-tpl
:
<textarea name="content" class="form-control write-content" rows="20">{{{content}}}</textarea>
In the WriteBlogView.render()
function:
this.$el.html(this.template(attributes)).find('.write-content').wysihtml5();
Now it’s working!
Step 3: Display Summary on the Home Page
Play with the new write blog page and add some blog posts with summary, and pull the summary instead of the content in #blogs-tpl
:
{{#each blog}} <div class="blog-post"> <h2 class="blog-post-title"><a href="#">{{title}}</a></h2> <p class="blog-post-meta">At {{time}} by {{authorName}}</p> <div>{{summary}}</div> </div> {{/each}}
Step 4: Add the SingleBlogView Page
Take a minute and think about how would you add a /blog/:id
page to show the content for each blog post, and try to do it yourself. You should be able to do it all by yourself now!
But for the purpose of this tutorial, let me give you a quick walkthrough:
Add a new HTML template for this page:
<script id="blog-tpl" type="text/x-handlebars-template"> <div class="blog-post"> <h2 class="blog-post-title">{{title}}</h2> <p class="blog-post-meta">At {{time}} by {{authorName}}</p> <div>{{{content}}}</div> </div> </script>
Add a new BlogView
class that takes in a blog
object, and render it in #blog-tpl
:
BlogView = Parse.View.extend({ template: Handlebars.compile($('#blog-tpl').html()), render: function() { var attributes = this.model.toJSON(); this.$el.html(this.template(attributes)); } }),
Add a new URL pattern in BlogRouter
:
routes: { ... 'blog/:id': 'blog', ... }
And in the BlogRouter.blog()
function, get a blog by its id, render a blogView
, and put it in $container
:
blog: function(id) { var query = new Parse.Query(Blog); query.get(id, { success: function(blog) { console.log(blog); var blogView = new BlogView({ model: blog }); blogView.render(); $container.html(blogView.el); }, error: function(blog, error) { console.log(error); } }); }
Finally, update the link in #blogs-tpl
to link to this page:
{{#each blog}} <div class="blog-post"> <h2 class="blog-post-title"><a href="#/blog/{{objectId}}">{{title}}</a></h2> <p class="blog-post-meta">At {{time}} by {{authorName}}</p> <div>{{summary}}</div> </div> {{/each}}
Give it a try:
Extra points if you did this by yourself.
Conclusion
In this session, you built a lot: a delete function, a logout function, and another new page type. If you’ve been following this tutorial series until now, I think you have a solid understanding of how database, model, view, template, and router work together. I hope you’re also starting to enjoy building a Parse.js project at this point. Leave me your feedback and let me know if it’s helpful.
With this single blog post page we built this time, we are going to add a comment section next time. Should be a fun one. Stay tuned!
Comments