Getting started on ASP.NET Core & React - episode 04

Getting started on ASP.NET Core & React - episode 04

Contents

  1. Episode 1 – Introduction
  2. Episode 2 – Backend / Infrastructure
  3. Episode 3 – Frontend Setup
  4. Episode 4 – Data flow (you are here)
  5. Episode 5 – Tests / Security
  6. Episode 6 – It’s your turn

Introduction

We’ve got prepared working ASP.NET Core application with whole frontend stuff set up. You can think – it’s good moment to start working on features. You’re right, but still we’ve got some things to do before we’ll be able to focus only on features. In this episode we’re going to show you some basic concepts of so called architecture and data flow. You can write your whole logic in controller like in most of tutorials you can find on the Internet, but it’s good manner? Obviously it’s not, so stay tuned!

To-do list for this episode:

  1. Add first custom entity to database
  2. Create layers – DataAccess, Services, Contract
  3. Prepare example data flow through those layers
  4. Create MVC controller for features related to project management
  5. Create React-based Projects list
  6. Get data from server using fetch API
  7. Create project details and delete functionality

1. Add entity to database

Something with database we’ve already done – membership system. Ok, but we’ve got all this for free. All what we had to do was just run Update-Database command, so how to create database entity from scratch? Let’s try this out!

  1. Go to „Data” folder and add new class „Project”. This will be our new entity.
  2. Open ApplicationDbContext.cs, add DbSet with Project entity and configure primary key for that entity.

    Ok, let’s explain what’s going on here. First of all, we’re using Object-Relational Mapping tool – Entity Framework (to be more specific – Entity Framework Core). Thanks to that we can work with database as let’s say objects. We don’t have to write plain SQL commands, instead of this we can use powerful tools like LINQ to Entities which is great.

    Next important thing – we’re going to use Code-first approach. That means we write normal C# code and based on this model in database is created. It’s nice, because we obtain whole migrations mechnism for free, we can easily track changes in model schema and we’ve got clean, readable code. Other approach as you can guess is Database-first which means that we first create database in Management Studio and than we generate C# classes.

    Let’s go back to our code. Project.cs is normal C# class, so we must do sth to inform Entity Framework that this will be database entity. To do so, we’re using DbSet<Project> in ApplicationDbContext.cs. Next thing – primary key configuration in OnModelCreating method. It’s another approach to work with Entity Framework and it’s called FluentApi. Another available option is to use Data Annotations. In FluentApi approach which we chose, all configuration info like primary keys, foreign keys, fields restrictions etc. will be presented in OnModelCreating method – we’ve got all in one place.

  3. Once it’s done add migration. Go to Package Manger Console and type „Add-Migration AddedProjectEntity” command. „AddedProjectEntity” it’s just name of this migration, so you can call it as you want.
  4. Next type „Update-Database” command and it’s done. Go to Management Studio and check if new table has been added.
  5. After all this steps your solution should looks similar to this one:
  6. It’s good moment to commit your changes 😉

2. Add DataAccess layer

First let’s try to shortly explain why we need those layers. From our point of view .Web project is most important for us. We’ve got there controllers, views, scripts and styles. Can’t we put all required code there? Of course we can and if you’re going to create some really small application for your own it’s enough. But we’re trying to show you example of real-world application. We’re not able to show you all aspects of this kind of projects, becouse normally it takes few months to create even let’s say small size web application, but we want to show you as much as we can in this short time.

Layered architecture is right now the most popular one. Of course there are other architecture types which you can choose depending on your needs: Event-driven, Microservices, Microkernel etc. Ok, but why we need it? Basically for three reasons:

  • maintainability – how easy it is to maintain the system. For example changes in one module of application shouldn’t break anything in other modules
  • extensibility – how easily the system can be extended with new functionality. We should be prepared to introduce new module or extend existing implementation of some module at any time
  • reusability – how easy we can reuse module in a differenet systems

As you can see we’re talking about modules which means that our application should be devided into those ones (in our case layers). Modules should help us to write code which is low coupled and thanks to that it’s easier to fullfill above concepts. There are also other more advanced things like e.g. scalability.

Before you go further try to read more about it on the Internet. For example there is very good article about this topic.

Ok, let’s add our first layer – DataAccessLayer. As its name suggest this layer will be responsible for data access which means that code related to, in our case, Entity Framework will be there.

  1. Right-click on solution and choose Add -> New Project.
  2. Choose Class Library from .Net Core node on the left side and type SoftwareHouse.DataAccess name.

