Rendering a Star Rating System using angularjs - javascript

My app is in this Fiddle
I need to render a star rating system dynamically from a http service, where the current stars and maximum stars can vary with each case.
Is it a good idea to create arrays from $scope.current and
$scope.max - $scope.current and pass them and run ng-repeat over them, or there is a more optimised solution than this.
Iteration ng-repeat only X times in AngularJs

Star Rating can be done either statically (read-only) or dynamically
If you want just simply to display Rating as star then try the below one
Static Star Rating
Working Example
html
<body ng-app="starApp">
<div ng-controller="StarCtrl"> <span ng-repeat="rating in ratings">{{rating.current}} out of
{{rating.max}}
<div star-rating rating-value="rating.current" max="rating.max" ></div>
</span>
</div>
</body>
script
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.ratings = [{
current: 5,
max: 10
}, {
current: 3,
max: 5
}];
}]);
starApp.directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '='
},
link: function (scope, elem, attrs) {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
}
}
});
If you want to do Star Rating dynamically try this out
Dynamic Star Rating
Working Demo
Html
<body ng-app="starApp">
<div ng-controller="StarCtrl"> <span ng-repeat="rating in ratings">{{rating.current}} out of
{{rating.max}}
<div star-rating rating-value="rating.current" max="rating.max" on-rating-selected="getSelectedRating(rating)"></div>
</span>
</div>
</body>
script
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.rating = 0;
$scope.ratings = [{
current: 5,
max: 10
}, {
current: 3,
max: 5
}];
$scope.getSelectedRating = function (rating) {
console.log(rating);
}
}]);
starApp.directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star" ng-click="toggle($index)">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '=',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
var updateStars = function () {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
};
scope.toggle = function (index) {
scope.ratingValue = index + 1;
scope.onRatingSelected({
rating: index + 1
});
};
scope.$watch('ratingValue', function (oldVal, newVal) {
if (newVal) {
updateStars();
}
});
}
}
});
There is a wonderful tutorial here for more explanation about Angular Star Rating

You can even try angular-ui. Here is the link.
Just need to add this tag.
<rating ng-model="rate" max="max"
readonly="isReadonly"
on-hover="hoveringOver(value)"
on-leave="overStar = null">

