{"id":21585912,"url":"https://github.com/kawthare/addtomycard","last_synced_at":"2026-04-10T12:04:16.262Z","repository":{"id":214318213,"uuid":"128968659","full_name":"KawtharE/AddToMyCard","owner":"KawtharE","description":"Simple application shown the usage of the organizational library BackboneJS (version 1.3.3) for the Front-end side and the Flask micro-framework (version 0.12.2) for handling the Back-end.","archived":false,"fork":false,"pushed_at":"2018-04-11T14:51:28.000Z","size":1248,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-24T15:35:11.263Z","etag":null,"topics":["backbonejs","css3","flask","html5","javascript","jquery","python3"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KawtharE.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-04-10T17:09:28.000Z","updated_at":"2018-04-11T14:51:29.000Z","dependencies_parsed_at":"2023-12-27T11:45:15.550Z","dependency_job_id":null,"html_url":"https://github.com/KawtharE/AddToMyCard","commit_stats":null,"previous_names":["kawthare/addtomycard"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KawtharE%2FAddToMyCard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KawtharE%2FAddToMyCard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KawtharE%2FAddToMyCard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KawtharE%2FAddToMyCard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KawtharE","download_url":"https://codeload.github.com/KawtharE/AddToMyCard/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244189826,"owners_count":20412991,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backbonejs","css3","flask","html5","javascript","jquery","python3"],"created_at":"2024-11-24T15:12:09.632Z","updated_at":"2025-12-31T00:16:28.331Z","avatar_url":"https://github.com/KawtharE.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Add To My Card App\n\nSimple application shown the usage of **the organizational library Backbone** for the Front-end side and **the Flask micro-framework** for handling the Back-end.\n\n![Starting Screen](https://github.com/KawtharE/AddToMyCard/blob/master/assets/addToMyCardApp.gif)\n\nBefore start explaining the process of developing this application, let me mention the fact that business application should be a feature-rich app, in order to be a strong competitor in the market of application. And adding a shopping feature around in the app with a great user experience and a secure back-end handling will take the app to another level.\n\nIn this demo i am focusing on the **User Experience** part, using **BackboneJS** and **Flask**.\n\n**Note:** To run this application on your machine make sure that you have **Python 3.5** and **Flask** installed in your environment.\n\n## Developemnt Process\n\nOur development process is an **iteration process**, which allows us to have a working prototype at each iteration. So to go through this process we need to organize our steps in iterations.\n\n#### Iteration 1: create mock-ups\n\nOur first iteration consist in creating **mock-ups** for every page in the application and design URLs for each one. In our case, we only have two pages: the starting page where we will be displaying all of the items (**index.html**) and an after submitting page (**postSubmit.html**) where we will be displaying the items chosen by the user.\n\n**Page:** index.html -\u003e **URL:** '/'\n\n**Page:** postSubmit.html -\u003e **URL:** '/submitted'\n\n\n#### Iteration 2: configure routing\n\nWe will be configuring the routing in the **Flask application**, so first of all we need to install it:\n\n      $ pip3 install flask\n      \n**Note:** For Mac or Ubuntu users python 3.5 is already installed.\n\nAfter installing Flask, lets get start configuring the routing in the back-end:\n\nCreate a new file inside the root directory of the project and call it for example, as i am calling it, *submit.py*:\n\n\n\tfrom flask import Flask, render_template, request, url_for, redirect\n\tapp = Flask(__name__)\n\n\t@app.route('/', methods=['GET', 'POST'])\n\tdef start():\n\t\treturn render_template('index.html')\n\n\t@app.route('/submitted', methods=['GET', 'POST'])\n\tdef submitted():\n\t\tif request.method == 'POST':\n\t\t\titems = request.form.getlist('itemName')\n\t\t\tif items == []:\n\t\t\t\treturn render_template('index.html')\n\t\t\telse:\n\t\t\t\treturn render_template('postSubmit.html', items=items)\n\t\telse:\n\t\t\treturn redirect(url_for('start'))\n\n\tif __name__=='__main__':\n\t\tapp.debug = True\n\t\tapp.run(host='0.0.0.0', port=5000)\n\n**Line by line:**\n\n0- From the flask framework we are importing:\n\n**the Class Flask** that we will be using in the next line to create a new instance of the app.\n\n**the render_template function** that we will be using to render HTML files and passing python variables to it whenever needed.\n\n**the request function** that we will be using to request the submitted data in the form inside of our templates.\n\n**the url_for function** that we will be using to return URLs of certain files.\n\n**the redirect function** that we will be using to update the URL whenever needed.\n\n\tfrom flask import Flask, render_template, request, url_for, redirect\n\t\n1- The following line of code creates a new instance of the Flask class with the name of the running app. **_name_** is a special variable in python that have the value **_main_** if the app is running by the python interpreter or **_fileName_** if the file is imported in another project.\n\n\tapp = Flask(__name__)\n\n2- Configure the URL for the starting page to be '/' using **@app.route()** function and associate the *start()* function to it, which will render the *index.html* template using the **render_template()** function. By default the *@app.route* response only to **GET request**, so to be able to respose **POST requests** we are adding **methods=['GET', 'POST']** as a second argument.\n\n\t   @app.route('/', methods=['GET', 'POST'])\n\t   def start():\n\t\treturn render_template('index.html')\n   \t\n   \n3- Identically to the previous function we are configuring the URL for the after submitting page to be '/submitted' and adding the methods=['GET', 'POST'] argument to response to both GET and POST responses. Now the *submitted()* function, which is associated to the '/submitted' URL, will check the type of request first of all:\n\nIf the request is a **GET request** the function redirect the user to the starting page --\u003e redirect(url_for('start')).\n\nIf the request is a **POST request** we will be asking for the submitted data using **request.form.getlist('valueOfNameAttr')** function, if it returns null, which means that the user did not select any item, we will stay at the same page, otherwhise the application render the *postSubmit* template and pass in the selected item to be displayed. \n   \n\t@app.route('/submitted', methods=['GET', 'POST'])\n\tdef submitted():\n\t\tif request.method == 'POST':\n\t\t\titems = request.form.getlist('itemName')\n\t\t\tif items == []:\n\t\t\t\treturn render_template('index.html')\n\t\t\telse:\n\t\t\t\treturn render_template('postSubmit.html', items=items)\n\t\telse:\n\t\t\treturn redirect(url_for('start'))\n\n4- Finally, run the app.\n\nIf the special variable **_name_** have the value **_main_** that mean that the current file is the main project and in that case we will be running the app *app.run()*, and in order to be able to visualize changes automatically without reloading the page every time we enable the debug mode *app.debug = True*.\n\n\t if __name__=='__main__':\n\n\t   app.debug = True\n\n\t   app.run(host='0.0.0.0', port=5000)\n      \n      \n=\u003e Now that we have all URLs working, we will be adding templates which we will be developing using **Backbone**.\n\n#### Iteration 3: configuring the Front-end part of the app\n\nFlask knows that your HTML files are in the **templates** folder and your CSS and JavaScript files are in the **static** folder. So this is how will be the structure of our project:\n\n      ---static\n            |---css\n                  |---main.css\n            |---js\n                  |---collections\n                  |---models\n                  |---routers\n                  |---views\n                  |---app.js\n      ---templates\n            |---index.html\n            |---postSubmit.hmtl\n      ---submit.py\n\nNow the reason there are *collections, models, routers and views* folders inside the js folder is that we will be using **BackboneJS** for the client-sid part of the application.\n\n**Backone** is one of the amazing organizational library that belong to the **MV * pattern family**. As any other organizational library or framework, backbone is made of these five modules:\n\n1- Views                 \n\n2- Events\n\n3- Models                 \n\n4- Collections\n\n5- Routers\n\n**Backbone**'s only dependcy is the **underscore** library, and adding **jQuery** makes its functionalities a their top.\n\n      $ npm install underscore\n      $ npm install backbone\n      $ npm install jquery@3.3.1\n      \nAnd now we are ready to go!\n\n#### Iteration 4: developing the Front-end part with backbone\n\nThe goal of using the library backbone, is to make our code stable, free of bugs, scalable and well organized so it can be easily maintained and adding new features in the future can be done in no time.\n\nStarting by our HTML templates:\n\n**index.html**\n\n      \u003c!DOCTYPE html\u003e\n      \u003chtml lang=\"en\"\u003e\n      \u003chead\u003e\n            \u003cmeta charset=\"utf-8\"/\u003e\n            \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/\u003e\n            \u003ctitle\u003eAdd To My Card | BackboneJS\u003c/title\u003e\n            \u003clink rel=\"stylesheet\" type=\"text/css\" href=\"{{url_for('static', filename='css/main.css')}}\"/\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n      ...\n\n      \u003cscript src=\"{{url_for('static', filename='node_modules/jquery/dist/jquery.min.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='node_modules/underscore/underscore.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='node_modules/backbone/backbone.js')}}\"\u003e\u003c/script\u003e\n\n      \u003cscript src=\"{{url_for('static', filename='js/models/item-model.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='js/collections/item-collection.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='js/routers/router.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='js/views/list-view.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='js/views/app-view.js')}}\"\u003e\u003c/script\u003e\n      \u003cscript src=\"{{url_for('static', filename='js/app.js')}}\"\u003e\u003c/script\u003e\n      \u003c/body\u003e\n      \u003c/html\u003e\n      \nSince we are inside a **Flask** project referencing CSS and JS files will be different from the usual way, we are using here the function **url_for('static', filename='..')** in order to return the url of our CSS and JS files that are placed inside the **static** folder.\n\nNotice the order in which we import our js files (model, collection, router, view and app.js), it is important to place the *app.js* at the end in order to give time to the model, collection, router and view to get setup before kicking off the app inside the app.js file.\n\nInside the body we will be having a form to be submitted, the form composed of a bunch of checkbox items, a submit button and a total price of selected items that will be calculating while selecting or deselecting items:\n\n\t\t\u003cform id=\"form\" method=\"POST\" action=\"{{url_for('submitted')}}\"\u003e\n\t\t\t\u003cul id=\"list\"\u003e\u003c/ul\u003e\n\t\t\t\u003cdiv class=\"total\"\u003e\n\t\t\t\t\u003cinput class=\"submitBtn\" id=\"submit-btn\" type=\"submit\" value=\"Add\"\u003e\n\t\t\t\t\u003cspan id=\"total-price\"\u003eTotal: \u003cspan id=\"total\"\u003e$ 0.00\u003c/span\u003e\u003c/span\u003e\n\t\t\t\u003c/div\u003e\t\t\t\t\t\t\n\t\t\u003c/form\u003e\n\nThe attribute **action** in the form element take a value of **{{url_for('submitted')}}**:\n\n- {{..}}, {%..%} : This syntax is called **HTML Escape**. Flask is pre-configured and its templates recogonize this code, **{%..%}** used to execute code, **{{..}}** used to print code. Here we are using *{{url_for('submitted')}}* to print the URL associated to the **submitted** function, that way when the user submit the form the application execute the function **submitted**.\n\nThe list of items are added dynamically to the form once we configure the model and filled the collection, so we will be having a template that will be rendred for each item in the collection:\n\n      \u003cscript type=\"text/template\" id=\"item-template\"\u003e\n            \u003c% _.each(items, function(item){ %\u003e\n                  \u003cli id=\"list-item\"\u003e\n                        \u003cinput class=\"toggle\" id=\"toggle\" type=\"checkbox\" name=\"itemName\" value=\"\u003c%- item.name %\u003e\" data-name=\"\u003c%- item.name %\u003e\"\u003e\u003cspan id=\"name\"\u003e\u003c%- item.name %\u003e \u003c/span\u003e\u003cspan id=\"author\"\u003e\u003c%- item.author %\u003e\u003c/span\u003e\u003cspan id=\"price\"\u003e$ \u003c%- item.price %\u003e\u003c/span\u003e\n                  \u003c/li\u003e\u003cbr\u003e\n            \u003c% }); %\u003e\n      \u003c/script\u003e\n\nThe rest of files in our backbone project: model, collection, router, view and app.js, have the following structure:\n\n      var app = app || {};\n\n      (function(){\n            ...\n      })();\n      \n      \n**var app = app || {}**: test if the app have already been created otherwise it take a value {}.\n\n**IIFE**: Immediate Invoked Function Expression, where the code goes and immediately executed.\n\nThe first component we need to develop is our Model, in our case it is a book Model with the following properties:\n\n\tapp.Item = Backbone.Model.extend({\n\t\tdefault:{\n\t\t\tname: '',\n\t\t\tauthor: '',\n\t\t\tprice: 0,\n\t\t\tchecked: false\n\t\t},\n\t\ttoggle: function(){\n\t\t\tthis.set('checked', !this.get('checked'));\n\t\t}\n\t});\n\t\nThe *toggle* function will update the value of *checked* property every time the item is selected or deselected.\n\nNext, from the previous Model we will be creating a Collection of Models that will be rendred next in a list:\n\n\tvar Items = Backbone.Collection.extend({\n\t\tmodel: app.Item,\n\t\tcheckedItems: function(){\n\t\t\treturn this.where({checked: true});\n\t\t}\n\t});\n\tapp.items = new Items([\n\t\tnew app.Item({id:0, name: 'The Self-Taught Programmer', author:'Cory Althoff', price: 14.98, checked: false}),\n\t\tnew app.Item({id:1, name: 'Python for R Users: A Data Science Approach', author:'Ajay Ohri', price: 42.53, checked: false}),\n\t\tnew app.Item({id:2, name: 'Web Design with HTML, CSS, JavaScript and jQuery Set', author:'Jon Duckett', price: 29.55, checked: false}),\n\t\tnew app.Item({id:3, name: 'HTML and CSS: Design and Build Websites ', author:'Jon Duckett', price: 13.99, checked: false}),\n\t\tnew app.Item({id:4, name: 'Deep Learning with Python ', author:'Francois Chollet', price: 42.78, checked: false})\n\t\t]);\n            \nThe *checkedItems* returns all selected items and we will be using this function to calculate the Total price.            \n            \nNow that we have all data we need, we will start render them in the DOM by making a view for each item and then render all of them to the DOM:\n\n\tapp.ListView = Backbone.View.extend({\n\t\tel: '#list',\n\t\ttemplate: _.template($('#item-template').html()),\n\t\tevents: {\n\t\t\t'click .toggle': 'toggle'\n\t\t},\n\t\tinitialize: function(){\n\t\t\tthis.render();\n\t\t},\n\t\trender: function(){\n\t\t\tthis.$el.html(this.template({items: app.items.toJSON()}));\n\t\t\treturn this;\n\t\t},\n\t\ttoggle: function(ev){\n\t\t\tvar listName = $(ev.currentTarget).attr('data-name');\n\t\t\tapp.model = app.items.where({name: listName});\n\t\t\tapp.model[0].toggle();\n\t\t\tvar total = 0;\n\t\t\t_.each(app.items.checkedItems(), function(item){\n\t\t\t\ttotal += item.get('price');\n\t\t\t});\n\t\t\t$('#total').html('$ '+total.toFixed(2));\n\t\t}\n\t});\n\t\nIn this view we are adding a **click event** that calls the *toggle* fuction every time the item is selected. The *toggle* function is responsible for updating the *checked* value of the corresponding item and updating the total price.\n\nFor the **render** function we are passing the whole collection of items in JSON format *app.items.toJSON()* to be displayed using the *item-template* in the *ul#list* element.\n    \nUntil this step we don't have a *ListView* view in the DOM since we are not instantiating the view yet and that what we will be doing in the **app-view.js**. Now the **AppView** view grab all necessary elements from the DOM: form, list and total and render them after instantiating the **app.ListView** view, which is responsible about displaying the items and calculating the total price.\n\nAny changes affect the collection of items *app.items* will make the view re-render since we are configuring a **change** event on it: *this.listenTo(app.items.toJSON(), 'change', this.render)*\n\n     var AppView = Backbone.View.extend({\n            el: '#form',\n\n            initialize: function(){\n                  this.listenTo(app.items.toJSON(), 'change', this.render);\n                  app.listView = new app.ListView();\n                  this.render();\n            },\n            render: function(){\n                  this.form = $('#form');\n                  this.list = $('#list');\n                  this.total = $('#total');\n                  this.list.show();\n                  this.total.show();\n                  this.form.show();\n\n                  return this;\n            }\n      });\n      \n To kick off the application we instantiate the **AppView** variable inside the **app.js** file:\n \n\t var app = app || {};\n\n\t $(function(){\n\t    'use strict';\n\n\t    var appView = new AppView();\n\t });\n\nFinally, our **postSubmit.html** file will display all selected items:\n\n\t\u003cul\u003e\n\t\t{% for item in items %}\n\t\t\t\u003cli\u003e{{item}}\u003c/li\u003e\n\t\t{% endfor %}\n\t\u003c/ul\u003e\n\t\nNotice, with **HTML Escaping** we have access to python variables and functions. **items** is an array that we have passed from flask when calling **render_template** function in the *submitted()* function:\n\n\trender_template('postSubmit.html', items=items)\n\t\n#### Iteration 5: styling\n\nFirst, we will start by passing **flash** message on the starting page to alert the users whenever they try to submit the form without selecting any item. First we will import *flash* from *flask*, configuring a **secret_key** before running the app then passing the message we want to display in the function **flash('message to display')** and configuring the client-side where we will be displaying the message.\n\nSo in *submit.py* we will add the following changes:\n\n\tfrom flask import ..., flash\n\t...\n\t@app.route('/submitted', methods=['GET', 'POST'])\n\tdef submitted():\n\t\t\t...\n\t\t\tif items == []:\n\t\t\t\tflash('You need to choose one or more items to add it to your card!')\n\t\t\t\t...\n\t\t\t...\n\n\n\tif __name__=='__main__':\n\t\tapp.secret_key = 'secret_key'\n\t\t...\n\t\t\nAnd in *index.html*, where we will be displaying our message, we will be calling the **get_flashed_messages()** function which will return all messages we have setup in python file:\n\n\t{%with messages = get_flashed_messages()%}\n\t\t{%if messages%}\n\t\t\t\u003cul\u003e\n\t\t\t{%for message in messages%}\n\t\t\t\t\u003cli\u003e{{message}}\u003c/li\u003e\n\t\t\t{%endfor%}\n\t\t\t\u003c/ul\u003e\n\t\t{%endif%}\n\t{%endwith%}\t\n\n**Note:** For production we should make the *secret_key* more secure than just a passing a simple string!\n\nNow the rest of styling of the application and these flash messages is pure CSS that goes inside the **main.css** file, there is nothing special about that!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkawthare%2Faddtomycard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkawthare%2Faddtomycard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkawthare%2Faddtomycard/lists"}