Next we’re going to add reference from our .Web project to DataAccess layer. Without it we won’t be able to use any code from DataAccess layer in .Web project.

  1. Right-click on Dependencies in .Web project and choose Add Reference.
  2. Check the checkbox next to SoftwareHouse.DataAccess and click ok.

DataAccess layer is created and we’ve got reference to it which means we can move our data acces code from .Web project.

  1. Go to Data folder in .Web project and move it content to .DataAccess project.
  2. Delete Data folder in .Web project.

Ok, code is moved, but as you noticed there are missing some things in .DataAccess project and solution can’t compile. To fix this problem we need to install appropriate packages in .DataAccess project.

  1. Right-click on solution and choose Manage NuGet Packages for Solution.
  2. There are list of all installed packages in your solution. Choose each one presented in list belowe the screen and install them to .DataAccess project. To do so, just check the checkbox next to .DataAccess project and click install. List of packages to install:
    Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
    Microsoft.AspNetCore.Identity.EntityFrameworkCore
    Microsoft.EntityFrameworkCore.Design
    Microsoft.EntityFrameworkCore.SqlServer
    Microsoft.EntityFrameworkCore.SqlServer.Design
    Microsoft.EntityFrameworkCore.Tools

Now, solution should rebuild successfully.

Important note – because we moved code associated to Entity Framework, if you would like to add migations or update database using Package Manager Console you must change Default Project to .DataAccess project.

Great, we’ve got created first layer of our application architecture. If your solution rebuilds it means that everything is working and we can make some refactoring. As in previous episodes there is not a lot of things to refactor, but we move classes from place to place and it’s good to reorganize namespaces.

  1. Go to .DataAccess project and change namespaces in each class there to appropriate. For example in ApplicationDbContext.cs new namespace should be SoftwareHouse.DataAccess. Analogously in ApplicationUser.cs class new namespace should be SoftwareHouse.DataAccess.Models. Namespace changes will cause errors in few places, but you already know how to deal with this kind of problems – we’ve done similar thing in Episode02.

3. Add Contracts layer

In this layer we’ll hold interfaces and data contracts which are necessary to communication between other layers. Becouse we keep all interfaces in separated layer we should be able to easily change it’s implementation or even replace whole layer.

Generally interfaces are another big concept. Shortly it helps us to write low coupled code, but it has much more advantages like e.g. testability. You can read more about it there.

  1. Right-click on solution -> Add new project and type name „SoftwareHouse.Contract”.
  2. Add reference from .Web and .DataAccess projects to just created .Contract projet (do it the same way as you did it with reference between .Web and .DataAccess projects).

4. Add Services layer

There we’ll hold services with so called business logic. If you need to do some data processing you should do it there.

  1. Right-click on solution -> Add new project, type name „SoftwareHouse.Services”.
  2. Add reference from .Web project to .Services project.
  3. Add reference from .Services project to .DataAccess project.
  4. Add reference from .Services project to .Contract project.

5. Data flow

Ok, our „just-enough” architecture is done. Finally it’s time to write some code.

  1. Go to .Contract project and add DataContracts folder.
  2. Inside this folder add ProjectDto class.
  3. Add Repositories folder to .Contract project.
  4. Add interface in this folder called IProjectsRepository – for now it will contain definition of two basic methods:
  5. Go to .DataAccess project and add Repositories folder.
  6. Add ProjectRepository class in Repositories folder.

Finally it should looks like this.

Next, let’s go add some business logic code.

  1. Go to .Contract project and add Services folder.
  2. Add interface IProjectsService in Services folder.
  3. Go to .Services project and add Services folder.
  4. Add ProjectsService.cs in Services folder.

There is one new class here called CommonResult – don’t worry, we’ll cover it in next section.

After this steps project structure should looks like this.

Ok, we’ve got last thing to do to make those layers working. This thing is Dependency Injection. You can read another great article about dependency injection here.

Thanks to God or I should say thanks to Microsoft in ASP.NET Core we’ve got embedded DI Container, so everything what we need to do is just use it.

  1. Go to .Web project to Startup.cs.
  2. In ConfigureServices method register ProjectsRepository and ProjectsService.