//Controller
var starApp = angular.module('starApp', []);
starApp.controller('StarCtrl', ['$scope', function ($scope) {
$scope.maxRating = 10;
$scope.ratedBy = 0;
$scope.rateBy = function (star) {
$scope.ratedBy = star;
}
}]);
.rating {
color: #a9a9a9;
margin: 0;
padding: 0;
}
ul.rating {
display: inline-block;
}
.rating li {
list-style-type: none;
display: inline-block;
padding: 1px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.rating .filled {
color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="starApp">
<div ng-controller="StarCtrl">
<ul class="rating">
<li ng-repeat="n in [].constructor(maxRating) track by $index">
<span ng-click="rateBy($index+1)" ng-show="ratedBy > $index" class="filled">★</span>
<span ng-click="rateBy($index+1)" ng-show="ratedBy <= $index">★</span>
</li>
</ul>
</div>
</body>

You could hold an array of objects like so:
var starApp = angular.module('starApp',[]);
starApp.controller ('StarCtrl', ['$scope', function ($scope) {
$scope.ratings = [];
var rating = {
current : 5,
max : 10
}
$scope.ratings.push(rating); // instead you would push what your http service returns into thearray.
}]);
Then in your view you could use ng-repeat like so:
<body ng-app="starApp">
<div ng-controller="StarCtrl">
<span ng-repeat="rating in ratings">{{rating.current}} out of {{rating.max}}</span>
</div>
</body>

My minimalistic approach:
The view
<head>
<!-- alternatively you may use another CSS library (like FontAwesome) to represent the star glyphs -->
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"></link>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0-rc.2/angular.min.js"></script>
<!-- insert the JavaScript controller and the CSS enhancements here -->
</head>
<body>
<div ng-app="StarRatings">
<div ng-controller="myController as ctrl">
<div ng-repeat="n in ctrl.getStarArray()" ng-class="ctrl.getClass(n)" ng-mouseover="ctrl.setClass($event,n)"> </div>
<p>
You have chosen {{ctrl.selStars}} stars
</p>
</div>
</div>
</body>
Note: if you want to use the onClick event instead onMouseOver event then replace ng-mouseover with ng-click in the HTML above.
The controller
<script>
(function() {
var app = angular.module('StarRatings', []);
app.controller('myController', function() {
this.selStars = 0; // initial stars count
this.maxStars = 5; // maximum number of stars
// a helper function used within view
this.getStarArray = function() {
var result = [];
for (var i = 1; i <= this.maxStars; i++)
result.push(i);
return result;
};
// the class used to paint a star (filled/empty) by its position
this.getClass = function(index) {
return 'glyphicon glyphicon-star' + (this.selStars >= index ? '' : '-empty');
};
// set the DOM element class (filled/empty star)
this.setClass = function(sender, index) {
this.selStars = index;
sender.currentTarget.setAttribute('class', this.getClass(index));
};
});
})();
</script>
Optionally some CSS enhancements
<style>
.glyphicon {
color: gold;
cursor: pointer;
font-size: 1.25em;
}
</style>
Not convinced? Try this JSFiddle: https://jsfiddle.net/h4zo730f/2/

let app = angular.module ('myapp',[])
-
##star Rating Styles
----------------------*/
.stars {
padding-top: 10px;
width: 100%;
display: inline-block;
}
span.glyphicon {
padding: 5px;
}
.glyphicon-star-empty {
color: #9d9d9d;
}
.glyphicon-star-empty,
.glyphicon-star {
font-size: 18px;
}
.glyphicon-star {
color: #FD4;
transition: all .25s;
}
.glyphicon-star:hover {
transform: rotate(-15deg) scale(1.3);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<div ng-app= "myapp" >
<div class="stars">
<div id="stars" class="star">
<span ng-repeat="x in [0,1,2,3,4,5]" ng-if="($index < 4)" class="glyphicon glyphicon-star"> </span>
<span ng-repeat="x in [0,1,2,3,4,5]" ng-if="($index >= 4 && $index < 5) " class="glyphicon glyphicon-star-empty"></span>
</div>
</div>
</div>

hmc.starRating.js
angular.module('starRatings',[]).directive('starRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '='
},
link: function (scope, elem, attrs) {
console.log(scope.ratingValue);
function buildStars(){
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
}
buildStars();
scope.$watch('ratingValue',function(oldVal, newVal){
if(oldVal !== newVal){
buildStars();
}
})
}
}
});
<script src="hmc.starRating.js"></script>
**app.js**
var app = angular.module('app', ['ui.bootstrap', 'starRatings']);
**indix.html**
<div star-rating rating-value="7" max="8" ></div>
.rating {
color: #a9a9a9;
margin: 0 !important;
padding: 0 !important;
}
ul.rating {
display: inline-block !important;
}
.rating li {
list-style-type: none !important;
display: inline-block !important;
padding: 1px !important;
text-align: center !important;
font-weight: bold !important;
cursor: pointer !important;
width: 13px !important;
color: #ccc !important;
font-size: 16px !important;
}
.rating .filled {
color: #ff6131 !important;
width: 13px !important;
}

Related

Angular material 1 / md-virtual-repeat select scroll

