Next lesson playing in 5 seconds

Cancel
  • Overview
  • Transcript

5.2 Create the New Card Modal

We're ready to start creating interfaces for cards! In this lesson, we'll create the CardModal component: it's a presentational component that we'll be able to use for both creating new cards and editing existing cards.

5.2 Create the New Card Modal

In this lesson, we are going to create the modal window that will appear. Whenever we click the new card button, so that we can add a new card to a deck. However, we know that eventually we want a modal for editing cards, right? And this modal is gonna look really similar to our new card modal, so is there some way that we can reuse some code there? Actually, there is, and it comes in the form of a presentational component. So far, all of our presentational components have only had a single use, and you'll have some presentational components that are only there to be wrapped immediately by a single container component in your application. However, you will also often make presentational components that can be reused multiple places in your application. And that's what we're going to be doing in this lesson, we're gonna create a card modal presentational component. And then we're going to use that to create the container component new card modal. And then later on, we'll use that same card modal to create the edit card modal, which is a different container component. So let's go ahead and create a new component file here that's src/components/CardMo Modal.js. Of course we'll begin by importing React as you might expect and let's go ahead and create a CardModal. Now I haven't really mentioned it, but most of the components that we've created so far have been pure components, or functional components. They're just a function that takes some properties and returns some JSX this card modal is gonna have to be a reactor class. So we'll say, react.createClass and this is because we want to do a little bit of manipulation of the dom. Let's go ahead and add our render function here and there are two properties that we're going to need to get from our props. And we'll use destructuring again to do this. We need to get the card property and we also need to get the onDelete property and we get these from this.props. We haven't actually destructured in this way yet. So if you're not sure what we're doing here this is basically the same as saying what card equal this .props.card; and let onDelete = this.props.onDelete. As you can see, we've just written it in much shorter syntax. Okay, so what are we gonna return here? Well, let's return a div with a className of modal); and then of course the top here will have an H1 and the text inside this H1 will be different depending on our modal. And this is where we get this onDelete function. This will be a function that we can use to delete a currently existing card. But that's only gonna be something we need in the EditModal not in the CreateModal. So when this presentational component is used in the new card modal, onDelete is gonna be no. However, if it's the edit modal, we do wanna be able to delete that card and so that will be a function. So we can also use this as a kind of flag to figure out which model we're working with. Inside this h1 if onDelete exists, then we want to have the word new, here. Otherwise, we wanna have the word at it and then, we'll just have the word card outside of that. So this is gonna say new card or edit card and actually I have this wrong. If I delete exist we want to say edit, if it does not exist, we wanna say new now let's have a label here and this is going to be for the card front. Then underneath this, let's create a text area. Let's give it a reference called front and let's give it a default value and the default value is going to be card.front. Now we have this card property here that's gonna be passed into this modal and this is going to be the actual card that we're either creating. If the we're creating it will be an empty object and of course if we already have the card it's going to have properties. So the default value for this text area is going to be card front. Now I'm just going to copy this and paste it again, and we'll change the label to card back, and the text area card back, and then card.front will change to card.back. All right, so now, underneath this let's put paragraph, and this will before our Save button. First, we'll have a button here and we'll say on click and we'll have to write a function called this dot on save we'll get there in a second. And we'll say, save card or close the button. Underneath that, we actually want to have a link, so I'm gonna have to go ahead and import link. From react-router and then let's go ahead and add a link property here. We'll give this link a className of button. So it looks like one of our buttons and this link is going to go to /deck/ and then we'll interpolate card .deckID. So this will just be the cancel button, this is just gonna close the modal. And finally, if this card is being edited we want to show an onDelete button, so once again, do an onDelete ternary here. If onDelete exists then let's create another button here, the onClick for this will be onDelete. So we'll call this function and let's give this a class name of delete just so we can position it over to the right, we'll give it the text delete card and we will close off the button. Otherwise, if we don't have undelete will just return null and null means that a JSX will actually render nothing. So that's our main render function, we have a few other things to add though. At the top here, let's add a function for component did update and if this is the case. We'll do react, DOMfind DOMNode and will pass at this dot ref stuff front and we'll call focus. So this way, whenever we click the new card button will be Immediately focused in the text area for the front of the card. Of course we don't have react on here yet, so let's go ahead and import ReactDom from react-dom, okay. So now we need to create the this.onSave function that we're using, so down here, let's go ahead and say, onSave. We'll need to do the same thing that we do up here with ReactDOM.findDOMNode, so let me just copy that line and paste it. And actually not quite the same thing, we just need to get the Node. So let me go ahead and duplicate this, so we'll get the front of the card and we'll get the back of the card and let's say this dot props dot on save. So we're going to expect this card model to receive a property called on save and what we're going to pass back is the new card to save. So we can create this new card by saying object on a sign and we'll start with an empty object and then, we'll extend it with this .props.card. So whatever the original card was and then we'll replace the front property with front.value and then we'll replace the back property with backed up value excellent. And so this is what happens when they click on Save and notice that we don't need to worry about how we're saving this. Basically, we just return the new card object to this on say function and our container component that wraps card model will decide whether that means saving a new one or updating an old card. Okay, so after this we actually want to navigate back to the deck view, so that means we have to have some way to navigate between roads. So this is actually something else we can get from reactor over here. So we'll import link and we'll also import browser history and so now down here in the on Save after we call the stop Propst on Save. We can say browser history dot push And let's push in /deck/this.props.card.deckId and once again, we're using ES6 string templates. We're almost done here, there's actually something else we've got wrong. Here inside our delete button, when this is clicked we called onDelete and actually, we want to call this .onDelete because we would need to write our own onDelete function. So we'll say, onDelete here and what we'll do is we'll call this.props.onDelete. So we're still displaying this button based on whether or not we have that onDelete function, but we're not calling that delete function raw, if you will. Instead, we're wrapping it with our own call. So this.props.onDelete, and we pass it this, .props.cards.id. So we pass at the id for the card and then lets copy our browser history line here and paste that down below. So we'll use that again and this way whether we save or delete, wither way we navigate back to the deck view. All right, and so now at the very bottom let's export default CardModal and there we go. Now this is been a pretty big presentational component but remember it is still a presentational component. It manages how everything's going to look but it doesn't have to worry about where it gets its data. It just assumes that the data that we need for example the card and the functions that we need are in delete and on save are going to be passed into this component when it's created. So this is our card model presentational component. Now we're ready to create our container component which is new card model. So let's create a new component file source slash components/NewCard modal.JS and let's import card motile. We also, of course need to import connect and for the new card motile, there is one action that we're going to need and that is the ADD card action, so we'll get that from actions. Now this reminds me we don't actually have an ADD card action yet. So let's go ahead and open actions and it's actually really simple. We can just duplicate the add deck here, and so we have export const addCard and this is gonna take more than the name. It's gonna be a lot of properties, so we just call it card the type is going to be addCard and the data is going to be a card. Okay, so now that we have that action we can actually use it here. So let's go ahead and create map state to props and lets see what we know this takes the props and what we're interested in here is actually one of the rudder parameters. So I say, params and then we'll say deck ID Again. And the only property that we need from the state is actually going to be our card. Now, in the case of the new card model, this is not an existing card in the deck that we have to look up this is a new card. So let's create a new object here for card and we'll just give it a deck id property so that we can associate it with whichever the current deck is. And again, if this is confusing remember what we're doing here is setting card With a deckId:_deckId. So it doesn't have a front, doesn't have a back yet, it just has a deckId. Now we're also going to need to mapDispatchToProps, this is gonna take dispatch as a function of course and from here, we needed two functions. We needed onSave and we needed onDelete. Now, onDelete, we just won't include, so that will be undefined, which is a false value and it won't be used. OnSave, however, is going to be a little bit different. On save is gonna take the car data as its permit or as we've already seen and it's going to dispatch the ADD card event and add card takes that card as data. So that's really all we need to do to wire up on say we just take the card and dispatch the ADD Card event with that card. All right so now it's time to actually do the connection, so it's just to export default.We can call connect. We will pass it maps state to props and also map dispatch to props and to the function that's returned. We will pass the card modal, so now we have our new card modal. So now, we can actually see this in action if we head over to app.js. Let's go ahead and import this. I'll just duplicate one of our lines here, and we'll import the newCardModal from components/NewCardModal. And now, we need to create a new route. Now we want our visible cards to be displayed underneath our modal, so we're actually gonna make this a nested route. So I'm gonna add a closing tag to our visible cards component and let's create a new Route. So the path is gonna be /deck/:deck Id/new and that component is going to be the NewCardModal. All right, now we've written a lot of code here, without looking at the browser, so there might be a few bugs, let's see what we have, if we come back and we refresh the page, we don't have any bugs that's a good sign. Okay, so I'll click deck one, and we have our new card button displaying if I click a new card. Okay, you can see that the row has changed up here, so it's now deck slash ID slash new, that's good. However, the modal window has not appeared. Why is this and the reason is something we've already talked about it's that our visible cards component here does not expect to have children right because we have a nested row here. We need to be able to have nested components which means visible cards needs to expect the new card model to be rendered inside of it but it doesn't right now. So let's go ahead and open up visible cards and inside this diff we can still leave deck will display here, but after that let's go ahead and say children. And of course for that to be true, we need to get children as a property here. Okay, so now our visible cards component expects to have children and expects to render those children. So now, if we go back, notice that since the browser automatically refreshed the page and of course the road doesn't change our new card model is displayed right away. So let's see if this works, I can click the Cancel button and it disappears. I can click new card and it appears again, great, so let's say Front of first card. Card Back can be back of the first card if I click Save Card. All right, not quite and we don't quite get what we expect. Let's see, we have an error ReactDOM is not defined. Okay, so let's go back to our card model here and the problem is that when I imported ReactDom I imported dom in lowercase. So let's go ahead and save that, the page will refresh, that's good. So let's type in front and I'll type in back and I'll click Save and okay. So notice that it did disappear, that's fine, that's what we expected. And we're not currently displaying any cards. So how can we tell if this card was actually saved? Well, let's do local storage, .getItem state. And you can see, we have our decks here and then right here we have our courage property and it's an array with just one item in it. We have a card with a front value and a back value. Excellent and it has the same deck ID as deck number one. So let's see if we can do this again. Let's switch to deck five, I'll click new deck again and we'll say here's the front and here's the back and I'll click Save. And now, if I run that again you can see we have a second card down here with the front. Here's the front back here's the back and the deck ID. Yep, it matches the deck ID for deck five. Excellent, so now we are successfully creating new cards. There's one more thing I wanna point out that we didn't do in this lesson because we did it in the very first lessons, and that is let's open the reducers file. We haven't looked at this file for a long time and that's because we've been spending a lot of time setting up infrastructure. However, if we look at the actions file, we can see that in this lesson we created this ad card action. However, we were already ready for that in the reducer, we already had a card's reducer with an ad card case. So we knew this was going to happen already so we were already prepared for this. Now soon, we're gonna be adding other features that require new actions and so, we'll be coming back to this reducers file soon to add some more cases to some of our switch statements or maybe even add some more reducers. But for now, we have the ability to create a new card and I think that looks pretty good

Back to the top