Thanks to that in ProjectsService where in constructor we typed IProjectRepository as parameter, Dependency Injection container will automatically inject ProjectsRepository instance. That’s it. Don’t forget to read more about DI, especially about available lifetime types (in attached link you’ll find it below „Service Lifetimes and Registration options” header).

6. Add new project functionality – backend

It’s time for our first feature – possibility of adding new project. For now we’ll focus just on backend.

  1. Let’s start by defining data contract class for our functionality. It will be called AddProjectDto – put it near ProjectDto created before:
  2. Go to IProjectsRepository and add „Add” method.
  3. Go to ProjectsRepository and add „Add” method with its implementation.
  4. Go to IProjectsService and add „Add” method.
  5. Go to ProjectsService and add „Add” method with its implementation.

As you noticed here we’re also using CommonResult mentioned before – of course nothing like this exists yet. Generally it’s example of controlling data flow based on operation result. Let’s say we want to have unique projects name in database, or we want to execute some additional validation. It’s simple implementation of this. Thanks to CommonResult object we’re able to return operation status and optionally data or error message.

  1. Go to .Contract project.
  2. Create Common folder.
  3. Add CommonResult class in Common folder.

CommonResult is super simple pattern for extending result of some operation with additional information about status of it. By using two static methods – Success and Failure we can create instances of our CommonResult class, which will contain three (or two in non-generic version) basic properties – Item (operation result – returned data), IsSuccess (flag), and ErrorMessage. We can use this object to check if i.e. adding some data to database succeeded or failed.

Ok, but there is next problem – missing GetByName method. In order to fix this do following steps:

  1. Go to IProjectsRepository and add GetByName method.
  2. Go to ProjectsRepository and add GetByName method.

Done. Try to rebuild solution and if succeed don’t forget to commit your changes 😉

Last thing in Add project feature. As I mentioned earlier we want to have unique project names in database. We’ve added appropriate logic in services layer, but it’s good also to restrict this in database. To do so, do following steps:

  1. First you can open Management Studio, expand tree to see tables from our database, right-click on Projects table and click Design – you will see there that „Name” column is not required. That measn that someone can add project without any name. We want to change that.
  2. Go to ApplicationDbContext.cs.
  3. In OnModelCreating method add following code.
  4. Open Package Manager Console, add migration and update database (do it just like we did it earlier).
  5. Now you can go to Management Studio once again. Refresh database on tree on the left side na click Design on Project table. Column „Name” should be required now.

Ok, it’s nice now, but we can do it even better. It’s your task 🙂 Try to restrict unique project names in database using triggers. Find more about it on the Internet 🙂

7. Delete project functionality – backend

Second feature for this episode – delete project. Let’s do it quickly.

  1. Go to IProjectsRepository and add Delete method.
  2. Go to ProjecstRepository and add Delete method with its implementation.
  3. Go to IProjectsService and add Delete method.
  4. Go to ProjectsService and add Delete method with its implementation.

That’s mostly everything what we need for now to implement basic features in the top-most layer (Web). Let’s jump into front-end.

8. Cleaning up front-end stuff

In previous episode we created our first React component, but it was only for demo purposes. It was very helpful because with this approach we created basic, working version of front-end project structure, but now of course files like DemoComponent won’t be very useful.

Now we can remove everything from Frontend folder and create new directory structure:

  • Frontend/Home
  • Frontend/Projects
  • Frontend/Shared

In first two folders we’ll keep page-specific code, in the last one we’ll put share utilities and helpers.

You can also go to webpack.config.js and in entry object you can keep only libs definition:

Now few words about our Frontend folder structure:

Home

Homepage for non-authorized users will be React-free. There won’t be any single components – we want to create simple layout so we only need views and styles.

Projects

Projects directory will be dedicated to all files and components related to ‚Projects’ page – we’ll create it in a few minutes.

Shared

In Shared you can create two folders :

  • Shared/Components
  • Shared/Styles

In the first directory we’ll keep all reusable React components, in the second one – all reusable css rules and mixins. We’ll fill this directories with files later.

Now, it’s time to update our views – let’s start by creating basic flow with homepage for authorized and non-authorized users.

9. Updating homepage

Let’s start by update layout for all views (Views/Shared/_Layout.cshtml). Comparing to version from previous episode, here we added link to Lato font (in the head section), we also updated navbar by removing all default links like Home, Contact and About and we added one link to Projects site which will be visible only for authorized users.