What is the right way to change md-virtual-repeat's scroll to watch events from an other scroll ?
I have tried some manual triggering
And i readed this question but it is not exactly i want.
So is there a way to select a scrool to virtual-repeat container ?
EDIT 2
I need to change scrollbar of virtual repeat with scrollbar of Body
Live example
(function() {
'use strict';
var app = angular.module('MyApp', ['ngMaterial', 'ngMessages']);
app.filter('to_trusted', ['$sce', function($sce) {
return function(text) {
return $sce.trustAsHtml(text);
};
}]);
app.controller('AppCtrl', function($timeout) {
this.itemSize = 529.39;
var DynamicItems = function() {
this.loadedPages = {};
this.numItems = 0;
this.PAGE_SIZE = 50;
this.fetchNumItems_();
};
DynamicItems.prototype.getItemAtIndex = function(index) {
var pageNumber = Math.floor(index / this.PAGE_SIZE);
var page = this.loadedPages[pageNumber];
if (page) {
return page[index % this.PAGE_SIZE];
} else if (page !== null) {
this.fetchPage_(pageNumber);
}
};
DynamicItems.prototype.getLength = function() {
return this.numItems;
};
DynamicItems.prototype.fetchPage_ = function(pageNumber) {
this.loadedPages[pageNumber] = null;
$timeout(angular.noop, 300).then(angular.bind(this, function() {
this.loadedPages[pageNumber] = [];
var pageOffset = pageNumber * this.PAGE_SIZE;
//console.log(pageNumber);
for (var i = pageOffset; i < pageOffset + this.PAGE_SIZE; i++) {
var obj = {};
obj.name = 'Ad ' + i;
obj.surname = 'Soyad ' + i;
obj.age = i;
obj.image = 'http://lorempixel.com/75/75/cats?' + i;
obj.list = [];
obj.clicked = false;
obj.status = "asd";
obj.html = '' + i + '---Lorem Ipsum, dizgi ve baskı endüstrisinde kullanılan mıgır metinlerdir. Lorem Ipsum, adı bilinmeyen bir matbaacının bir hurufat numune kitabı oluşturmak üzere bir yazı galerisini alarak karıştırdığı 1500 lerden beri endüstri standardı sahte metinler olarak kullanılmıştır. Beşyüz yıl boyunca varlığını sürdürmekle kalmamış, aynı zamanda pek değişmeden elektronik dizgiye de sıçramıştır. 1960 larda Lorem Ipsum pasajları da içeren Letraset yapraklarının yayınlanması ile ve yakın zamanda Aldus PageMaker gibi Lorem Ipsum sürümleri içeren masaüstü yayıncılık yazılımları ile popüler olmuştur.' + i;
for (var j = 0; j < 1000; j++) {
obj.list.push('http://lorempixel.com/75/75/city/?' + j);
}
this.loadedPages[pageNumber].push(obj);
}
//this.setItemNum(this.numItems + this.PAGE_SIZE);
}));
};
DynamicItems.prototype.fetchNumItems_ = function() {
$timeout(angular.noop, 300).then(angular.bind(this, function() {
//console.log("fetchNumItems_");
this.numItems = 10000;
}));
};
DynamicItems.prototype.setItemNum = function(number) {
this.numItems = number;
};
this.dynamicItems = new DynamicItems();
//console.log(this.dynamicItems);
});
}());
.virtualRepeatdemoDeferredLoading #vertical-container {
width: 100%;
}
.virtualRepeatdemoDeferredLoading .repeated-item {
border-bottom: 1px solid #ddd;
box-sizing: border-box;
padding-top: 10px;
}
.virtualRepeatdemoDeferredLoading md-content {
margin: 16px;
}
.virtualRepeatdemoDeferredLoading md-virtual-repeat-container {
border: solid 1px grey;
}
.virtualRepeatdemoDeferredLoading .md-virtual-repeat-container .md-virtual-repeat-offsetter div {
padding-left: 16px;
}
.virtualRepeatdemoHorizontalUsage #horizontal-container {
height: 100px;
width: 100%;
}
.virtualRepeatdemoHorizontalUsage .repeated-item {
border-right: 1px solid #ddd;
box-sizing: border-box;
display: inline-block;
height: 84px;
padding-top: 10px;
text-align: center;
width: 100px;
}
.virtualRepeatdemoHorizontalUsage md-content {
margin: 16px;
}
.virtualRepeatdemoHorizontalUsage md-virtual-repeat-container {
border: solid 1px grey;
}
<!DOCTYPE html>
<html class=''>
<head>
<meta charset='UTF-8'>
<meta name="robots" content="noindex">
<link rel="shortcut icon" type="image/x-icon" href="//production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
<link rel="mask-icon" type="" href="//production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
<link rel="canonical" href="http://codepen.io/anon/pen/OWXMXW" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link rel='stylesheet prefetch' href='https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.css'>
<link rel='stylesheet prefetch' href='https://material.angularjs.org/1.1.1/docs.css'>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js'></script>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js'></script>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-route.min.js'></script>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-aria.min.js'></script>
<script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js'></script>
<script src='https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.1/angular-material.js'></script>
</head>
<body>
<h1> Virtual Repeat Test </h1>
<div ng-controller="AppCtrl as ctrl" ng-cloak="" flex layout-fill layout-padding class="virtualRepeatdemoDeferredLoading" ng-app="MyApp">
<md-content layout="column" flex layout-fill>
<md-virtual-repeat-container flex layout-fill md-auto-shrink="false" layout id="vertical-container">
<div md-virtual-repeat="item in ctrl.dynamicItems" id="repeat_item" md-on-demand="true" md-item-size="ctrl.itemSize" layout-fill>
<img ng-src="{{item.image}}" class="md-avatar" alt="{{item.name}}" />
<br> Adı : {{item.name}} <br> Soyadı : {{item.surname}} <br> Yaşı : {{item.age}} <br> is Clicked : {{ item.clicked }} <br> status : {{ item.status }}
<p ng-bind-html=" item.html | to_trusted "> </p>
<h5 ng-click="item.clicked = !item.clicked; item.status = item.status + item.status; "> List Items </h5>
<md-content ng-if="item.clicked == true" class="virtualRepeatdemoHorizontalUsage" layout="column">
<md-virtual-repeat-container id="horizontal-container" md-orient-horizontal="">
<div md-virtual-repeat="picture in item.list" class="repeated-item" flex="">
<img ng-src="{{ picture }}" class="md-avatar" alt="{{ picture }}" />
</div>
</md-virtual-repeat-container>
</md-content>
<br>
</div>
</md-virtual-repeat-container>
</md-content>
</div>
</body>
</html>
If I understand your question correctly, you just want to scroll the virtual repeater div using a scrollbar somewhere else on your page? One way to do that would be to create a directive that listens to scroll event of the div you want to use as the visible scrollbar and then just scroll the other div the same percentage.
Here is one such directive. Add it to the md-virtual-repeat-container and point it to the div you want to scroll it. The divs can even be different heights.
Update
I changed the directive to fall back to the body tag if an element wasn't specified.
app = angular.module('myApp', ['ngMaterial']);
app.controller("myController", function($scope) {
$scope.items = [];
for (var i = 0; i < 5000000; i++) {
$scope.items.push(i);
}
});
app.directive("scrollFrom", function($window, $document) {
return {
scope: {
elTarget: '#scrollFrom'
},
link: function(scope, element, attrs) {
var body = $document[0].body;
angular.element(scope.elTarget || $window).bind("scroll", function() {
var percentScrolled = scope.elTarget ?
this.scrollHeight > 0 ? this.scrollTop / (this.scrollHeight - element.height()) : 0 :
body.scrollHeight > 0 ? body.scrollTop / (body.scrollHeight - element.height()) : 0;
var trgt = angular.element(element).find('.md-virtual-repeat-scroller')[0];
trgt.scrollTop = trgt.scrollHeight * percentScrolled;
});
}
};
});
#vertical-container {
height: 292px;
width: 100%;
max-width: 400px;
position: fixed;
top: 10px;
}
#vertical-container .md-virtual-repeat-scroller {
overflow: hidden;
}
.repeated-item {
border-bottom: 1px solid #ddd;
box-sizing: border-box;
height: 40px;
padding-top: 10px;
}
md-virtual-repeat-container {
border: solid 4px grey;
}
<html ng-app="myApp">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.4/angular-material.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-aria.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.4/angular-material.min.js"></script>
</head>
<body style="height: 5000000px">
<div ng-controller="myController">
<md-virtual-repeat-container id="vertical-container" scroll-from>
<div md-virtual-repeat="item in items" class="repeated-item" flex>
{{item}}
</div>
</md-virtual-repeat-container>
</div>
</body>
</html>

