How to stop hiding the treeview when the expand/collapse icon was clicked? But clicking the name/item in the treeview should hide the treeview. Below is the sample implementation where a dropdownlist will show/hide a treeview with searchbox. when I click the searchbox the treeview component should remain showing same goes while clicking the expand/collapse icon. But when I click the item the treeview it should hide. When I hover outside the treeview component, it should hide. The list item is dynamically created inside the component. I tried to pass the event, in the click event for list item element, and I called the event.stopPropagation but it's not working. Any ideas?
dropdowntreeview.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Demo angular-tree-dropdown</title>
<link rel='stylesheet' href="dropdowntreeview.css" />
<link rel='stylesheet' href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
<link rel='stylesheet' href="dropdowntreeview.css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js" ></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js" ></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js" ></script>
<script src="dropdowntreeview.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="myController" class="dropdown dropdown-scroll">
<div class="btn btn-default dropdown-toggle" id="dropdownMenu1" data-toggle="dropdown" style="margin:10px 0 30px 0; padding:10px; background-color:#EEEEEE; border-radius:5px; font:12px Tahoma;">
<span><b>Selected Node</b> : {{mytree.currentNode.roleName}}</span>
</div>
<div class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
<input ng-model='treeSearchQuery' type="text" placeholder="Search in tree..."/>
<div
data-angular-treeview="true"
data-tree-id="mytree"
data-tree-model="roleList"
data-node-id="roleId"
data-node-label="roleName"
data-node-children="children"
data-search-query='treeSearchQuery'>
</div>
</div>
</div>
</div>
<body>
</html>
dropdowntreeview.js
(function(){
//angular module
var myApp = angular.module('myApp', ['angularTreeview']);
//test controller
myApp.controller('myController', function($scope){
//test tree model 2
$scope.roleList2 = [
{ "roleName" : "User default expanded", "roleId" : "role1", "expanded" : true, "children" : [
{ "roleName" : "subUser1 non-openable folder", "roleId" : "role11" },
{ "roleName" : "subUser2", "roleId" : "role12", "expanded" : true, "children" : [
{ "roleName" : "subUser2-1", "roleId" : "role121", "expanded" : true, "children" : [
{ "roleName" : "subUser2-1-1 folder ico", "roleId" : "role1211" },
{ "roleName" : "subUser2-1-2 file ico", "roleId" : "role1212" , "fileicon" : true}
]}
]}
]},
{ "roleName" : "Admin default collapsed", "roleId" : "role2", "children" : [
{ "roleName" : "subAdmin1", "roleId" : "role11", "children" : [] },
{ "roleName" : "subAdmin2", "roleId" : "role12", "children" : [
{ "roleName" : "subAdmin2-1", "roleId" : "role121", "children" : [
{ "roleName" : "subAdmin2-1-1", "roleId" : "role1211", "children" : [] },
{ "roleName" : "subAdmin2-1-2", "roleId" : "role1212", "children" : [] }
]}
]}
]}
];
//roleList1 to treeview
$scope.roleList = $scope.roleList2;
$('.dropdown-menu').find('input').click(function (e) {
e.stopPropagation();
});
});
})();
(function(angular) {
'use strict';
angular.module('angularTreeview', []).directive('treeModel', ['$compile', function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var treeId = attrs.treeId;
var treeModel = attrs.treeModel;
var nodeId = attrs.nodeId;
var nodeLabel = attrs.nodeLabel;
var nodeChildren = attrs.nodeChildren;
var searchQuery = attrs.searchQuery;
var template = '<ul>' + '<li data-ng-repeat="node in ' + treeModel +
' | filter:' + searchQuery + ' ">' +
'<i class="collapsed" data-ng-class="{nopointer: !node.' +
nodeChildren + '.length}"' +
'data-ng-show="!node.expanded && !node.fileicon" data-ng-click="' +
treeId + '.selectNodeHead(node,event)"></i>' +
'<i class="expanded" data-ng-show="node.expanded && !node.fileicon" data-ng-click="' +
treeId + '.selectNodeHead(node)"></i>' +
'<i class="normal" data-ng-show="node.fileicon"></i> ' +
'<span title="{{node.' + nodeLabel + '}}" data-ng-class="node.selected" data-ng-click="' +
treeId + '.selectNodeLabel(node)">{{node.' + nodeLabel + '}}</span>' +
'<div data-ng-show="node.expanded" data-tree-id="' +
treeId + '" data-tree-model="node.' + nodeChildren +
'" data-node-id=' + nodeId + ' data-node-label=' + nodeLabel +
' data-node-children=' + nodeChildren + ' data-search-query=' +
searchQuery + '></div>' + '</li>' + '</ul>';
if (treeId && treeModel) {
if (attrs.angularTreeview) {
scope[treeId] = scope[treeId] || {};
scope[treeId].selectNodeHead = scope[treeId].selectNodeHead || function(selectedNode,event) {
//below code is not working!
event.stopPropagation();
if (selectedNode[nodeChildren] !== undefined) {
selectedNode.expanded = !selectedNode.expanded;
}
};
scope[treeId].selectNodeLabel = scope[treeId].selectNodeLabel || function(selectedNode) {
if (scope[treeId].currentNode && scope[treeId].currentNode.selected) {
scope[treeId].currentNode.selected = undefined;
}
selectedNode.selected = 'selected';
scope[treeId].currentNode = selectedNode;
};
}
element.html('').append($compile(template)(scope));
}
}
};
}]);
})(angular);
dropdowntreeview.css
div[data-angular-treeview] {
/* prevent user selection */
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
/* default */
font-family: Tahoma;
font-size:13px;
color: #555;
text-decoration: none;
}
div[data-tree-model] ul {
margin: 0;
padding: 0;
list-style: none;
border: none;
overflow: hidden;
}
div[data-tree-model] li {
position: relative;
padding: 0 0 0 20px;
line-height: 20px;
}
div[data-tree-model] li .expanded {
padding: 1px 10px;
background-image: url("collapse-icon.png");
background-repeat: no-repeat;
}
div[data-tree-model] li .collapsed {
padding: 1px 10px;
background-image: url("expand-icon.png");
background-repeat: no-repeat;
}
div[data-tree-model] li .normal {
padding: 1px 10px;
background-image: url("");
background-repeat: no-repeat;
}
div[data-tree-model] li i, div[data-tree-model] li span {
cursor: pointer;
}
div[data-tree-model] li .selected {
background-color: #aaddff;
font-weight: bold;
padding: 1px 5px;
}
.dropdown.dropdown-scroll .dropdown-menu {
max-height: 200px;
width: 60px;
overflow: auto;
}
You have to add $event as input parameter when invoking the selectNodeHead function.
From your code (refactored, just for human readablity, with backtick approach:
var template = `
<ul>
<li data-ng-repeat="node in ${treeModel} | filter: ${searchQuery}">
<i
class="collapsed"
data-ng-class="{nopointer: !node.${nodeChildren}.length}"
data-ng-show="!node.expanded && !node.fileicon"
data-ng-click="${treeId}.selectNodeHead(node, $event)"
></i>
<i
class="expanded"
data-ng-show="node.expanded && !node.fileicon"
data-ng-click="${treeId}.selectNodeHead(node, $event)"
></i>
<i
class="normal"
data-ng-show="node.fileicon"
></i>
<span
title="{{ node.${nodeLabel} }}"
data-ng-class="node.selected"
data-ng-click="${treeId}.selectNodeLabel(node)"
>{{ node.${nodeLabel} }}</span>
<div
data-ng-show="node.expanded"
data-tree-id="${treeId}"
data-tree-model="node.${nodeChildren}"
data-node-id="${nodeId}"
data-node-label="${nodeLabel}"
data-node-children="${nodeChildren}"
data-search-query="${searchQuery}"
></div>
</li>
</ul>
`;
Hope it helps.
Related
I have a basic add-to-cart that allows for adding and removing cart items using checkboxes.
I want to be able to pre-set some of the items to be in the cart by getting the item's id or data-wish attribute and adding it to the array for the items.
var wish = {
items: ["handbag", "backpack"]
};
How do I allow for items to be pre-selected so that they appear in the cart and the .my-wish-add button is :checked (for the icon's checked state animation).
// Wish Function
var wish = {
items: []
};
var update_product = function(product) {};
$(function() {
//Add to wish
var addToWish = function(product, qty) {
qty = qty || 1;
var wish = getWish();
var indexOfId = wish.items.findIndex(x => x.id == product.id);
if (indexOfId === -1) {
wish.items.push({
id: product.id,
img: product.img,
name: product.name,
location: product.location,
price: product.price
});
$parent = $("#" + product.id).closest(".items__cart");
$parent
.find(".wish-icon")
.addClass("active")
.attr("data-prefix", "fas");
} else {
wish.items[indexOfId].stock = Number(product.stock);
}
//Update popup wish
updateWish(wish);
};
//Remove from wish on id
var removeFromWish = function(id) {
var wish = getWish();
var wishIndex = wish.items.findIndex(x => x.id == id);
wish.items.splice(wishIndex, 1);
$parent = $("#" + id).closest(".items__cart");
$parent
.find(".wish-icon")
.first()
.removeClass("active")
.attr("data-prefix", "far");
//Update popup wish
updateWish(wish);
};
var getProductValues = function(element) {
var productId = $(element)
.closest(".items__cart")
.find(".item__tile")
.attr("id");
var productImg = $(element)
.closest(".items__cart")
.find(".item__img")
.attr("src");
var productName = $(element)
.closest(".items__cart")
.find(".item__title")
.html();
var productPrice = $(element)
.closest(".items__cart")
.find(".cost")
.html();
var productLocation = $(element)
.closest(".items__cart")
.find(".item__location")
.html();
var productTax = $(element)
.closest(".items__cart")
.find(".circle")
.html();
return {
id: productId,
img: productImg,
name: productName,
location: productLocation,
price: productPrice,
tax: productTax
};
};
$(".my-wish-add").on("change", function() {
var product = getProductValues(this);
if ($(this).is(":checked")) {
addToWish({
id: product.id,
img: product.img,
name: product.name,
location: product.location,
price: product.price,
tax: product.tax
});
} else {
removeFromWish(product.id);
}
});
//Update wish html to reflect changes
var updateWish = function(wish) {
$(".wishlist__items").html("");
for (var i = 0; i < wish.items.length; i++) {
$(".wishlist__items").append(
"<li class='wish__item'>" +
'<div class="wish__thumb">' +
"<img src='" +
wish.items[i].img +
"' />" +
"</div>" +
'<div class="wish__info">' +
'<div class="wish__header">' +
'<div class="wish-name">' +
wish.items[i].name +
"</div>" +
"</div>" +
' <div class="wish__value">' +
"<span>Price: </span> $ " +
wish.items[i].price +
"</div>" +
"</div>" +
'<div class="wish__remove">' +
'<label class="wish__label">' +
'<input type="checkbox" id="my-wish-remove' +
i +
'" class="my-wish-remove" aria-hidden="true">' +
"<i class='fas fa-heart'></i>" +
"</div>" +
"</div>"
);
(function() {
var currentIndex = i;
$("#my-wish-remove" + currentIndex).on("change", function() {
$(this)
.closest("li")
.hide(400);
setTimeout(function() {
wish.items[currentIndex].stock = "";
update_product(wish.items[currentIndex]);
$("#" + wish.items[currentIndex].id)
.parents()
.find($(".wish-btn > input"))
.prop("checked", false);
removeFromWish(wish.items[currentIndex].id);
}, 400);
});
})();
}
};
//Get Wish
var getWish = function() {
var myWish = wish;
return myWish;
};
});
.wish__item {
display: flex;
}
.my-wish-add {
font-family: "Font Awesome\ 5 Pro";
font-weight: 900;
color: #6394f8;
}
.wish-btn {
position: relative;
cursor: pointer;
}
.wish-btn input {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.wish-icon.active {
color: green;
}
.active i.fa.fa-wishlist__list {
color: #6394f8;
}
.my-wish-remove {
font-size: 20px;
cursor: pointer;
color: rgb(109, 109, 109);
}
.my-wish-remove:hover {
color: #464646;
}
.items__wish {
border: 1px solid;
width: 150px;
}
.wish__info {
flex: 1;
border: 2px solid;
}
.wish__thumb {
display: flex;
}
.wish__thumb img {
height: 30px;
}
.wish-main {
flex: 1;
}
img {
width: 50px;
}
.wishlist__list {
margin: 20px 0;
float: right;
background: #fff;
width: 320px;
position: absolute;
border: 2px solid;
border-radius: 3px;
padding: 20px;
right: 7.5%;
}
.wishlist__items li {
list-style: none;
}
.wishlist__list-empty img {
width: 100px;
}
<script src="https://pro.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-id="wishlist">
<div class="wishlist__list">
<ul class="wishlist__items">
</ul>
</div>
</div>
<div class='products'>
<div class="items__cart">
<div id='headphones' class='item__title item__tile' data-wish="headphones">Product 1</div>
<img class="item__img" src="https://www.iconasys.com/wp-content/uploads/2017/06/360-Product-Photography-White-Background-Acrylic-Riser-08.jpg">
<div>
<span>$</span>
<span class='cost'>500</span>
</div>
<label class="wish-btn">
<input type="checkbox" name="wish-check" class='my-wish-add'><i class="wish-icon far fa-heart"></i></input></label>
</div>
<div class="items__cart">
<div id='backpack' class='item__title item__tile' data-wish="backpack">Product 2</div>
<div>
<h4 class="count">200</h4>
</div>
<img class="item__img" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQoqpSgkG4AQDQOe33jI1NiW3GW2JSB-_v36aREsVyFQH55JFOJ">
<div>
<span>$</span>
<span class='cost'>460</span>
</div>
<label class="wish-btn">
<input type="checkbox" name="wish-check" class='my-wish-add'><i class="wish-icon far fa-heart"></i></label>
</div>
<div class="items__cart">
<div id='handbag' class='item__title item__tile' data-wish="handbag">Product 3</div>
<h4 class="count">400</h4>
<img class="item__img" src="https://qph.fs.quoracdn.net/main-qimg-de7d9680c4460296e461af9720a77d64">
<div>
<span>$</span>
<span class='cost'>212</span>
</div>
<label class="wish-btn">
<input type="checkbox" name="wish-check" class='my-wish-add'><i class="wish-icon far fa-heart"></i></label>
</div>
This following works when a list item is selected and then hovered and a popover is shown. But when I try to remove popover data attributes from list tag, all the tag removes but the popover does not remove. How to remove the popover such that when an item is not selected, the popover is not shown?
/* Latest compiled and minified JavaScript included as External Resource */
// Checked list box items
$(function() {
$('.list-group.checked-list-box .list-group-item').each(function() {
// Settings
var $widget = $(this),
$checkbox = $('<input type="checkbox" class="hidden" />'),
color = ($widget.data('color') ? $widget.data('color') : "primary"),
style = ($widget.data('style') == "button" ? "btn-" : "list-group-item-"),
settings = {
on: {
icon: 'glyphicon glyphicon-check'
},
off: {
icon: 'glyphicon glyphicon-unchecked'
}
};
$widget.css('cursor', 'pointer')
$widget.append($checkbox);
// Event Handlers
$widget.on('click', function() {
$checkbox.prop('checked', !$checkbox.is(':checked'));
$checkbox.triggerHandler('change');
updateDisplay();
});
$checkbox.on('change', function() {
var id = $(this).closest('li').attr('id');
var isChecked = $checkbox.is(':checked');
if (isChecked) addPopOver(id);
else removePopOver(id);
updateDisplay();
});
function addPopOver(id) {
id = "#" + id;
$(id).attr('data-toggle', "popover");
$(id).attr('data-trigger', "hover");
$(id).attr('data-original-title', $(id).text());
$(id).attr('data-content', "(No items selected)");
$('[data-toggle=popover]').popover();
}
function removePopOver(id) {
id = "#" + id;
$(id).removeAttr("data-toggle");
$(id).removeAttr("data-trigger");
$(id).removeAttr("data-original-title");
$(id).removeAttr("data-content");
}
// Actions
function updateDisplay() {
var isChecked = $checkbox.is(':checked');
// Set the button's state
$widget.data('state', (isChecked) ? "on" : "off");
// Set the button's icon
$widget.find('.state-icon')
.removeClass()
.addClass('state-icon ' + settings[$widget.data('state')].icon);
// Update the button's color
if (isChecked) {
$widget.addClass(style + color + ' active');
} else {
$widget.removeClass(style + color + ' active');
}
}
// Initialization
function init() {
if ($widget.data('checked') == true) {
$checkbox.prop('checked', !$checkbox.is(':checked'));
}
updateDisplay();
// Inject the icon if applicable
if ($widget.find('.state-icon').length == 0) {
$widget.prepend('<span class="state-icon ' + settings[$widget.data('state')].icon + '"></span>');
}
}
init();
});
$('#get-checked-data').on('click', function(event) {
event.preventDefault();
var checkedItems = {},
counter = 0;
$("#check-list-box li.active").each(function(idx, li) {
checkedItems[counter] = $(li).text();
counter++;
});
$('#display-json').html(JSON.stringify(checkedItems, null, '\t'));
});
});
/* Check Box For item required */
.state-icon {
left: -5px;
}
.list-group-item-primary {
color: rgb(255, 255, 255);
background-color: rgb(66, 139, 202);
}
/* DEMO ONLY - REMOVES UNWANTED MARGIN */
.well .list-group {
margin-bottom: 0px;
}
.list-inline>li {
display: inline-block;
padding-right: 12px;
padding-left: 20px;
margin-bottom: 3px;
font-size: 17px;
}
#check-list-box {
padding: 10px;
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<ul id="check-list-box" class="list-group checked-list-box list-inline ">
<li class="list-group-item event-item" id="venue" data-color="danger">Venue</li>
<li class="list-group-item event-item" id="catering" data-color="info">Catering</li>
<li class="list-group-item event-item" id="desserts" data-color="warning">Desserts</li>
<li class="list-group-item event-item" id="photographer" data-color="success">Photographer</li>
<li class="list-group-item event-item" id="bus" data-color="danger">Party buses</li>
<li class="list-group-item event-item" id="castles" data-color="danger">Bouncy Castles</li>
<li class="list-group-item" data-color="danger">Other</li>
<!--<input type="textbox" name ="other" >-->
</ul>
You could use .popover('destroy').
function removePopOver(id) {
id = "#" + id;
$(id).popover('destroy')
}
To destroy the shown popover you can use the following code-snippet:
function removePopOver(id) {
id = "#" + id;
$(id).popover('dispose'); // JQuery > 4.1
// $(id).popover('destroy'); // JQuery < 4.1
}
You can also remove all created popovers from your DOM via .popover class (of course each popover has its own id, so by knowing the IDs you can be more precise)
$('.popover').remove();
I have html file , and JSON file . In html file , I have created textbox with button to enable the user to enter a course name . In JSON I have an array of course objects contains some information regarding the course .
I want to use Ajax with jQuery when the user enter a course and hit the button it should loop the array in json to check whether this value is found or not . If it is found then show the details of this course .
This is my trying .
<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
<head>
<title>Finding Course Details</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#btn').click(function() {
var result = $('#div1');
result.html('');
var data1 = $('#text1').val();
$.ajax({
url: 'course.json',
method: 'get',
data: data1,
dataType: 'json',
success: function(response) {
result.html('level: ' + response.course[0].level + '<br/>' + 'Theoretical hours :' + response.course[0].Theoretical + '<br/>' + 'Practical hours :' + response.course[0].Practical);
}
});
});
});
</script>
<style>
div.container {
width: 100%;
border: 1px solid gray;
}
header,
footer {
padding: 1em;
color: black;
background-color: white;
clear: left;
text-align: center;
}
nav {
float: left;
max-width: 160px;
margin: 0;
padding: 1em;
}
a:hover {
color: blue;
background-color: transparent;
text-decoration: underline
}
nav ul {
list-style-type: none;
padding: 0;
}
nav ul a {
text-decoration: none;
}
article {
margin-left: 170px;
border-left: 1px solid gray;
padding: 1em;
overflow: hidden;
}
</style>
</head>
<body>
<div class="container">
<header>
<img src="9.jpg" width="300" height="200" alt="">
</header>
<nav>
<ul>
<li>Main
</li>
<li>Courses
</li>
</ul>
</nav>
<article>
<h1>Course Details</h1>
<form>
Enter Course Name :
<br/>
<input type="text" id="text1">
<input type="button" id="btn" value="Get Course Details">
<br/>
</form>
<div id="div1"></div>
</article>
<footer>B</footer>
</div>
</body>
</html>
This is json code :
{
"course" : [
{
"name":"Computer Programming",
"level" : "level 1",
"Theoretical" : "4",
"Practical" : "2"
},
{
"name":"Web Technologies",
"level" : "level 2",
"Theoretical" : "2",
"Practical" : "2"
},
{
"name":"Project",
"level" : "level 4",
"Theoretical" : "2",
"Practical" : "2"
}
]
}
get the specific json by user entered data using $.grep
var data = {
"items": [{
"id": 1,
"category": "cat1"
}, {
"id": 2,
"category": "cat2"
}, {
"id": 3,
"category": "cat1"
}]
};
var returnedData = $.grep(data.items, function (element, index) {
return element.id == 1;
});
alert(returnedData[0].id + " " + returnedData[0].category);
Vanilla JavaScript solution:
var searchItem = "Web Technologies"
var jsonCourses = {
"course" : [
{
"name":"Computer Programming",
"level" : "level 1",
"Theoretical" : "4",
"Practical" : "2"
},
{
"name":"Web Technologies",
"level" : "level 2",
"Theoretical" : "2",
"Practical" : "2"
},
{
"name":"Project",
"level" : "level 4",
"Theoretical" : "2",
"Practical" : "2"
}
]
};
var details = jsonCourses.course.filter(function(i){
if(i.name === searchItem)return i
})
alert("Course:"+details[0].name+"|Level:"+details[0].level)
I am a newer for javascript and gridstack. My job is creating a dashboard based on gridstack. The most close example for my goal is demo example knockout.html. Also, I need add on several new features based on this example. Here is the problem I am facing.
I need add a dropdown menu to each widget and it is added to the template, please refer the code attached below. Now I am using a button for toggling the drop menu. Is that possible for me to use the mouse right click to show the dropdown menu?
When I added the method named toggleDropdownMenu, I am getting a type error as "TypeError: this.toggleDropdownMenu is not a function", is this function should be defined somewhere?
I want to make specified widget to be a static. I added a attr settings when create the widget like staticGrid: true but does not work. How can fix this?
For each dash board, the dropdown menu is different for each widget. How can I attach dropdown menu with different items for each widget?
Please refer the code... Thank you very much for any advices!
<!DOCTYPE html>
<html lang="en">
<head>
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dash board demo</title>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="./dist/gridstack.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="./dist/gridstack.js"></script>
<style type="text/css">
.grid-stack {
background: lightgoldenrodyellow;
}
.grid-stack-item-content {
color: #2c3e50;
text-align: center;
background-color: #18bc9c;
}
.grid-stack-item-content button{
background-color: Transparent;
background-repeat:no-repeat;
border: none;
cursor:pointer;
overflow: hidden;
outline:none;
color: white;
position: relative;
}
.grid-stack-item-content .dropdown-submenu {
position: relative;
}
.grid-stack-item-content .dropdown-submenu .dropdown-menu {
top: 0;
left: 100%;
margin-top: -1px;
}
</style>
</head>
<body>
<div class="container-fluid">
<h1>Dashboard Demo</h1>
<div>
<button data-bind="click: addNewWidget">Add new widget</button>
</div>
<br>
<div data-bind="component: {name: 'dashboard-grid', params: $data}"></div>
</div>
<script type="text/javascript">
ko.components.register('dashboard-grid', {
viewModel: {
createViewModel: function (controller, componentInfo) {
var ViewModel = function (controller, componentInfo) {
var grid = null;
this.widgets = controller.widgets;
this.afterAddWidget = function (items) {
if (grid == null) {
grid = $(componentInfo.element).find('.grid-stack').gridstack({
auto: false
}).data('gridstack');
}
var item = _.find(items, function (i) { return i.nodeType == 1 });
grid.addWidget(item);
ko.utils.domNodeDisposal.addDisposeCallback(item, function () {
grid.removeWidget(item);
});
};
};
return new ViewModel(controller, componentInfo);
}
},
template:
[
'<div class="grid-stack" data-bind="foreach: {data: widgets, afterRender: afterAddWidget}">',
' <div class="grid-stack-item" data-bind="attr: {\'data-gs-x\': $data.x, \'data-gs-y\': $data.y, \'data-gs-width\': $data.width, \'data-gs-height\': $data.height, \'data-gs-auto-position\': $data.auto_position}">',
' <div class="grid-stack-item-content">',
//' <button data-bind="click: $root.deleteWidget">Delete me</button>',
' <div class="dropdown">',
' <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">Click me',
' <span class="caret"></span>',
' </button>',
' <ul class="dropdown-menu">',
' <li data-bind="click: $root.deleteWidget"><a tabindex="-1" href="#">Delete</a></li>',
' <li><a tabindex="-1" href="#">Change Color</a></li>',
' <li><a tabindex="-1" href="#">Copy to</a></li>',
' <li class="dropdown-submenu">',
' <a class="plan" tabindex="-1" href="#">New dropdown <span class="caret"></span></a>',
' <ul class="dropdown-menu">',
' <li><a tabindex="-1" href="#">2nd level dropdown</a></li>',
' <li><a tabindex="-1" href="#">2nd level dropdown</a></li>',
' <li class="dropdown-submenu">',
' <a class="test" href="#">Another dropdown <span class="caret"></span></a>',
' <ul class="dropdown-menu">',
' <li>3rd level dropdown</li>',
' <li>3rd level dropdown</li>',
' </ul>',
' </li>',
' </ul>',
' </li>',
' </ul>',
' </div>',
' </div>',
' </div>',
'</div> '
].join('')
});
$(function () {
var Controller = function (widgets) {
var self = this;
this.widgets = ko.observableArray(widgets);
this.addNewWidget = function (i) {
this.widgets.push({
x: 0,
y: 0,
id: 'widget'+i,
width: i== 0 ? 2 : 1, //Math.floor(1 + 3 * Math.random()),
height: i== 0 ? 2 : 1, //Math.floor(1 + 3 * Math.random()),
auto_position: true
});
return false;
};
this.deleteWidget = function (item) {
self.widgets.remove(item);
return false;
};
/**
this.toggleDropdownMenu(function(){
$('.dropdown-submenu a.test').on("click", function(e)
{
$(this).next('ul').toggle();
e.stopPropagation();
e.preventDefault();
});
});
**/
};
var widgets = [
{x: 0, y: 0, width: 2, height: 2, staticGrid: true },
{x: 2, y: 0, width: 1, height: 1},
{x: 3, y: 1, width: 2, height: 2},
{x: 1, y: 2, width: 1, height: 1}
];
var controller = new Controller(widgets);
ko.applyBindings(controller);
});
</script>
</body>
</html>
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;
}