Here’s the updated file:

You can also see placeholders for two sections – in head there’s place for Styles section, and at the bottom of body tag there’s Scripts section (child views can inject something there if needed and you’ll see how to use this feature in a moment).

Now let’s take a look at specific views. First, we’d like to update our homepage. To do this, we need to go to Views/Home/Index.cshtml and we can create basic layout for our home:

By using Bootstrap grid system we can use existing css classes like row and col-… to create responsive layout in very cheap and fast way. You can read more about it on Bootstrap’s website – right here.

What if we’d like to customize our view slightly? Of course it needs more style! In our layout view we’ve prepared place for custom per-page sections, where we can apply some custom styles and scripts. On the homepage we can for example create custom stylesheet file and inject it at the top:

But this file is missing! To be able to use it you can go to Frontend/Home and there create home.scss (home.css will be built by Webpack later automatically) file:

That’s… boring, I would say. Let’s add something more interesting. What if we’d like to set custom margins here based on users’ screen size? We can of course use css feature called media queries to define some rules for given breakpoints (applied if screen type or size will be proper).

At the top of this file please add rule for our banner:

Now our banner will have margin applied only if users’ screen width will have at least 992px width. Why exactly 992px? This value is matching Bootstrap’s grid system – that’s medium breakpoint. Smaller one is equal to 768px, and greater one is equal to 1200px.

We can even extract these values to different file and reuse them later.  To do this, you can go to /Frontend/Shared/Styles and create file breakpoints.css:

Let’s go back to home.scss and replace hardcoded value with one of these variables:

Cool! Now we have one place for all breakpoints, we can import these values to different files and in case of changes in the future we will have only one place to update something. You should use scss features like variables and mixins as much as possible to keep your code clean and really reusable.

Let’s move forward – it would be nice to be able to use our stylesheet for homepage if it’s ready ;). Which tool will help us in building all this stuff? That’s right – Webpack. Please go to webpack.config.js and in entry points section add entry for home:

With defined loaders for scss files Webpack we can use scss files as entry points. After executing webpack command two files will be created – /wwwroot/dist/styles/home.css, and, as a drawback – /wwwroot/dist/home.js.

We can get rid of this home.js file later, because that’s not needed for now (we don’t have any scripts for homepage) but for now it’s not a problem. With production app that config wouldn’t be perfect, but with limited amount of time during the course we can stick with it for now.

With basic setup of layout and home view we can run our app and check if everything works correctly. It should looks similar to this (you can create your own logo, and put it in /wwwroot/images directory to use it, like we did):

With homepage like that we’ve gained at least +30% to awesomeness. It would be nice to also have some features, and now it’s perfect time to add them.

10. Making flow better

What’s the current flow for users who successfully logged into our system or created new account? They are redirected to homepage. This homepage looks nice, but we want to show them something more interesting than few blocks of text!

Since we’ve decided that we are going to create software for managing our Software House, we can start by creating Projects list page and redirect our users there.

Let’s start by create basic controller – ProjectsController.cs in Controllers folder. For now it should have only one method:

At the top we can also add Authorize attribute to make sure that only authorized users will be able to execute methods from it. Also we need to use dependency injection and inject there something which implements IProjectsService (of course for now it’s ProjectsService):

Also we’ll need a view. In /Views/Projects create file called Index.cshtml and update it’s content:

Great!

Now if we have something to show users after logging in / creating account, we can redirect them there. First let’s start by updating RedirectToLocal method in AccountController (at the bottom):

This method is called from Login and Register methods above, so that’s all what we need to fix flow after signing in and up. One last thing – we can disable homepage for authorized users by adding simple if statement in HomeController.cs / Index method:

If user is authenticated, after going to „/” or „/Home/Index” he’ll be redirected to Projects page. Our app works like Facebook now, but there’s still not enough features!

11. Projects components kick off

Every Software House has to deal with various projects. We want to give our users possibility to take a look at all projects available in their companies by creating first React-based component –  Projects List.

Should we jump straight into .tsx files and write some code? Maybe… but I suggest you to start by creating some kind of ‚mental model’ of thing we’d like to create. Let’s try to think what would be the data flow inside our components – at the beginning we definitely need to make a request to the back-end to get all available projects, then we need to pass them to some kind of list, and maybe we also need to create some kind of summary. During waiting for data it would be nice to calm down our users – they have to know that everything works correctly and data will be here in a few milliseconds. There’s no guarantee that there will be projects available, so we need to find a way how to tell users that they should start by creating first project.

