Git Code Commit (all commits between last post and this commit (link))
Today, we added the ability to add a new bucket list item and then display it.
First, I broke up the controller that I had been using till now for everything. If you remember, I was stuffing everything into the home controller. This was never a good design and I felt I now had enough separate functions to warrant the change. So, I updated ‘TgimbaNetCoreWebShared’ with the following:
- Moved IWebClient.cs and WebClient.cs to ‘TgimbaNetCoreWebShared’ so I can return models. This is a little controversial since to date I have kept all these things in the Shared project. My reasoning is that the webclient is only relevant to the MVC project (actually, shared MVC project). So, that is why I am doing this.
- Created wrapper utilities class on the shared web project to convert bucket list items between models (used by the website/clients) and the string (used by the current API). When the API is re-written as a separate service, I will do away with the string and use the bucket list model.
For tests, I created these classes to mirror the files being tested:
- LoginController.cs – This was pretty fast. Once I had created the controller and test class, I just moved the login related code from the home controller and test classes over.
- RegistrationController.cs – Exactly the same LoginController.cs
- BucketListItemController.cs – This was a little more complex (but not much). After creating the controller and its test class counterpart, I added these two methods – AddBucketListItem(args) and GetBucketListItems(args). For tests, I am just doing a good and bad parameters test for each. If this was a commercial site, I would have added a lot more tests (probably bad null values for each parameter).
- UtilitiesTest.cs and WebClientTest.cs.
I am having some issues with Moq on my mocks. More specifically, when I use a primitive and/or string, the setup(x => x.Method(args)).Returns(expected return); works. But, when I use a reference, it doesn’t. When I check the object in the watch window and compare it with the actual parameter being used in the test, they are identical. It is not super important at this point since this is a fun/learning project…but its troubling. I have not researched it online, but I suspect I am either out of date on my Moq version and/or there is a known bug. For now, I am using the It.IsAny<SharedBucketListModel>() in the method I care about.
- Added a LoadMainPage() method in HtmlVanillaJsIndex.cshtml. I tried adding it with the dynamically loaded HTML, but the method would never fire. I guess it has to be in the original header of the .cshtml file.
- In Display.js, I set a property to hold the loaded bucket list items. I then call LoadMainPage() from Display.SetView(args) and the data is accessed inside LoadMainPage(). Not perfect, but it keeps the data viewing portion of the code inside the view 🙂
- Added ServerCalls.GetBucketListItems() to retrieve any bucket lists for the logged in user and updated LoadMainPage() to take the JSON object list and create an HTML display table.
- Added the Add.html view which will allow the user to add a new bucket list item. An interesting note – because I am loading views dynamically and the view is loaded asynchronously, I need to set the current date on the Add form. I am using the same method to set that date as I am the titles. This is set in Display.js.
I am starting to wonder if I should even call this client JQuery. It is only JQuery on how it access data within HTML and communicates with the server. The HTML part has been moved to Vanilla JS for ease and because the last TGIMBA version was all JQuery. I made minor updates to the JQuery server calls and how the data is displayed.
Angular 6 Type Script Client
I made the following modifications:
- Since all of the clients share the same controllers, I had to update the Angular 6 client to reference the new login, registration and bucket list controllers.
- I added add.component.ts, add.component.html and add.component.css.
- I added a GetBucketListItems Hyper Text Transfer Protocol (HTTP) GET call. Binding the bucket list item data to the HTML table was not as straight forward as the Vanilla and JQuery code was. There, I just dynamically created an HTML table and displayed it. Here, we need to use an ngmodel. More specifically, we bind the <div to a data variable in main.component.ts (more details in reference #6).
- Something to be careful with is capitalization. I initially got a “Can’t bind to ‘ngforOf’ since it isn’t a known property of ‘div'” error because I used *ngfor vs *ngFor (reference #7). Additionally, I added a number column to the JSON data array dynamically for display purposes.
- Another error occurred while running the Selenium tests. Specifically, a contextswitchdeadlock error. This can be turned off. For details, see reference #8.
React JS Redux Client
Out of all the clients and on this particular set of tasks, the React JS Redux client was the toughest. First and just like the other clients, I had to update the controllers. The login and registration were already present, so their update was a URL change. The new bucket list controller hadn’t existed, so it was all new. Adding the controller was easy, but the client was not.
To date, this is the first time I have taken data from the server and rendered it. Til now, all I had done was pull data from a form (i.e. Login, Registration, etc.) and acted on it. Displaying data (especially with React JS) is a little be different that I have done before. To start, I consolidated my first re-usable React component Button.js into a user interface directory. I then added Table.js. I knew I needed a table, but I didn’t know all of the details of how to create it.
To develop the table, I read a lot. One suggestion was to develop the component statically. So, I created table with static data in the component and got it to render. I then took the static data out Table.js and placed it in Main.js and referenced Table.js as a control. Ultimately, it ended up looking like this. I could have called it BucketListItemTable.js perhaps, but its a bind-able table that will display any list the server returns.
Once this worked, I had to determine how to load data from the server and this was a lot trickier. I started by calling the reducer in the component’s constructor which I thought made since since constructors (in my experience) initialize and build an object. Long story short, I tried a lot of flavors and none of the constructor based ones ended up working. What finally worked came from two sources. Reference #11 showed me to create a waiting check. More simply, testing for a data result and showing a loading icon until the data shows.
Apparently, render() is called whenever the ‘state’ of the component is updated. The trick (it seems) is to figure out how to update the state. This other clue came from studying the default React JS Redux. More importantly, using the componentDidMount() and componentWillReceiveProps(nextProps) in conjunction with the reducer did the trick. These two methods (I believe) are provided as events by React JS. From my reading and experimentation, componentDidMount() fires once when the component first loads and componentWillReceiveProps(nextProps) fires when ever there is an update in state. component/Main.js ended up looking like this. It may see redundant to make the this.props.load() call twice, but that is what the stock React JS project generated by Visual Studio 2017 did. If it is not needed, I will remove it. Remember, learning as I go 🙂
I also moved the HTTP GET load data call to the actionCreators object. I am not completely sure if this is correct or having server call in the reducer is correct. Additionally, I removed XMLHttpRequest and used what appears to be a native fetch() call which is a lot simpler. If you can do a post with a similar method, I will remove XMLHttpRequest all together and move my other calls into the actionCreators objects.
Next came component/Add.js and store/Add.js. Nothing really special here as I followed the same process used on login and registration. One interesting item though:
- Date Created – For some reason, readonly HTML decorator not working that had worked on the other clients did not work here. So, I achieved this by removing the onchange handler. It works and it is distinctly a React JS thing solution 🙂
Next will be the edit and delete function.