[MUSIC PLAYING]
TODD KERPELMAN: Hi, everybody.
Welcome back.
I believe this should be episode six.
And hey, look, I got a new shirt!
So yay.
Since the time we have been doing this,
I finally went out and got a second shirt.
So now you get to see me in my second shirt
for probably the remainder of these episodes.
Now since we are back, and it did take me
a while to come back here, there's
a couple things I want to point out.
First, I don't know if you saw this email, but at some point
you're probably going to get an email that says, hey,
make sure you upgrade to the latest Firebase tools
in order to keep using any callable Cloud Functions.
The reasoning has to do with some security stuff
that the Cloud Functions folks are changing
and the Firebase CLI folks are changing in response
to those changes.
Long story short, make sure you have at least I think version
7.7.0 of the Firebase tools.
Let me see what Firebase version I have here.
Doot-doo-doo, all right.
So I got 7.7.0.
Make sure you have that-- at least that or later.
Or else when you try and call your callable Cloud Functions
in the last video, it's not going to work.
And you're going to complain to me in the comments and stuff.
Second thing I want to point out is that since we took a little
break since the last episode and this one, or at least I did--
maybe you're just binge-watching these--
we have to remember to restart the emulator.
So I'm going to go in here into my command line,
and I'm going to type Firebase emulators colon start.
Is that the right command?
It is.
And it's going to start up again both the local server
and the local Cloud Functions, as well as the local instance
of Cloud Firestore.
And you have to remember that in the local--
when Cloud Firestore starts up again,
it basically starts up empty.
So all those restaurants I had before, all
my user preferences, all that stuff is probably gone.
And so because-- remember right now
we're sort of hacking in our array of favorite restaurants,
I need to make sure that that is still going to be up to date.
Or at least reflect some accurate restaurants
when I go ahead and start doing that within my web app.
So I'm going to go ahead and open up my browser here.
And let's look at local host 5000.
As you can see here, I have no more data,
because my local Firestore has been deleted.
Let me click Add Mock Data, and now we
got a whole bunch of data.
All right.
But-- let's see.
I'm going to check two things here.
I'm pretty sure this is still the same user ID so
that has been retained from session to session,
as it should be.
But it does look like I no longer--
my array of favorite restaurants,
like if we jump into--
I believe it's under FriendlyEats data.
Remember how we sort of hacked in this favorites
array of totally fake data?
Well, basically, this now reflects
restaurants that no longer exist,
because we had to regenerate that entire list
of restaurants.
Let me pick two that look good.
Let's pick these two with the chef-looking guy.
So we got Place Best.
I'm going to basically just go ahead and copy those constants
into here.
And Drive Thru Fire, let me copy that into here.
We'll save this.
Let's go ahead and sort of reload our front page.
And now, I think if everything is still
working the way it did before, if I click on My Favorites,
we're going to take a look at our favorites array,
and we're going to get back.
That's right, this JSON-y looking
object that has our two favorite restaurants.
Looks like Place Best and Drive Thru Fire as our favorites
array.
And we get that back as JSON-y data.
So yay, I'm back up to where I was in the last video.
Like I said, if you were just sort of binge watching these,
then you probably don't have to do this.
But if at any point you're like, all right, I'm done for now.
I'm going to Control C out of these emulators
and get back into my app, you're going to have to do this, too.
So turns out that for most users,
getting to see data in the JavaScript console
is not the right approach.
They actually need to make some changes in our web page.
So let's get this data and actually get
it showing up into our app.
Now as you recall, what we're getting back
from our Cloud Function kind of looks like document data.
But as far as our local code is concerned, it's not a document.
It's not a Cloud Firestore document.
It's just a bunch of JSON-y data.
But that's OK.
We're going to show you how to take that JSON-y data
and basically turn it into, again,
another restaurants list.
So let's jump into our code here,
and I'm going to go under my friendly eats view.
So I have this function called View
List that you can see right now takes the list of filters
and a filter description and goes ahead
and generates this whole page of restaurant tiles-- panels.
Or I guess cards is what we call them.
Now, I think probably what I'm going to do here--
and this is a little bit of a hack--
is I'm going to go ahead and copy View List
and basically turn it into another function that's
called view favorites.
And then once I have this all looking nice, or at least
working, then I'll figure out how to combine them and remove
all that duplicate code.
But for right now, kind of a bit of a giant
hack where there's going to be a lot
of duplicate code in our app.
And we'll fix that at a later point.
So let's see here.
Let's go ahead and we're going to create this thing.
We're going to call it view favorites.
We don't need-- we don't need our filters or a filter
description.
Basically we'll just say our filter description here is--
we'll call it my favorites.
My favorite-- your favorite foods.
At least for now, we're only looking
at the foods of the currently logged in user.
Oh, right, it's probably--
there we go.
It didn't like the fact that I got rid
of that second parenthesis.
The next thing I'm going to need to do is probably go down here.
And it's going to complain because I no longer am
passing in filters.
So all this is going to be broken.
And I'm no longer going to be getting all restaurants.
I'm going to probably want to call this get favorites.
And there you go.
So the way, for instance, this get
all restaurants call works in the original function
is we create this whole other helper function called
render results that takes in a document
and then basically does a whole bunch of stuff.
This code actually is all basically
what to do if there is no data in our results.
And then this basically takes that data,
massages it a little bit to what we need, and then sends it off
or basically creates this restaurant card out
of that data.
That's kind of mostly it.
And then adds it into our main page,
and then this sort of stuff basically just adjusts
the way our filters look.
So we're kind of going to do the same thing here.
We have this render results function.
Right now it's expecting a document,
but I think we can basically change that
so that instead of expecting a document
it will go ahead and expect a JSON-y looking thing.
First thing I want to do is--
well, let's see.
Let's just kind of go through this top to bottom.
So this querySelector that says, hey, let's
look at your favorites.
Most likely when we're looking at our favorites,
this should be something like, we
should stop viewing favorites.
We'll figure out what to do here in a bit.
And let's just kind of try this and see where we're at so far.
All right, so our main page of looking
at all documents, that still works fine because we
didn't really mess with that.
I'm going to click on this.
And well actually, I guess nothing has really
happened because we are not calling this function right
now.
So let's do that.
Here we'll replace getFavorites with viewFavorites.
You'll notice that there is kind of a viewFavorites
and a getFavorites.
The getFavorites is actually the call
that talks to the database.
The viewFavorites is the call that actually
makes it display in our HTML.
OK, there we go.
So now I am clicking on it.
It's calling our viewFavorites call that has essentially
cleared out all of our data, and it's
started the part of fetching our restaurants.
But I actually think for starters
our restaurant is not even looking at this render results
function yet.
So that's why we're getting nothing.
So let's fix some of that.
Go into our getData function, and getFavorites
looks like-- yeah, see it's a function that then just makes
this call to our cloud function.
If we were to look at, for instance, getAllRestaurants,
you can see here It's actually calling this render function
a little later.
So let's do some of that.
So we're going to take a function that's called render.
And within here, sure, we can still
log our result. But then let's call render on our result.
Or you know what, sorry, our result here--
what was that noise?
All sorts of weird stuff going on in the studio.
So I take that back.
Our result, as you recall, is actually an array of objects.
So what I need to do is let's take our result.data,
and we'll call for each.
And in here we're going to get a restaurant.
Did I spell restaurant right?
Looks like I did.
And now we'll call render on an individual restaurant.
All right, so we're making some progress here.
Our getFavorites call is actually going ahead
and rendering our restaurant.
But I'm pretty sure if we were to reload this and run it,
yeah, we're going to run into problems.
Because the render function that we have copied and pasted
is basically still expecting a document,
and it's hoping to be able to call data on it.
And as you recall, we just have JSON-y stuff.
So let's go back here and fix this.
So just to clear things up, I'm just
going to-- instead of calling this doc,
I'm going to call his data.
And we'll replace it in places where it causes trouble.
So basically, all this is like it's basically saying,
hey, if for some reason I'm not getting data back
in my getRestaurants function, all of this really just kind
of shows a hey, you have no restaurants result.
That's all it does.
So I don't really need to call data equals doc.data because we
already have the data.
So I'm going to go ahead and delete this.
And here it looks like we're grabbing our document ID
and storing that.
And we do kind of need that so later on when
we want to have a link that goes to that specific restaurant's
details, we know where to go.
But I don't think we have that right now.
So maybe we need to update our cloud function.
Let's see here.
So here we are.
We're taking our snapshots, and we're
returning our snapshot data.
I think probably what I need to do here is
let's expand this a little bit.
Let's see.
We're going to say how about this?
Const snap data equals snapshot.data.
And then what we can do is like snap data, and here we're--
this is-- there's nothing special, really,
about this field that is called .id in quotes.
This is basically just a special thing
that we added with the assumption
that we're probably not going to have
a lot of fields that have a dot period
at the beginning of the name.
Therefore, it's probably safe to overwrite or hard code
in our code here.
And this will actually be our snapshot.id.
And why is this giving me an error?
Because object is possibly undefined.
We'll need to return data, snapData.
What should we do here?
I suppose what we should do is let's just--
we could force unwrap it, but let's just
say if snapData then we'll do this and return that.
I think we might need to be smarter about this.
I'm going to put in a little to-do here or something.
To do, make sure we're not completely broken
if a snapshot is null.
We might be making some assumptions that we're always
going to have data.
That might cause problems later on.
I'm just going to add a little reminder myself of let's go
back and double check that this all works.
But all right.
We got our document ID for now.
So should be pretty simple here.
Let me go back into my--
am I in the right place?
Go into functions and call npm run build All right
that all looks good.
And in fact you know what let me just reload this and see
what happens
Still an error but this time now I bet you my array--
look at that.
We got that .id field.
So that worked.
So now I think it should be fairly simple to fix up
our render call.
So we actually don't really need data equals .id
because basically this is already included our cloud
function.
So I'm just going to delete that.
But we do need to now call it here--
so you know what, let's just say const docId equals data.id.
And then we'll call docId here and let's see.
And then I think we can also just swap in
that constant here, as well.
docId.
And the rest of this looks like it should just kind of work.
All right, well, let's try it.
Going to reload this.
Click my favorites.
Oh, look at that.
We got like data and stuff, and it's our favorite restaurants.
Wow, that was-- and actually, I think
I could still click on this.
And I get details about Place Best.
Wish we had some ratings.
Let's add a couple of ratings.
Oh, that's right.
Oh, there we go.
That's cool.
All righty.
Double check one more time.
Whoops.
I don't want to add a filter.
I want to get my favorites, and there they are.
Cool.
All right.
How are we doing on time?
How much time we got so far?
14 minutes.
All right, you know what?
We got time to do a little bit of refactoring.
So let's do some of that.
All right, so there's a bunch of places
here we have some duplicate code.
I think one thing I want to extract
is this bit here where we're rendering a restaurant.
So let's see here.
Let's create a function call.
We'll call it friendlyEats.pro totype.renderRestaurantCard.
And we're going to pass in-- it's going to be a function
that's going to take in like--
I think we're going to need a reference to this main element
so we where to put our restaurant card
and then we'll pass in our data and our docId
as two separate arguments.
And then I think what we can do here is let's look at this.
So we're creating our function to go to the restaurant.
We are checking to see if the restaurant card already exists.
We're sort of adding in our ratings and the price.
We're adding that on, and then we're
basically appending all of that to our main element.
So you know what?
I think I can just cut this.
Paste it in here.
And now I think I actually don't need that anymore.
This can now be--
this that can now be this.
If this were any other language that
would sound very confusing.
But it probably still does, but yeah.
Now our all over that's can be this again.
Double check my note.
See if that'll work.
Might.
And so now I think we can just call this function for up here.
So we're going to say that.renderRestaurantCard.
We're going to pass in the main element, our data,
and our docId.
And let's see if this works.
Let's reload this.
Seems to work, except we're getting a cannot read property
router of undefined.
Oh, this is another that is this error.
Basically, router here I believe refers to the--
oh, boy.
[LAUGHTER] Essentially, I think this is another that is this
where this function now--
this is no longer defined to the larger friendlyEats object.
I think it's sort of a local this basically representing
the closure in this function.
Like I said, this is not stuff I'm super expert in.
But basically, I think this can be fixed
with a var that equals this.
My gosh.
And then I think if we just have that router navigate,
this should all work now.
Let's try that one more time.
So I click on our favorites.
I see Drive Thru Fire.
I click on that.
We can get to details about Drive Thru Fire.
All right, looking pretty good.
That seems to be working pretty well.
So you know what, let's double check and make sure we can call
renderRestaurantCard within our view list.
Basically we're going to replace all of that with main element
data and doc.id.
Here docId is not a separate variable.
And I think it should all still look good.
So let's reload.
Looks like our main page is still rendering.
OK so yay, we refactored a little bit.
Honestly, you can probably see how
we could do a little more refactoring that
does this repeated times.
Like maybe we decide that this is probably
a function that can be extracted,
a show empty state function.
And probably a show the main header function,
and show the filter description function.
And I actually think probably the right thing to do
would be to keep extracting chunks of logic
out of this view list or view favorites function
into smaller functions, and then just have
them called from both of these.
That's the right thing to do, but I'm
going to the lazy thing.
Which is I think at this point these functions
are similar enough that I could probably just
add in isFavorites argument into my main view list,
and kind of do a little bit of if-then statements
all over the place.
And that'll get us there as well.
Just because this is not necessarily a video
about refactoring and chopping up
your functions into lots of little ones,
but I'm pretty sure the--
who's that author?
It's not Code Complete.
I don't know.
There's an author who would be very proud of me
for chopping this up into little functions.
Instead he's going to be very embarrassed
that I'm not going to do that for the rest of this.
I think basically probably the right thing to do--
or not the right thing, but the thing I'm going to do--
is we'll just pass in a third argument call favorites.
And essentially do some if-then statements
throughout our view list to make that work.
But you know what?
Maybe this is a good stopping point.
We're at the point now where we actually have our favorites.
Well, how are we doing on time?
All right.
Yeah, you know?
We're at a point where you can now
view our favorites within our web app,
and that's pretty exciting.
And so yeah, we'll finish up our refactoring in the next video
and then maybe move on to some other things.
So thanks for staying around and we'll
see you on the next episode of "Firebase in My Life."