Introduction
Hello! Tomorrow I have a deadline for the project that I am doing for the local Kamchatka food delivery company. And so I have two reasons to write this article, the first is procrastination before the deadline, and the second I did not find any training manual on writing a basket of goods on AngularJS on Habré.
I found
an article on a third-party blog, which partially helped me solve a couple of problems that confronted me. But the design of the article left much to be desired, and for 5 years I had already lost the code in the notebook, without syntax highlighting, so it was necessary to somehow structure and make this useful information more readable.

')
Why was the one-page store format chosen?
Some of you probably already know that there is a problem with the Internet in Kamchatka, since our peninsula is not yet connected to the mainland by optical fiber, and the entire stream goes through a single tower. By the end of 2015, it is planned to complete work on laying optical fiber along the bottom of the Sea of ​​Okhotsk, and perhaps we will finally have a stable and fast Internet.
And accordingly, in order to exclude the loss of customers moving to different categories of dishes due to the unstable channel, a similar model was chosen. That is, a person needs only once to download the site and get all the necessary information and place an order.
Also this format allowed us to avoid storage of goods in the basket in sessions, localStorage, or in the database. Since we know for sure that the person will not leave this page, we store the cart data in the javascript object. Another advantage was the reduction in the time of the order, as there is no need to navigate through the categories, and download new pages. And since the positions of the dishes are not too much, we didn’t even have to do Ajax data loading when clicking on the category, everything was loaded from the database cache.
Database
After the design of a one-page site was ready and laid out, it was time to create a database and category structure for goods. This is probably the fastest and easiest stage, taking into account our needs and focus on the simplicity of the system and user interaction. I already had an admin sketch on the Phalcon PHP Framework, so fixing it to work with two category and products tables was not difficult.
Category table

Products table

