CSC/ECE 517 Spring 2015 E1526
E1526. Responsive web design for Expertiza
Strategy
We analyzed the three Expertiza pages that we need to modify for this project, as well as all of their dependencies, and tried to identify all the tasks that we need to complete for this project. It became clear to us that a large majority of this project will be based on AngularJS, while the small fraction will be related to Bootstrap. Therefore, our strategy was to jumpstart this project by having all 4 team members work together on adding AngularJS to the Login page. Once the Login page was completed, we split in two groups. One group worked on implementing AngularJS on Manage-course page, while the other group worked on introducing AngularJS to Manage-User page. The reasoning behind this strategy was that all of us working together on the Login page first allowed us to obtain some basic understanding of AngularJS and feel more comfortable using this impressively useful and powerful framework. We completed AngularJS constituents first by connecting some of the Rails controllers to AngularJS controllers and then, as a final step, we add Bootstrap. Our decision was based on the fact that Bootstrap does not require much work to be completed and it can be considered to be a pretty straight-forward element of the project.
Integration
This section represent the cornerstone of this project. It consciously explains how Angular and Rails can be integrated into a powerful system capable of providing solid control over both the backend and frontend development of your application.
Steps to interconnect Rails and AngularJS
These guidelines assume that you have a Rails project in place and that you are intending to add AngularJS to it now. Please follow the steps bellow:
1. In the Gemfile, ADD gem ‘bower-rails’ AND gem ‘angular-rails-templates’, then REMOVE ‘gem turbolinks'
2. run
bundle install
3. Create a Bowerfile. It is used by AngularJS and it can be thought of as the Gemfile that Rails uses.
4. In the Bowerfile, ADD asset ‘angular’ , asset ‘bootstrap-sass-official’, asset ‘angular-route’ AND asset ‘angular-boostrap’(for ui.bootstrap injection)
5. run
rake bower:install
. Bower installs dependencies in vender/assets/bower_components.
6. ADD the following content to the config/application.rb
config.assets.paths << Rails.root.join("vendor","assets","bower_components") config.assets.paths << Rails.root.join("vendor","assets","bower_components","bootstrap-sass-official","assets","fonts") config.assets.precompile << %r(.*.(?:eot|svg|ttf|woff|woff2)$)
7. REMOVE //= require turbolinks AND ADD
//= require angular/angular //= require angular-route/angular-route //= require angular-rails-templates //= require angular-bootstrap
to assets/javascripts/application.js
8. Rename assets/stylesheets/application.css to assets/stylesheets/application.css.scss.
9. ADD
@import "bootstrap-sass-official/assets/stylesheets/bootstrap-sprockets"; and @import "bootstrap-sass-official/assets/stylesheets/bootstrap";
to application.css.scss
Since we are did not get to build a new website using AngularJS from scratch, it was a good idea to retain the routing system of Rails rather than implement it in AngularJS.
So, we added a new tag div ng-app='MPApp'
into the app/views/layout/application.html.erb to include all pages into the scope of our AngularJS app, named ‘MPApp’.
Then, for the home page app/views/content_pages/view.html.erb, we added a tag to make this page controlled by ‘testCtrl’, which was defined in app.coffee.
AngularJS's point of view
1. Retrieving data from database Because it is not appropriate to let AngularJS to directly fetch data from the MySQL database, the only way we could load data was through a Rails controller. For example, it is possible to do a http get/post request(GET /users/fetch_10_users) from AngularJS controller using $http injection or jQuery AJAX, which calls a new method ‘def fetch_10_users’ in the users_controller, which execute some database query via accessing methods in the user model, and returns json formatted data to the AngularJS controller. After getting the returning data, the AngularJS controller can present the data to the screen.
2. Build custom directives Instead of using the original Rails page, we decided to replace some forms and components using AngularJS directives, which enabled us to do some live modifications to the pages without loading.
Rails' point of view
The initial problem was how to provide entries from the database to AngularJS. We decided to create methods in Rails controller which will listen to the incoming AngulaJS and use a select statement to fetch a couple of entries from the database. These entries are then stored in a hash, converted to json and passed to AngularJS controller. AngularJS will then send another query request to Rails for the next set of entries. This strategy greatly reduces the loading time, making the time needed for representing the page kept at the minimum.
The specifics
This section explains the very detailed actions and their results related to the 3 specific pages that were used to integrate AngularJS. Numerous snapshots are provided with the goal to make our approach very clear.
Login procedure:
‘Login’ button clicked → auth_controller#login → auth_controller#after_login(user) → if user.role != ‘student’ → tree_display#drill → tree_display#list
If session[:menu] is defined, that is, when a user is logging in, the page will render the content of variable session[:menu].
<ul class="nav navbar-nav"> <% if session[:menu] %> <%= render :partial => 'menu_items/suckerfish', :locals => { items: session[:menu].get_menu(0), level: 0 } %> <% end %> </ul> <ul class="nav navbar-nav"> <% if session[:menu] %> <%= render :partial => 'menu_items/suckerfish', :locals => { items: session[:menu].get_menu(0), level: 0 } %> <% end %> </ul>
For each menu item in the nav bar, it is a dropdown menu now. For different level of dropdown menu, we apply different CSS style to it.
<% level += 1 %> <% items.each do |item_id| item = session[:menu].get_item(item_id) selected = session[:menu].selected?(item.id) long_name = item.name.split(/\W/).collect{|word| word.capitalize}.join(' ') %> <% if level == 1 %> <li class="dropdown first-level"> <% elsif level == 2 %> <li class="dropdown-submenu"> <% else %> <li class="dropdown"> <% end %> <a href=<%= URI.encode("/menu/#{item.name}") %> <%= if item.children ' class="daddy"' end %>title='<%= long_name %>'><%= item.label.html_safe %></a> <% if item.children -%> <% if level == 1 %> <ul class="dropdown-menu multi-level"> <%= render :partial => 'menu_items/suckerfish', :locals => {:items => item.children, :level => level} %> </ul> <% else %> <ul class="dropdown-menu"> <%= render :partial => 'menu_items/suckerfish', :locals => {:items => item.children, :level => level} %> </ul> <% end %> <% end -%> </li> <% end -%>
Manage-course page
courses
Manage Users page
users