Get the number of dates time later than current datetime

I have a array of objects where i have a property of date_time, and in this array i wish to get the lenght of dates_times objects that are sooner with my current date_time.
Ex:
[data:[{date: "2017-02-24 16:41:51"}, {date: ""2017-02-21 16:41:51"}...],
last_clicked: "2017-02-24 19:41:51"]
I wish to get the length of objecs on "data" array that haves a date_time sooner than "last_clicked".
Maybe you can try something like this:
let length = data.filter(function(d) {
return new Date(d.date) < new Date(last_clicked)
}).length;
You can use Angulars-orderBy-filter. I set up an example for you, just swap out where I have the id key for your data's date key.
function exampleController($scope, exampleFactory, $filter) {
$scope.list = [];
$scope.reverse = false;
$scope.changeSortOrder = function() {
$scope.reverse = !$scope.reverse;
};
function getList() {
exampleFactory
.getList()
.then(function(list) {
// $scope.list = list;
//alternatively you could filter here and change the sort order with the button if needed at all.
$scope.list = $filter('orderBy')(list, '-id'); //where this would be replaced by date(note: the '-' in front of id changes the sort order ASC/DESC)
//$scope.list = $filter('orderBy')(list, 'id', true); //where 3rd argument is wether order should be reversed
});
}
getList();
}
function exampleFactory($http) {
var root = 'http://jsonplaceholder.typicode.com';
function getList() {
return $http.get(root + '/comments')
.then(function(resp) {
return resp.data;
});
}
return {
getList: getList
};
}
angular
.module('app', [])
.controller('exampleController', exampleController)
.factory('exampleFactory', exampleFactory);
.container-fluid {
background-color: #1D1F20;
color: #fff;
font-size: 14px;
font-weight: bold;
button {
margin-top: 20%;
}
}
ul li {
list-style: none;
margin: 10px;
}
.child-padding>div {
padding: 2px;
}
.col-md-2 {
position: fixed;
button {
margin-bottom: 10%;
}
}
.btn-circle {
width: 30px;
height: 30px;
text-align: center;
padding: 6px 0;
font-size: 12px;
line-height: 1.428571429;
border-radius: 50%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div class="container-fluid" ng-app="app">
<div class="container" ng-controller="exampleController">
<div class="row">
<div class="col-md-2 text-center">
<button class="btn btn-primary" type="button" ng-click="changeSortOrder()">Change Sort Order</button>
</div>
<div class="col-md-10 pull-right">
<ul class="ul">
<li ng-repeat="comment in list | orderBy: 'date': reverse track by $index">
<div class="child-padding">
<div>
<span ng-bind="comment.email"></span>
<span class="pull-right btn-info btn-circle" ng-bind="comment.id"></span>
</div>
<div ng-bind="comment.body"></div>
</div>
<div ng-bind="comment.name"></div>
</li>
</ul>
</div>
</div>
</div>
</div>
Use Date.parse to parse your date strings
DEMO JsFiddle
The following code will alert:
Sooner times: 1
var o = {
data:[
{date: "2017-02-24 16:41:51"},
{date: "2017-02-21 16:41:51"}
],
last_clicked: "2017-02-24 12:41:51"
};
function getSoonerLength(data, soonerThan) {
var count = 0;
for (var i = 0 ; i < data.length ; i++) {
var d = Date.parse(data[i].date);
var than = Date.parse(soonerThan);
if (d < than)
count++;
}
return count;
}
alert('Sooner times: ' + getSoonerLength(o.data, o.last_clicked))

CSS Star Rating with ng-repeat and json data using angularjs

Want to show a div n times according to the value which was received from JSON.
My object:
$scope.pro = [
{
product: "chicken",
rating: 3
},
{
product: "fish",
rating: 3
},
{
product: "pizza",
rating: 4
}
];
If a product has 3 ratings means the div have to show three times, like a star rating.
How to do it in angular.js?
My Plunker Demo
Can you try this,
JS
$scope.pro = [{product: "chicken", rating: 3},{product: "fish", rating: 3},{product: "pizza", rating: 4}];
var ratingTotal = 5;
$scope.getRepeater = function() {
return new Array(ratingTotal);
};
Html
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script data-require="angular.js#1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="myController">
<div ng-repeat="array in pro">{{array.product}} <span class="star-icon" ng-repeat="r in getRepeater() track by $index" ng-class="{'full': ($index + 1) <= array.rating, 'half': ($index + .5) == array.rating}" ></span></div>
</body>
</html>
Note: The class name for selected star is mentioned as 'full' and feel free to change this.
geNumber() will create an empty array with the size of the rating. ng-repeat will iterate over it no matter what is inside
track by $index is necessary in this case because you will display multiple times the same value and duplicates in a repeater are not allowed
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.pro = [{
product: "chicken",
rating: 3
}, {
product: "fish",
rating: 3
}, {
product: "pizza",
rating: 4
}];
$scope.getNumber = function(num){
return new Array(num);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in pro">
<div ng-repeat="n in getNumber(item.rating) track by $index">
{{item.product}}
</div>
</div>
</body>
You can create an array, based on the rating. Then repeat over that array:
<div ng-repeat="array in pro">
{{array.product}} , <span ng-repeat="n in createArray(array.rating) track by $index">X</span>
</div>
In your controller:
$scope.createArray = function(n){
return new Array(n);
}
https://plnkr.co/edit/gUPn6m7Tiu01yksa9VOs?p=preview
If you're feeling fancy, you can use this ES6 solution to show x number of *:
Note: no IE support at all.
JS
$scope.getAsterisks = rating => Array.from('*'.repeat(parseInt(rating, 10)));
HTML
<span ng-repeat="x in getAsterisks(array.rating) track by $index">{{x}}</span>
Plunker: https://plnkr.co/edit/9H3j3NH9w5xNvqM142Gq?p=preview
Info on the ES6 functions:
Array.from (no support in IE) creates an array from a string, each character becomes an array item.
String.prototype.repeat (no support in IE and Opera) ... repeats the string X times.
<div ng-repeat="array in pro">{{array.product}} ,
<span ng-repeat=" arr in array.rating ">{{arr.j}}</span>
</div>
// Code goes here
// Code goes here
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.pro = [{product: "chicken", rating: 3},{product: "fish", rating: 4},{product: "pizza", rating: 6}];
for(var i=0;i<$scope.pro.length;i++)
{
if($scope.pro[i].rating >0)
{
$scope[$scope.pro[i].product]=[];
for(var j=0;j< $scope.pro[i].rating;j++)
{
$scope[$scope.pro[i].product].push({j:'*'});
}
$scope.pro[i].rating = $scope[$scope.pro[i].product];
}
}
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script data-require="angular.js#1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="myController">
<div ng-repeat="array in pro">{{array.product}} ,
<span ng-repeat=" arr in array.rating ">{{arr.j}}</span>
</div>
</body>
</html>
An AngularJS directive ratings:
angular
.module('myApp', [])
.directive('ratings', function () {
return {
restrict: 'E',
scope: false,
template: '<span ng-repeat="x in arrRating track by $index">★</span>',
link: function ($scope, $el, $attr) {
$scope.arrRating = new Array(+$attr.rating);
}
};
})
.controller('myController', function ($scope) {
$scope.pro = [{product: "chicken",rating: 3}, {product: "fish",rating: 3}, {product: "pizza",rating: 4}, {product: "steak",rating: 10}];
});
<script data-require="angular.js#1.3.15" data-semver="1.3.15" src="https://code.angularjs.org/1.3.15/angular.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="p in pro">
{{p.product}} <ratings rating="{{p.rating}}"></ratings>
</div>
</div>
A better way to do this is to create a star component via a custom directive it can also be reused throughout the Angular app, this directive takes the rating and generates the number of stars in the DOM.
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.controller('StarController', StarController)
.directive('star', star);
function DefaultController() {
var vm = this;
vm.products = [{
product: "chicken",
rating: 3
}, {
product: "fish",
rating: 4
}, {
product: "pizza",
rating: 5
}];
}
function star() {
var directive = {
restrict: 'E',
scope: {
rating: '=',
max: '='
},
link: linkFunc,
controller: StarController,
controllerAs: 'star',
bindToController: true
};
return directive;
function linkFunc(scope, element, attrs, ngModelCtrl) {
for (var i = 0; i < scope.max; i++) {
var fillStyle = '';
if (i < scope.rating) {
fillStyle = 'fill';
} else {
fillStyle = 'empty';
}
element.append('<span class="star-icon ' + fillStyle + '">☆</span>');
}
}
}
function StarController() {
var vm = this;
}
ul {
font-size: 20px;
list-style-type: none;
padding: 0;
}
ul li {
padding: 10px;
}
ul li > span {
display: inline-block;
width: 100px;
}
star span {
margin: 0px 5px;
}
.star-icon {
color: #ddd;
font-size: 1.5em;
position: relative;
top: 3px;
}
.star-icon.fill:before {
text-shadow: 0 0 1px rgba(0, 0, 0, 0.7);
color: #FDE16D;
content: '\2605';
position: absolute;
left: 0;
}
.star-icon.empty:before {
text-shadow: 0 0 1px rgba(0, 0, 0, 0.7);
color: #FFF;
content: '\2605';
position: absolute;
left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
<ul>
<li ng-repeat="product in ctrl.products">
<span ng-bind="product.product"></span>
<star rating="product.rating" max="5"></star>
</li>
</ul>
</div>
</div>
Star rating inspired by https://coderwall.com/p/iml9ka/star-ratings-in-css-utf8

knockout, css binding dynamic and conditional

I wan't to have a conditional css class and a dynamic css class added via the css binding.
Like so:
data-bind="css: {$data.something() : true, open : showOpen() }"
Clearest is probably to combine them in one computed:
function ViewModel() {
var self = this;
self.something = ko.observable("danger");
self.showOpen = ko.observable(true);
self.cssClass = ko.computed(function() {
return self.something() + (self.showOpen() ? " open" : "");
});
}
ko.applyBindings(new ViewModel());
div { padding: 10px; }
.danger { background-color: orange; }
.open { border: 5px solid gray; border-width: 5px 5px 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<div data-bind="css: cssClass"> my div with class: <code data-bind="text: cssClass"></code> </div>
<hr>
<label><input type="checkbox" data-bind="checked: showOpen"> showOpen</label>
<br>
<input type="text" data-bind="value: something, valueUpdate: 'afterkeydown'">
Allows you to unit test the entire thing, and keeps your view concise.
I prefer a custom binding like this:
ko.bindingHandlers.klass = {
init: function (el, val) {
var prevClass = null
ko.computed(function () {
if (prevClass)
$(el).removeClass(prevClass);
var newClass = ko.unwrap(val());
$(el).addClass(newClass);
prevClass = newClass;
}, null, {disposeWhenNodeIsRemoved: el})
}
}
var vmo = {
cssClass: ko.observable('a'),
toggle: function () { vmo.cssClass(vmo.cssClass() == 'a' ? 'b' : 'a') }
}
ko.applyBindings(vmo);
.a {
color: red;
}
.b {
color: blue;
}
.another {
text-decoration: underline;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<p data-bind='klass: cssClass, css: {another: true}'>Hello</p>
<button data-bind='click: toggle'>Toggle</button>

Angular list of items ng-repeat, ng-click show description on full width

<div ng-controller = "MyController">
<ul class="items" >
<div ng-repeat="item in colors" ng-class="{active:isActive(item)}" ng-click="select(item); whattoshow=!whattoshow">
<li class="col-md-3 col-sm-3 col-lg-3 col-xs-3" >
<img class="img-responsive" ng-src="images/colors/{{item.number}}.jpg">
</li>
<div class="col-md-12 col-sm-12 col-lg-12 col-xs-12" ng-class="whattoshow && isActive(item) ? 'show' : 'hidden'}">
<h2>{{item.bio}}</h2>
</div>
</div>
</ul>
</div>
That is my HTML code, the controller uses a JSON file to go through the items, and if you click on an item you shall see the description of it. As I try to show in this poorly drawn picture (http://i.stack.imgur.com/FCvmd.png), I can make the item bio appear after the item's picture, but as every description corresponds to its own item picture it makes my display order to change. I want every items description show on click below his own row of items.
Here is my angular controller if needed.
var myApp = angular.module('ProjectAssembly', []);
myApp.controller('MyController', ['$scope', '$http', function($scope, $http) {
$http.get('data/color.json').success(function(data) {
$scope.colors = data;
});
$scope.select= function(item) {
$scope.selected = item;
};
$scope.isActive = function(item) {
return $scope.selected === item;
};
}]);
I hope you can help my case, it seemed to be easy, but I can't find the solution :/
What you need is ng-repeat-start and ng-repeat-end and split your data into rows.
See details in example:
var myApp = angular.module('ProjectAssembly', []);
myApp.controller('MyController', ['$scope', '$http',
function($scope, $http) {
//$http.get('data/color.json').success(function(data) {});
$scope.colors = [{
number: 1,
bio: '1111111111111111111111111111111111111111'
}, {
number: 2,
bio: '2222222222222222222222222222222222222222'
}, {
number: 3,
bio: '33333333333333333333333333333333333333333'
}, {
number: 4,
bio: '4444444444444444444444444444444444444444'
}, {
number: 5,
bio: '55555555555555555555555555555'
}]
$scope.colors = (function(data) {
var result = [];
angular.forEach(data, function(val, index) {
var key = Math.floor(index / 4);
if (!result[key]) {
result[key] = [];
}
result[key].push(val);
});
return result;
})($scope.colors);
$scope.select = function(item, rowIndex, columnIndex) {
if (item == $scope.selected) {
$scope.selected = null;
$scope.selectedRowIndex = null;
$scope.selectedColumnIndex = null;
return false;
}
$scope.selected = item;
$scope.selectedRowIndex = rowIndex;
$scope.selectedColumnIndex = columnIndex;
};
$scope.isActive = function(item) {
return $scope.selected === item;
};
}
]);
.item-tr {
border: 1px solid #555;
margin-top: 5px;
position: relative;
background: #555;
}
.item-tr:before {
content: ' ';
position: absolute;
border-top: 5px solid transparent;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #555;
top: -11px;
}
.item-tr-0:before {
left: 12.5%;
}
.item-tr-1:before {
left: 37.5%;
}
.item-tr-2:before {
left: 62.5%;
}
.item-tr-3:before {
left: 87.5%;
}
.item-td {
border: 1px solid #555;
}
<link href="//cdn.bootcss.com/bootstrap/3.0.0/css/bootstrap-theme.css" rel="stylesheet">
<link href="//cdn.bootcss.com/bootstrap/3.0.0/css/bootstrap.css" rel="stylesheet">
<script src="//cdn.bootcss.com/jquery/2.2.1/jquery.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.0.0/js/bootstrap.js"></script>
<script src="//cdn.bootcss.com/angular.js/1.4.7/angular.js"></script>
<div ng-app="ProjectAssembly">
<div ng-controller="MyController">
<ul class="items">
<li ng-repeat-start="row in colors track by $index" class="row">
<div class="col-md-3 col-sm-3 col-lg-3 col-xs-3 item-td" ng-repeat="item in row" ng-class="{active:isActive(item)}" ng-click="select(item,$parent.$index,$index); whattoshow=!whattoshow">
<span>
<img class="img-responsive" ng-src="images/colors/{{item.number}}.jpg">
{{item.number}}
</span>
</div>
</li>
<li ng-repeat-end ng-show="selected && selectedRowIndex==$index" class="row item-tr item-tr-{{selectedColumnIndex}}">
<div>
<h2>{{selected.bio}}</h2>
</div>
</li>
</ul>
</div>
</div>

Categories