Here is the admin site
We get categories and dishes from the database
The standard ORM system of the Phalcon framework was used to work with the database, if you don’t work with it, you can skip this section, it’s for general development, so that you can see where your legs grow, or rather, how and where the dishes on the site come from.
In the main controller IndexController.php in the frontend module, I wrote a function that will generate the data in the necessary way and bring it to our only page.
public function indexAction() {
Perhaps there are more elegant solutions to this problem, but we will not have more than 200 visitors per day, and we will connect caching of queries to the database, and in principle there will not be such a strong load, especially Phalcon - “The fastest PHP framework”. But the question now is not about productivity and optimization, it is still early, the main thing is that it’s time to display the product on the page.
Who to entrust the rendering of goods on the page? AngularJS or Phalcon?
At first I implemented everything on AngularJS, well, it was somehow more graceful and more beautiful, but then I thought about SEO optimization and indexing by search engines, and thought that it was probably better not to risk, and to entrust rendering to our good old favorite PHP.
I will not give here the code that displays the goods on the page, both for reasons of the size of this code and for ethical reasons, all the same it is a product of our customer. Yes, and I think who reads Habr knows how to use the foreach function in php.
Well, okay, I'll show you how I deduced the categories of dishes, and there already, by example, everyone will understand the goods.
<div class="row"> <div class="large-12 columns"> <ul class="tabs small-block-grid-2 medium-block-grid-4 large-block-grid-6" data-tab> <?php foreach ($items as $item): ?> <li> <a href="#<?= $item['url'] ?>"> <img ng-src="/uploads/<?= $item['images'] ?>" alt="<?= $item['name'] ?>"> </a> <p><?= $item['name'] ?></p> </li> <?php endforeach; ?> </ul> </div> </div>
This whole thing looks very, very appetizing.
Add product to cart
Clicking on the category, below opens a tab with dishes from this category. Perhaps one of you can no longer look at these delicious pictures, but to continue the description of the work of a one-page shop, I just need to show you what the product card on the site looks like. See it.
So, now
wipe the drool off the keyboard , and go on, look at how the main functionality of adding to the cart and calculating the final amount of the order works.
Basically, to understand the basics, you only need to see this piece of code, which displays an “Add” button with a field where you can enter the number of pieces of the dish.
<div class="add-cart"> <input type="number" ng-model="num<?=$p['id']?> value="1" min="1" max="50"> <button type="button" ng-click="addCart(<?=$p['id']?>, num<?=$p['id']?>, '<?=$p['title']?>', <?=$p['price']?>)"></button> </div>
As you can see from the source code, the addCart () function adds data to the javascript object, let's see the source code on AngularJS.
$scope.carts = []; $scope.addCart = function(id, num, title, price) { var nums = num || 1; $scope.carts.push({ id : id, num : nums, title : title, price : price }); };
I think there is nothing to explain, everything is very simple and clear, and most importantly - it works! After we add the data to $ scope.carts, we need to output it somewhere. In our case, the designer decided to do this in the floating window on the right, like this.

And accordingly the code.
<div class="cart ng-cloak" ng-cloak ng-show="carts.length > 0"> <div class="cart-items"> <h3 class="text-center"></h3> <div class="row items" ng-repeat="item in carts"> <div class="small-5 columns text-right"> {{item.title}} </div> <div class="small-2 columns"> <input type="number" ng-model="item.num" min="1" max="50"> </div> <div class="small-4 columns"> {{item.price}} . </div> <div class="small-1 columns"> <a href ng-click="removeItem(carts,item)">X</a> </div> </div> </div> <div class="cart-results text-center"> <div class="row"> <div class="small-12 columns"> <select ng-model="delivery" ng-init="delivery = 0"> <option value="0"> </option> <option value="1"> </option> <option value="2">, </option> <option value="3"> (-10%)</option> </select> </div> </div> <h3>{{total() | number : 0}} .</h3> <button class="new_btn" data-reveal-id="order"></button> </div> </div>
By the way, the ng-cloak function simply helps out in the conditions of the Kamchatka Internet, if it is not used, while the person waits for the page to load, it will see an empty basket with scary characters. For those who are not familiar with AngularJS, I’ll point out a few key points.
It is necessary to show the basket, only if there are goods in it.
ng-show="carts.length > 0"
This code displays the final amount of the order, formatting the number, and removing the pennies that can be obtained by calculating a 10% discount, for example, in the case of self-collection.
{{total() | number : 0}}
I often had the task of deleting an element of an associative array, every time I forgot how to do it, and turned to Google, but I hope after this publication I will finally remember, and those who did not know will find out.
$scope.removeItem = function(carts, item) { carts.splice(item, 1); };
And what, someone hoped that there will be more code? You can by the way, even in one line to write. But let's not get rid of us, the main thing is the readability of the code. And probably the last function I want to give in this article is the calculation of the final amount of the order. It was made on the basis of the conditions and methods of delivery.
$scope.total = function() { var total = 0; angular.forEach($scope.carts, function(item) { total += item.num * item.price; }); var delivery = 0; if($scope.delivery == 0) { if(total >= 600) { delivery = 0; } else { delivery = 130; } } if($scope.delivery == 1) { if(total >= 1500) { delivery = 0; } else { delivery = 130; } } if($scope.delivery == 2) { delivery = 300; } if($scope.delivery == 3) { delivery = 10/total*100*(-1); } return total + delivery; };
How to explain where the numbers grow from, I’ll just show you the delivery information block, and you’ll guess where the different numbers in the code came from. Moreover, each of these data will be different, and there will be a different number of areas, so I don’t see any reason to focus on this, especially since the article has already turned out to be quite voluminous.
Afterword
The main task of my post is solved, now there is material on Habré, how to add goods to the basket and calculate the amount of the order for AngularJS, and on the Internet there is a well-designed material about it, which will add a record from a third-party blog, the link to which I cited the beginning of the article. Well, as well, I have already enough procrastinated, so it's time to get down to work again, and finish sending the order to our customer. I hope the article will help the same as me. If you still have questions about the source code, or something is not clear, I will be happy to answer in the comments. Unfortunately, the experience with AngularJS is only half a year, so the more I can, the more I will help. Thank you for your attention.
UpdateThank you all for the comments, I tried to correct the shortcomings and described the work done in the next publication -
“One-page store on Phalcon PHP + AngularJS. Work on the bugs .