Our E-commerce Web App.
Previously, we looked into navigating from one view to another with router which requires an anchor link and us passing the routerLink attribute to the shell but this approach requires us to keep creating component for each view, but what if there is a better approach to write codes and navigate between views without having to create a new component for each view.
Today, we are going to look into that with an e-commerce like web app while interacting between a product and its checkout.
Now, we have few product listed within our app but does that mean we have to create a new view for each product checkout? That could be really stressful and it makes it hard for us to manage our code base, which is why navigating imperatively seems quite the gold.
With imperative navigation, we only need to create one view for the product checkout page, and bind it to the product checkout button telling router to navigate to this page and also supply the data of the product and we are actually going to do this right now step by step.
If you have no idea about what router is and how to use it please refer back to my previous tutorial.
Before we get started ensure you know how to do the following:
- Create and define route paths
- Create a service that supplies data asynchronously to our app
And that’s pretty it. At the end of this tutorial, your app should function like the above image despite the absence of quality UI especially in the checkout page.
Alright, I already prepared the files we need to get started alongside the UI, products data and I also created a service, so just clone this from my repository, run pub get and webdev serve.
Guess we are all ready to navigate imperatively.
Before diving into imperative navigation, let’s run the app to get informed about the changes and structure. For now, when the user clicks on proceed to checkout the details of the clicked product displays under the product list section just like this:
Now, what we want to do is that when the user click proceed to checkout button we want it to navigate to an entire new view and for it to also show the data of the particular product clicked.
The first thing we want to do is create a component for the new view which we are going to call checkout view. So let’s create a checkout.dart file within our lib folder and add the following code to it:
Here we go, we’ve got our selector, template, directives and also exporting RoutePaths and Routes. If you noticed we didn’t import RoutePaths and Routes package because we already did that within the app component thereby making it available for all other components.
Next we define the route path for checkout which is going to be the tricky part.
So, what we want right now is for our URL to indicate the product name when the user clicks on the product like this:
http://localhost:8080/#shopping/2%20by%208%20bedroom
This URL indicates that the user clicked on proceed to checkout for a product with the name “2 by 8 bedroom” this can only be done with the use of Route Parameters. The syntax for route parameters is as follows:
The parameters only return a String but we can also parse it to return an integer, based on the API documentation of route parameters there isn’t any clear definition of how to use it, so am also going to do that.
Firstly, we want the URL to indicate shopping/”productname” but we already created a route path for shopping, so we’ll be referring to the path of shopping using shopping.path.
We also want to pass in the product name, and to do that we first create a variable name of idParam and give it a value of “products”.
We used the value products within the shopping page to list all items in our List, to be on the safer side I recommend you maintain one variable name within your program things might get bigger and you can’t keep track of all those variable names.
Alright, let’s go ahead and create a const variable name idParam with value products just above the class declaration of RoutePaths.
Next, we define the route path for checkout passing idParam as a parameter
Like I mentioned earlier, we want the URL address to indicate #shopping and the proper way to go about that is referring to the path of shopping followed by a forward slash with idParam as the route parameters.
Doesn’t seem like we are done, the idParam creates a slot in the path for a route parameter which the router will insert the product name of the clicked product into that slot of ours and if we tell router to navigate to the checkout component and display the checkout details for the product with product name 2 by 2 bedroom, price of 300$ and the description, the URL should look just like this:
http://localhost:8080/#shopping/2%20by%208%20bedroom
If the user also enters the above URL into the address bar directly it navigates to the checkout page of that product like I explained earlier.
The RouterParameters property has a map of URL parameters of type String such that if the matched RouteDefinition path has a path ‘shopping/:productName’ and the URL is ‘shopping/2by2bedroom’, the router parameters would equal {productName: ‘2by2bedroom’}.
Up next is getting the name of the product clicked and passing it into the Route parameters. Within our route_path.dart file we create a method getName() that returns a String value and receives a parameter of router parameters.
The getProducts() method returns the value for the given [product] or null if [product] is not in the map. Some maps allow keys to have null as a value so we also condition it to return null if the product equals null and if false it returns the products name which is a String parameter.
If you’re trying to return an integer and we are quite aware that route parameters receives a String we use tryParse(source) method to parse the item into an integer just like the below code:
Moving forward, we define the checkout route path in the same conventional way we define route paths for other view:
Now, we are all set up to implement all the checkout routes to be navigated imperatively.
Let’s switch our dart file to the components that hold the list of all products which is the shopping.dart file. We won’t be adding any routerLink to proceed to checkout anchor link tag instead we would be navigating imperatively with the use of router. But first we initialize Router within our ShoppingComponent class
After initializing the router, we create a private method since it’ll be initialized and it holds the checkout view URL and also passing the required parameters to the checkout path:
_checkoutUrl takes a String parameter of products because our product name is a String and like I mentioned earlier — maintain a variable name when navigating imperatively.
The chechoutUrl uses the arrow syntax to pass in the URL of the checkout route path alongside the necessary parameters.
So, we want to tell router to navigate automatically when something happens and in this case when the user clicks proceed to checkout button.
The Future is used to delay computation when calling router.naviagate and the NavigationResult holds the result of calling router.navigate which attempts to navigate to the defined path and in this case is the checkout path.
We defined a String parameter of the products that is the product name in the _checkoutUrl() method.
Earlier on, we initialized onSelect() method that responds to a click event and returns the data of the clicked product storing it in the variable selectedProduct of type class Products.
But we don’t want to display the selected product below the products list anymore, we just want to display the selected product in another view therefore we pass in the products parameter to the _checkoutProduct(), which gives us the leverage to refer to that particular product name.
That’s it; we are done interacting with the shopping component, the rest lies within the checkout view component but let’s head over to the shopping component HTML file and cut out the selected product section.
We are going to paste this selectedProduct section into the template property of our checkout component.
Quickly, let’s add ShoppingComponent into our directives and import the ShoppingComponent dart file.
Within our CheckoutComponent class, we initialize Product service and Location which interacts with the browser URL and will also be used to navigate back to the shopping page; we also declare a variable name selectedProduct of type class Products which is the same variable within the *ngIf attribute within the template.
We implement onActivate lifecycle hook on the CheckoutComponent class.
onActivate is a lifecycle interface which notify when a component is activated by a route. Component classes should implement this if they will be navigated to as part of a route definition and would like to be notified if they were activated due to routing.
After implementing onActivate we create an override annotation to override the onActivate method
We replace RouterState previous with an underscore ( _ ), which is called after the component is inserted by a router outlet and will occur after initial change detection.
Next we add this block of code within the onActivate method:
The getProduct(current.parameters) returns the product current value from the route path and if the product is not equal to null, we get the products assigning it to the variable name selectedProduct.
We haven’t initialized the get() method which will only be done within the product service class.
The await indicates an asynchronous operation, so we add async to the onActivate() method, which fetches the product data asynchronously.
Lastly, we head to the product service class and add a get method that fetches the data asynchronously from the list of products while iterating the list to fetch the selected product name where it first occurs in the list.
Yeah, that settles it and our app works fine so why not use navigating imperatively in your…..
One more thing, let’s look at how Location can be useful.
Within our checkout page am going to add a “back to shopping” button that navigates the user back to the previous page using location.back
Add this piece of HTML code to your template of your checkout page and add this piece of code within the CheckoutComponent class implementing the goBack() click event
The location class can navigate back and forward in a platform (browser) history.
Yeah, back to what I was saying.
That settles it and our app works fine so why not use navigating imperatively in your next e-commerce web app or any app of your choice like a blog also.