We can create simple wireframe model of our page in just few seconds:

By looking at this design we can tell something more about the way data will flow through all of them:

  • ProjectsListContainer will be our ‚root component’ – at the beginning of its lifecycle it will fetch necessary data from server, and it will pass it down through the props to other components. There we can put some user-friendly message let him know that data will be there soon.
  • Based on obtained data we can decide if it’s a good idea to display ProjectsList component (data available) or some kind of EmptyListWarning component (no data for you).
  • ProjectList component will contain two children – ProjectListSummary (overview of the projects) and list of components called ProjectListItem to display actual projects as a list.

Let’s start by creating first component, which will be responsible for fetching data and keeping the state of our list – create new file following this path: /Frontend/Projects/Components/ProjectsListContainer/ProjectsListContainer.tsx. Let’s take a look at it now:

There are two main properties of every React component – props (data received from the outside) and state (internal state). Component above will be our root component, so there will be no props passed into it. However, it will contain a state for our projects and also information if we are still waiting for data.

To make our work with props and state easier we can use TypeScript’s interfaces like ProjectsListContainerState and pass it into React’s Component definition. Based on provided type we’ll be able to use autocomplete feature of IDEs or editors like VS Code to work with statically typed objects.

Now few words about internals of this component. Starting from constructor we can see that it’s the right place to prepare initial state of our component. Projects array is empty, and loadingData flag is set to true. With that setup we can move down to render method and analyze what will rendered output look like. In render we can see that first we check if data is still loading – if yes, we’re going to display Loading data… text. If now, we have to check what is the state of our component – if there are available projects we can render list, if not – we can render No data message.

To see how it works create file /Frontend/Projects/index.tsx, and update it’s content:

ReactDOM will render our component in element with id react-root. We have to create something like that in /Views/Projects/Index.cshtml:

Also we have to add this index.tsx file to webpack config and use it as entry for projects bundle:

There’s also one additional file included – /Frontend/Shared/Styles/helpers.scss where we can put all kinds of css helpers. For now I’ve defined there two classes to remove margins and paddings from elements:

Now Webpack should build your app and create bundle in /wwwroot/dist directory – projects.js and /styles/projects.css. If you can see this files everything works correctly and we can inject them in our Index.cshtml view:

Now go to your application, log in and check if everything works correctly. You should see Loading data… text because we did nothing to change that 😉 You can try for example to change initial state of component and set loadingData flag to false, then you can add something to projects array – you’ll see how state changes affect the output accordingly.

If all of that works we can introduce small improvements. First, let’s define model for our projects. Let’s do this in /Frontend/Projects/Models/Project.ts:

We can use this model in our component – let’s update state of it in ProjectsListContainer.tsx:

Looks good – our container have to store all available projects. Now let’s try to fetch some data from our database!

12. Fetching data

There are many ways and patterns for fetching data from remote data sources. In our example we’ll use native API of modern browsers called fetch. 

The Fetch standard defines requests, responses, and the process that binds them: fetching.fetch spec

Fetch is a modern way of doing various kinds of network requests. Fetch is based on Promises, which also makes working with asynchronous code easier. It’s worth to mention that there are still some browsers which don’t support it, so in real-life applications we would need to provide some kind of polyfills (i.e. via npm package called whatwg-fetch). If you are working with modern browsers like Firefox, Chrome, Edge or the newest Safari you shouldn’t have any problems with it:

To use fetch we’re going to use something new – lifecycle callback of React components called componentDidMount. In React docs it’s defined as callback which…

„…is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.”

So in componentDidMount we are making a call to the server for list of the projects, and after receiving it we are updating the state by passing projects array to it and setting loadingData flag as false. Every time we invoke method setState React will re-render our component based on new state.

You can see that there’s still one missing part here – endpoint for projects. In our app there’s nothing under /Api/Projects url and now we need to change it. To provide endpoints for front-end we’ll create other type of controllers – WebAPI Controllers. In /Controllers/API create file called ProjectsController.cs and update it:

Ok, there is one more thing to do right now. You can go back to place where we defined Project.ts model. As you see all properties names are capitalised. This is a problem, because by default when we parsing data to JSON – Json(_projectsService.GetAll()); – output json will contain all properties names lowercase. We want to change this default behaviour and keep our models on backend and frontend consistent. To do this go to Startup.cs and in ConfigureServices method change:

