4.2 Display a List of Posts
Now that we can parse our documents, we need to display them. In this lesson, we'll write a repository to access our posts and to display them in a list.
1.Introduction1 lesson, 01:40
2.Get Started2 lessons, 11:54
3.Build the App4 lessons, 56:39
4.Display Content3 lessons, 38:55
5.Conclusion1 lesson, 01:10
4.2 Display a List of Posts
In the previous lesson we ended up creating a class called post, which represents our individual documents. It has a title, it has a publish date, and it also has the content of that post. So we need some way of retrieving these posts. And since we have a concrete class to work with now, it kind of makes sense to have a repository that we can use to retrieve those posts. Now this is going to be a read only repository because we don't want to edit or delete any of our posts. That is going to be done through the web hook. And the web hook deals exclusively with the raw files. Our post is going to be something that we would use inside of the controller that will be serving up the HTML for end users. So let's create a new class inside of our data folder. And we're going to call it PostRepository. Now usually I would have an interface that would be an IRepository, or an IReadOnlyRepository, or something along those lines. I'm not going to do that here, because I'm going to keep things simple. So we need a few things. We need to be able to retrieve all of our posts. So let's stub out our methods. And then we will get into implementing them. So, this is going to return an IEnumerable of Post. And we'll just call this GetAll. So eventually this is going to return all of our posts. And then we want to be able to retrieve an individual post. So this is going to return a Post, well call it GetPost. And since everything is based upon the path, we will pass in the path of the file that we want. And this will be something relatively simple to do. The first thing we will do is get the content, and we would need to read the file. So we can create a method called ReadFile. We will pass in the path and let's assume that we won't have an extension. The repository knows what type of extension that we will have so we will hard code that here. So we will get the content and if the content is not null, then we will parse that. So we will call Post.Parse, we will pass in the content, and that will give us our post. Otherwise, we will return null. So let's write that ReadFile method. It doesn't need to be public, but it does need to return a string and we will accept the path. And let's check to see if this file exists. So we'll say if (File.Exists), and we need a using statement for system I/O. We'll pass in path, then we will return File.ReadAllText and we will pass in that path. Otherwise we will return null. So we have our method for retrieving an individual post, let's implement the one that's going to retrieve all of our posts. And if you remember a few lessons ago, we created that FileStore class that's used for working with the raw files. And that would make sense to put a method here that we could use to retrieve all of the files within the base path. So let's do this. Let's have a private read only FileStore. We'll just call it _files. And then for our PostRepository constructor, we will accept the string for the basePath, which will be our wwwroot. But in this case, the PostRepository's basePath is actually going to be the posts folder inside of wwwroot. So it will be this folder, here. So we will say _files = new FileStore and we will pass in a string that has the base path. And we will also add in posts. So that all of the files that we are going to be working with inside of this PostRepository is going to assume that, is going to be inside of the posts. So then we can come down here inside of the GetAll method and we can say var files = and we would use our FileStore. We could say that we have a method called GetsFiles and that will retrieve all of the files directly within the posts folder as well as all of the sub-directories. And then we need something to store those posts so we could have a new list of posts and we will return posts.ToArray. Now whenever I return an IEnumerable of anything and if I've been working with a list, I always use the ToArray method. In this case, it's not that big of a deal, but if you were working with class data and you are returning class data as an IEnumerable, you don't want to return a list or anything like that, because whoever's consuming that code can then cast it as a list, and then they could mess up your class data. So if you call the ToArray method then they essentially get a copy of that data and they can't mess up what you have as far as the class is concerned. So, we have our files. We have the posts that we will eventually return, so let's loop over our files. So, we'll say var file in files. And we will want to get the contents of each file, so we will call the ReadFile method. We will pass in the file that we are working with, then we will get the post, so we'll say Post.Parse. We'll pass in the contents there. Now, if we don't have a post then we will skip that, but if we do then we will add it to our posts. And then at the end we return our posts. So really, we just need to write this GetFiles method inside of our FileStore. So let's go over here and let's go ahead and say public IEnumerable. And we'll call that GetFiles. Now traditionally, when finding all the files within all of the subfolders, we do so recursively. We will get all of the files and all of the directories within the given directory and then for those directories, we will then loop over those again using recursion in order to get the files in those. If that doesn't make sense then the code will. So, let's have a second method, this is going to be private, it's going to return an IEnumerable of string. We'll call this GetFilesInternal. And this is going to have the path. So that our public method is going to do this, it's going to call GetFilesInternal, it's going to pass in the basePath. And that will kick start everything and then GetFilesInternal is what is going to give us all of our files. So we'll say var files = new List, and then we want to loop over all of the files in the directory specified as path. So we will have a foreach loop, var file in directory.getFiles. We will pass in path and then here we will simply add each file to our list. But then we want to loop over all of the directories within this folder. So, we'll say var dir in Directory.getdirectories. We'll pass in path once again. And then this is where we are going to call GetFilesInternal, passing in the path to this directory. That will give us all of the files here. And we will simply add that to our list. But we will use the AddRange method. And that will give us the files inside of the subdirectory. So that after both of those loops, we'll simply return files and we will call the ToArray method. Now, we could take advantage of the search pattern for the GetFiles method so that we can then filter that out, and we wouldn't have to process a lot of files. So let's just go ahead and do that. We're going to add string pattern here. And we will have to change our code because we can't just call GetFiles, pass in the path, and then pass in the pattern. Because if pattern is null, then we're going to get an exception. So instead, lets do something like this. We'll have a string array. We'll call this filesToAdd and then we will have an if statement. If (!string.IsNullorEmpty(pattern)), then we will say a filesToAdd is going to be equal to Directory.getFiles. We'll pass in path and then pattern. Otherwise we will call the GetFiles method. But we will omit the pattern parameter, and then we will loop over filesToAdd. Now, that means that we will need to add a pattern parameter to our GetFiles, but we can make this optional. So we can initialize it as null and then we have already taken that in to account inside of GetFilesInternal. So if we go back to PostRepository and if we say that we only want the files that have an md extension, then we've just made our code a little bit better. Well, let's make a new controller, one that we will use for displaying these posts. So let's add an MVC controller and I'm going to call it BlogController. And we need to pull in our hosting environment just like we did with our hook controller. So, let's add a private read only IHostingEnvironment. We need a using statement there for Microsoft ASP net hosting and let's call that _environment. And we're also going to have our repository, so let's make another read only, but this is going to be PostRepository, and we will call this _posts. And then we will have our constructor. So we are going to accept that IHostingEnvironment, we'll call that environments once again. And we will set our _environment to that parameter. And then we will create our repository. So _posts = new PostRepository. And here we are going to pass in the WebRootPath. And let's go to the Index method. This is supposed to list all of our blog posts, so we will say var posts = _posts and we will call the GetAll method. But, you know, these should be ordered descending. We want the latest ones at the top. So let's OrderByDescending and we want to do so based upon the PublishDate. That will give us our model, which we will then pass to the View. And then we need to create that View. So let's go down to the Views folder and let's add a new folder called Blog, and then we will add a new view called Index. We want an MVC view page. And for the model, it's going to be an IEnumerable of post. And we're not going to do anything fancy here. We're just going to display our posts. So let's have a div with the class of row, and then we will have a foreach that iterate over our models. So, var post in model and we are going to display our title with an h3 element. So post.Title. And then for our content, that could be a div. I guess that's what we would want. And then let's separate these with a horizontal line. Now, we should only have one because we only have one mark down file. So I guess we could create another one just to see what that's going to do. So let's go to our posts folder and let's copy the one that we have and we will paste it so that we can have two. Let's call this something different, if Solution Explorer would stop going away. Then we can rename this, and let's give it a different date as well. So, the first will be the 27th. The second will be the 28th, and this is another test. We need different data. So, let's say that this is content of the post in the second test. And that's okay. Let's also set the date. And this is another test. Okay, so let's run this. Let's see what we get whenever we go to /blog. Well right now we get an error. So let's see what it is. There's no argument given that corresponds with the required formal parameter pattern. Yes that is inside of FileStore. Whenever we call GetFilesInternal, we also need to pass in the pattern. That was very important. And that should be the last error. Okay, so we are running this. We are going to go to /blog and we are going to see what we get. Hopefully we will see our list of posts, but if not, we will fix it. Now, we do, but we don't have the actual HTML, everything was encoded. That's easy enough to fix. If we go back to our view, we can say @Html.Raw. We will pass in the Content of the post and that will fix that. So if we refresh, there we go. Well now we can display all of our posts, but we also need to display a single post. And we will do that in the next lesson as well as finally publish this application and actually see if it works.