2.9 Section Component: Editing Sections
Now that we can display the section, let's look at allowing users to edit the content of each section.
1.Getting Started6 lessons, 27:04
2.Building the Application13 lessons, 1:29:43
3.Conclusion1 lesson, 00:44
2.9 Section Component: Editing Sections
In the previous lesson, we created a section component, so that we could display the individual sections of a page to viewers. Now we want to create the ability for logged in users to edit these individual sections. So in this lesson we'll create the editing capabilities for this section component. We actually start this in the page component. If our section component is to allow a logged in user to edit it, it's going to need to know whether a user is logged in or not. So you probably know exactly what's coming here. We need to give our section a user property. So we'll set the user to be this.props.user. That way our section will know whether or not the user is logged in. Now back in the section.js component, the first thing we want to do is add a property to the object that gets returned from the get state function. And this is going to be the editing property. We need to know whether or not the user is currently editing a given section. So this will be a boolean expression. We can say props.user to see if a user is currently logged in and props.user.username is equal to props.section.editor. So, as we saw in our page.js file, when we create a new section, that section gets an editor, the editor being the current user who created it. Also, when a user clicks on a section, they will become the editor for that section so that they can edit it. So that means that we will have a state property here that will tell us whether or not the current user is editing the current section. So now in our render function we can use this to decide whether we need to render this as HTML or render it as marked down inside of a text box. So underneath our content variable, let's say if this talk state DOT editing. And let me copy the content that we created above our span with the HTML set inside of it and put that in the else block. Because we only want to render that if the user is not editing the page. If they are editing the page we'll set the content to be something else. We'll set it to a text area element. Let's give it a class name of 12 columns so that it takes up the full width of the right hand side of our page. And then we have to give it a default value. That default value will be this.state.content. So this means that when that text area is rendered, the markdown content that is stored in the database will be rendered inside of this text area. So now our render method will render the content as editable markdown or as you will HTML. Depending on whether or not the user is currently editing this section. So how do we flip that bit? How do we set it so that the user can go from viewing to editing? Well let's add an On Click event handler to a root element here our section. When this is clicked, we will call this .startEditing. So let's go ahead and write that function now. Inside start editing, all we have to do is save this.setState and we'll set editing equal to true. However it's not quite that simple. There are a few cases where we don't want to set that state. So we can say if there is no props.user aalue or state.editing is already true, just return. So we're not going to run this function if we don't have a login user, and that makes sense, of course. We don't want to allow them to edit if they're not logged in. Or if they already are editing it, we're not going to reset that state because editing is already equal to true. Now, this is not all that we need to do in this editing function. We need to store in the database the fact that this user is now the editor of this section. Right? Because if any other user anywhere else is viewing this section, they need to know that someone else is editing it, and they are not allowed. So we'll say API., and this reminds me, we don't currently have the API in this file, so let's go ahead an import API as we've done before. And then we can do API.pages.child. And in this case, we need to get a child path that is directly for this section, not just the page does section is in, but for this section content itself. Now currently, our sections have no knowledge of what page they're in. So we don't have the information we need within our section.js file here, to choose the right child reference in the Firebase database. So we're gonna have to add this as a property on our sections element. So for now, we'll just say this.props.path. And we'll go set that in just a second. However what do we want to do when we get our reference? Well, we want to go ahead and update this value in the database. We're going to update this sections editor, property. And we're going to set it to this.props.user.username. So we'll make the current user the current editor of this section. Now before we forget, let's go over to our page.js file. And where we are instantiating our sections, let's add one or more property here, and this is going to be the path. And we'll set path to this.props.params.id. Remember, that is the id of the individual page we are on. And then we'll add '/sections/', and then we'll add the individual sections' id. So if you think about the way we've been using Firebase child references throughout these lessons, you should see that what this gives us, is the root URL to our Firebase data store. /pages, which we get from our API file here on the client, slash everything we've just added here, which is the specific page within the pages. Then the sections property of that specific page, and then the specific section within the sections object. So what we've basically done here is created a reference that starts at our entire database, and we can narrow it right down to the specific key in the database that we want to update. And this is one of the really neat things I think about the way Firebase works. It makes it really easy for you to reach inside your database and update only a single key. If I come back and refresh the browser, I can go ahead and log in and then I can click on one of our sections. And notice what happens. The content converts from HTML to a text box with mark down inside of it. And I can go ahead and make changes to that content. Now we haven't yet gotten to updating that content in the Firebase database. However if we flip back to the Firebase dashboard, you can see that this section in the database has the andrew user as its editor. So this is coming along pretty well. Unfortunately we're not quite done here. As you can see if I click one of the other sections, the first section is still editable. If I refresh the page, my changes are not saved but I'm still editing both those sections. So let me come back to fire base here. And I'll delete the editor keys on both of those sections. So the next step is actually saving those edits that we make. And this happens around our text area element. First, whenever we make a change to that text area, we want to save that back into the content state property of this section. So let's add an onChange handler here. And when that happens we'll call this.update content. Now let's create this update content function. Inside it's very simple, we'll just do this.setState and will set the content to be the value of the target Which is of course, the text area. The next thing we want to do is actually save those changes back to the database. So the way I've chosen to do this is we'll save the markdown back to the database, and will update the HTML here on the page whenever that text area loses focus. So we can add in onBlur handler here. And when that happens, we'll call this .save. So let's write that this .save function. We'll start by setting some state. We'll set editing back to false. And then we have to update this section in the database one more time. So we'll say API.pages.child, and we'll pass in that path again, and then we'll update it one more time. We'll set the editor equal to null, and this will essentially remove that editor field from the Firebase data store. Because whenever a value is set to null, Firebase will remove that key. But then will set the content to be whatever the currently saved content is. However if that is empty will set that's knowledge well. So this basically means that if the user removes all the content of the section. We just delete that section. Now if we come back to the browser and refresh our page. And I'll go ahead and make some changes to our text area there. Then when I click outside of that area, notice that the text area loses focus, and so of course, it saves those changes. However, our HTML is still displaying exactly as it did before. Why aren't we seeing the HTML update? Well, first, let's look here at the Firebase dashboard. It may not look like that data has changed. But if you are paying attention to the new lines before and after, it actually has changed. So let's come back to our page here. And if I refresh it, you can see that the html does display correctly. So the changes were saved, however, the html wasn't really rendered. If you think about the way that we were rendering that HTML, what's happening here should make sense. Remember when we render the HTML. Right now, we render it in the getState function here at the top of our class, right? And the only time we call that getState function is in the constructor. So even though we update the content property, we never actually recall getState, so the HTML state property is never updated. So you might say well why don't we just recall getState at the end of our save function? Well, because it's not quite that simple. Remember, we need to be able to rerender HTML not only after we make changes to that HTML, but also when anyone else makes changes to that HTML. We want to rerender or those changes as soon as possible after they are saved to the database. So what we need to do is listen to the database for changes to the value of that section right? Well that's actually what we're already doing. Remember that in our page.JS file, we have it listening for changes to the pages content. And if the content of any one of those sections changes, as we're doing here when we save it back to the database, then that section property, props.section, will be reassigned to this section component. So the way we can do this so that we will render the HTML both for changes that we save, but also for changes that anyone else saves, is by listening for those new properties, coming into the section. So I'm going to add a componentWillReceiveProps method to our class here. Those next props come in as a parameter. And so I can call this this.get state. Passing at the next props. And I get a new state object back. And then I can set that state with this.set state. With this in place we should be able to refresh the page and see those changes as soon as we make them. I'll refresh the page. I'll come to our list item here. And I'll add a fourth item to the list. And now. When I save those changes. I can see them right away. If I want to go ahead and add a heading to this section, then you can see that I see those changes right away. So this means we can successfully edit pages. In fact, our add section button will work right now. We've slowly been building up this add section functionality over a couple of lessons now. But now if I click add section, I get an empty text box. And I can click in there and add some text to that new section and it renders HTML. And if we look back at the firebase database, you can see that of course our new section has been properly saved. The final step here will be to add a bit of CSS. Let's open our styles on CSS file, and at the bottom here I'm going to say if something has the editable class. But it does not have the editing class, then when we were over that element let's set the border color equal to a light shade of gray. So I've just kind of pulled these two new class names out of mid-air. We're going to use editing on this section, if the user is currently editing it. And we'll use editable if this section is editable by the currently logged in user. base. Basically, if they're logged in, and no one else is editing it, then it is editable. So in the last lesson, we created our classes array here with row and section. Let me go ahead and add some if statements after this. We'll say if (this.state.editing). Well, then we'll just push in the class 'editing'. And then we can say if (this.props.user) meaning the user is logged in, then we will set the class editable. And, of course, this is not strictly true, if the user is logged in but someone else is editing a section, then it should not be editable. But we'll worry about some of that logic in the next lesson. However if I refresh our page one more time, you can see that, as I hover over different sections, I can see that they're editable by the border that appears. And I can click on them and edit them, and the changes are made. Excellent, so now we have Wiki pages in which the individual sections are editable. In the next lesson, we'll look at locking the sections so that if one person is editing them, nobody else can.