The single page catalog seeks to provide one unified page on which:
Complete details of the single page catalog functionalities are provided in the following link:
https://wiki.exphosted.com/doku.php/single_page_catalog_functionality
1) Create a controller action which renders single page view, once the page is rendered every request after that will is an Ajax call.
2) Need to create a layout named “single_page” which will load Vuejs framework, bootstrap css and other vue compoonents.
3) There can be 2 approaches to fetch data from Learnexa
4) On using existing controllers we need not have to make much code changes, all data fetch code is already available only json response need to be added(recommended option). Just an Ajax call will retrieve all the expected data.
5) On using API call session management should be handled and authentication token should be saved in session to make use of it for each API call. All this session management/token authentication has to be implemented(implementation time will increase).
6) On using either of the above approach, view rendering is achieved using Vuejs framework. Every view part of the page will be developed as a component.
7) All Ajax request from UI is achieved using Vue-resource, the plugin for Vue.js provides services for making web requests and handle responses using a XMLHttpRequest or JSONP.
8) Responsive and design styles are achieved using Bootstrap js
9) Following are the bootstrap classes that can be used to achieve the expected UI
body, html {
height: 100%;
margin: 0;
}
.bg {
/* The image used */
background-image: url("img_girl.jpg");
height: 50%;
/* Center and scale the image nicely */
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
10) The user should be able to switch between the old “Classic” UI(current panel design) and the “Single Page” UI by making a setting change in the “Site Properties” page.
Table name - companies
| Column_name | type | Description |
|---|---|---|
| single_page_catalog_enabled | boolean | default false |
Create a sample page which will make an ajax call to existing controllers, fetch products and dynamically render using Vue component:
1) HTML file looks like:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="bootstrap.min.css">
<script src="vue.js"></script>
<script src="vue-resource.js"></script>
<script src="javascripts/jquery.tools.min.js"></script>
<script src="vue_template.js"></script>
<script>
jQuery(document).ready(function() {
new Vue({
el: '#example',
data: {
items: []
},
mounted: function() {
this.$http.get('/products.json').then(response => {
this.items = response.body;
}, response => { });
}
});
});
</script>
<style>
.thumbnail {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.title {
margin-left: 20px;
text-align: left;
}
</style>
</head>
<body>
<div class="bg-1 text-center">
<img src="test1.png" class="img-responsive" style="display:inline" alt="Bird" width="100%" height="100">
<h3 class="title">Available Courses</h3>
</div>
<div id="products">
<div id="example">
<my-component v-for="item in items" :item="item"></my-component>
</div>
</div>
</body>
</html>
2) Sample products controller app/controllers/products_controller.rb (already existing controller)
class ProductsController < BaseController
def index
search_featured_items = Product.search(current_account, current_user, params.merge(:only_featured => true))
search_except_featured_items = Product.search(current_account, current_user, params.merge(:except_featured => true))
@products = search_featured_items.results + search_except_featured_items.results
respond_to do |format|
format.json { render :json => @products.to_json }
end
end
end
3) Vue template for rending item tile structure comes from vue_template.js
Vue.component('my-component', {
template: `<div class="col-md-3 z-depth-2">
<div class="thumbnail">
<a :href="item.product.thumbnail_src_url">
<img :src="item.product.thumbnail_src_url" alt="Lights" style="width:100%" ref="lazyLoadingImage">
<div class="caption">
<p> <strong> {{item.product.title}} </strong> </p>
<p v-html="item.product.description"></p>
</div>
</a>
</div>
</div>`,
props: {
item: Object,
}
})
Create a sample page which will make an API call to fetch products and dynamically render using Vue component:
1) HTML file looks like:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="bootstrap.min.css">
<script src="vue.js"></script>
<script src="vue-resource.js"></script>
<script src="javascripts/jquery.tools.min.js"></script>
<script src="vue_template.js"></script>
<script>
jQuery(document).ready(function() {
new Vue({
el: '#example',
data: {
items: []
},
mounted: function() {
this.$http.get('http://localhost:3000/api/v1/products.json?access_token=<token>&company_id=<id>&user_id=<id>').then(response => {
this.items = response.body;
}, response => { });
}
});
});
</script>
<style>
.thumbnail {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.title {
margin-left: 20px;
text-align: left;
}
</style>
</head>
<body>
<div class="bg-1 text-center">
<img src="test1.png" class="img-responsive" style="display:inline" alt="Bird" width="100%" height="100">
<h3 class="title">Available Courses</h3>
</div>
<div id="products">
<div id="example">
<my-component v-for="item in items" :item="item"></my-component>
</div>
</div>
</body>
</html>
2) Enable API in application by adding following in 'config/application.yml'
api_enabled: true
3) Created sample api controller app/controllers/api/v1/products_controller.rb
class Api::V1::ProductsController < Api::V1::BaseController
before_filter :verify_access_token
def index
current_account = Company.find_by_id(params[:company_id])
current_user = User.find_by_id(params[:user_id])
search_featured_items = Product.search(current_account, current_user, params.merge(:only_featured => true))
search_except_featured_items = Product.search(current_account, current_user, params.merge(:except_featured => true))
@products = search_featured_items.results + search_except_featured_items.results
end
end
4) API render file is index.rabl, show.rabl
index.rabl: collection @products extends "api/v1/products/show" show.rabl: object @product attributes :id, :title, :description, :item_type, :item_id, :thumbnail_url, :description child :categories => :categories do attributes :id, :name end child :tags => :tags do attributes :id, :name end
5) Vue template for rending item tile structure comes from vue_template.js
Vue.component('my-component', {
template: `<div class="col-md-3 z-depth-2">
<div class="thumbnail">
<a :href="item.product.thumbnail_src_url">
<img :src="item.product.thumbnail_src_url" alt="Lights" style="width:100%" ref="lazyLoadingImage">
<div class="caption">
<p> <strong> {{item.product.title}} </strong> </p>
<p v-html="item.product.description"></p>
</div>
</a>
</div>
</div>`,
props: {
item: Object,
}
})
6) Screen shot of the sample page:
1) Play progress button code using progressbar.js plugin:
<script src="progressbar.js"></script>
<div id="container">
<img src="play.png">
<span> 30% </span>
</div>
<script>
var bar = new ProgressBar.Circle(container, {
easing: 'easeInOut',
duration: 400,
color: '#ffffff',
trailColor: '#f2958e',
strokeWidth: 10,
trailWidth: 10,
svgStyle: null
});
bar.animate(0.3);
</script>
<style>
#container img {
position: absolute;
left: 29px;
top: 21px;
}
#container {
margin: 20px;
width: 68px;
height: 68px;
}
#container span {
position: absolute;
top: 67px;
font-size: 12px;
color: #fff;
left: 52px;
}
</style>