to

DefaultContractResolver is from Newtonsoft library. Thanks to that, now output json will contain all properties names capitalised, so we ensure compatibility between backend and frontend models.

With prepared controller and updated component you can run your app again and check if everything works correctly. At the beginning you should see Loading data… text but then immediately text No data should appear. That’s true – we don’t have any data to display now, so we can focus on polishing this sad experience.

13. No data scenario

‚No data scenario’ is very important case to handle. Many programmers forget about the fact that their application should also looks and works correctly without any data in the database. We can’t just stay with empty screen if user don’t have any projects – that would be ridiculous.

We can create component for this case in /Frontend/Projects/Components/EmptyListWarning/EmptyListWarning.tsx:

It’s just a basic stateless component – we can use it to make information about the lack of data better. You can see that here we are using two new parts, first – styles for it (EmptyListWarning.scss file):

with included mixing from another helper file, fonts.scss:

And there’s one new component used – custom button.

There’s also first shared component used – our Button, which has been created by me before in /Frontend/Shared/Components/Button.tsx:

There’s no magic behind it – in props the most important thing is this link property. Based on that we can decide if we should render our Button as a <a > element or maybe <button>. It depends if our button should work as hyperlink or not. If it should work as a link, we need to provide href – that’s also in props interface. One last this is used only for styling and it’s an array of custom class names which should be applied to that button.

To make sure that we can provide anything to that Button we can use {this.props.children} property inside render method.

With all of this we can update our ProjectsListContainer’s render method and use EmptyListWarning component if hasProjects is set to false:

Don’t forget to import it at the top of this file: import EmptyListWarning from ‚../EmptyListWarning/EmptyListWarning’;

You know what to do – run your app and see how it looks.

For me it looks reeaally nice 😉

14. Add your first project

As you can see, there’s a button which can be used to go to ‚Create project’ page. Let’s add this functionality now.

In the definition of EmptyListWarning component there’s a path to page which should be created now – /Projects/Add. To be able to go there we need to update our ProjectsController (non-api) and create method Add there (for serving view, and at the same time for obtaining data from a form):

First method will help us handling GET requests, second one is for POST (data from form). Since first one is pretty simple, we can focus for a moment on the second one. As you can see, we are using Add method from ProjectsService to add new project – we are mapping our view model to AddProjectDto, and checking what is the result of this operation. If it’s equal to success, we’re redirect users to projects list. If not, our view model is updated with error message and we’re returning the same view with prefilled viewmodel (we don’t want to force re-typing the same values again).

There’s also new class used – ProjectCreateViewModel. It can be created in /ViewModels directory, at the same level as Controllers, Views, etc.:

View for that page looks really simple:

Two inputs for name and description, if statement to display error message conditionally, and button for sending data to server. At this moment we don’t have any client-side validation here, so React was unnecessary here.

Please use this form to create your first project – you can use MSSQL Management Studio to see if database table Projects has been updated. If everything went good it means that we’re ready to create projects list component.

15. Listing available projects

If you have your first project in the database you can see that on the main page (/Projects) we can’t see EmptyListWarning component – now projects array is not empty, so we have to prepare something for that. It will be available in /Frontend/Projects/Components/ProjectsList/ProjectsList.tsx – that’s the base version of this component:

As you can see it receives list of the projects ‚from the top’ of component tree via props, and for now… it does nothing with it. We can do small styling improvements by creating ProjectsList.scss and updating the font:

Also, we can update ProjectsListContainer‚s render method, and update condition when projects are available:

Container rendering will work in that way – if we’re still waiting for the data, we can display Loading data… message. If data is here, based on the content of state.projects we can display either ProjectsList component, or EmptyListWarning.

To make it work you have to import ProjectsList component, but I’m sure you know that already 😉

Container is basically done, but we still don’t have any templates for list items or any kind of summary. Let’s do this now.

First, let’s focus on ProjectsListItem – create new component by following our convention and update it’s content:

Whole item will work as link (it’s wrapped by <a> tag), and we’ll use it only for displaying overview of our project in nice and simple format. Interface representing our props will have two properties – key, used by React to track items over which we’re iterating, and project itself.

We’re also using moment library for date formatting features – you can install it via npm install moment command and update package.json.

Due to way how moment is bundled, you can see some warning after trying to import it in TypeScript. To solve it you can create file tsconfig.json on the same level as package.json to configure how TypeScript should handle modules:

Unfortunately with tsconfig.json available in the project I’ve found that Visual Studio is trying to do too much for us and it compiles TypeScript files (in parallel to Webpack). For us it’s of course unnecessary, so we can go to SoftwareHouse.Web.csproj and disable this feature by updating first <PropertyGroup> with option to disable compiling:

After this short break for solving configuration issues we can go back to components world and add this file next to .tsx file (ProjectsListItem.scss):

Finally, the last component is needed – ProjectsListSummary:

Let’s use both ProjectsListSummary and ProjectsListItem to build content of ProjectsList:

Our structure is completed! We have summary encapsulated in one component, we have list encapsulated in another. Whenever we want we can do some changes in one component, and another won’t be affected. We can also do some kind of A/B tests in the future and see how users will interact with different layouts of summary or list. That’s the full power of components – reusable, independent parts of the UI.

With available projects it should looks like this:

16. Project – details and deleting functionality

Last part of this episode will be dedicated to details and delete functionalities. They will be implemented based on ASP.NET Core, because we just need one static view for details and basic form for deleting a project.

Let’s start by adding missing controller methods in ProjectsController (non-api):

For details we need to fetch data for given project from database, we need to build a view model and just pass it to our view. For deleting functionality we need POST request handling – there will be id provided, and we can delete given project. At the end, we’d like to redirect user to list view.

Please keep in mind that methods like this would be much more complicated in real-life applications. We should check if users can in fact see details of given project, and if they can delete it. Now we don’t have anything like that because we’d like to show you just basic recipe.

For creating view page for our details we can use smart shortcut – we have created layout for single project in React, so we can just method of famous scientist – John Copy-Paste – to prepare Razor view for details. It looks like this (/Views/Projects/Details.cshtml):

Go to project’s list, click on your favorite project and check if you can see details of it, and then try to delete it:

Be careful, because there’s no warnings prepared! 😉

Summary

Another great and long journey behind us! We did a lot in this episode – our back-end is layered, data is flowing back and forth from database to our front-end, we also did some brand new React components.

In the next episode we are going to make our app more secure and reliable – instead of making new features we’ll show you how to write tests for both back-end and front-end, and how to prepare data validation. It’s super important to know how to start working with i.e. forms validation or tests in your project, because it’s very hard to find real-life application written without any tests or security features. Features might be different in different projects, but these things will still be there.

And the most important thing – give us some feedback if that’s possible – we’re curious if you are satisfied after reading this episode, if everything works correctly for you and if you had any problems during implementing all that stuff with us.

Stay tuned for more and see ya later!

  • Aleš Vojáček

    I had to remove [Authorize] on API ProjectsController. I does not work with [Authorize]. Is there way to call Authorized API using fetch function?

    • Adrian Bystrek

      Hi, we have missing attribute in fetch function. We fixed it in tutorial already. All what you have to do is to specify ‚credentials’ property in fech data. Go to ProjectsListContainer.tsx and in componentDidMount function change fetch(this.paths.fetchAllProjects)… to fetch(this.paths.fetchAllProjects, { credentials: ‚include’ })…

      Without credentials: ‚include’ fetch function doesn’t send cookies and this is why [Authorize] attribute broke everything.

  • Aleš Vojáček

    Another problem I had is that Project (model in Project.ts) does not work for me as long as it had capitalised properties names. (Id, Name, Description, CreationDate) As soon as I changed to (id, name, description, creationDate) it worked ok.

    • Adrian Bystrek

      Thanks, we fixed it also 🙂 We forgot to describe changes in startup.cs which are needed to be done in order to make this work. Go to Startup.cs and in ConfigureServices method change:

      services.AddMvc();

      to

      services.AddMvc().AddJsonOptions(options =>
      {
      options.SerializerSettings.ContractResolver = new DefaultContractResolver();
      });

      As long as we had only services.AddMvc(); there was used embedded json converter, so all properties in output json where lowercase (default behaviour). To make our work easier we want to use capitalised properties names in .ts models. To ensure compatibility between backend and fronend data contracts we use DefaultContractResolver from Newtonsoft library and thanks to that properties in output json are capitalised.