UI Bootstrap control uib-carousel with custom buttons - javascript

I am trying to control the carousel through buttons, rather than the controls above the carousel (I will be hiding the chevron icons).
I inspected the chevron icon and found this in the source:
<a role="button" href="" class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">
<span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>
<span class="sr-only">previous</span>
</a>
I tried adding the attributes (except the class) to the button, but it does not work:
<button type="button" class="btn btn-default btn-block" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">Previous</button>
I am guessing it does not work because the button is not within the
uib-carousel, so it does not know what 'prev()' and 'isPrevDisabled()'
functions are. Can I reference the function, or create my own to control it?
Plnkr Demo
Another thing that I noticed, but it is off-topic, is if you double-click either the right or left chevron button (let's say the right), it only goes one slide to the right. And then if I click the left chevron, it moves to the right once and then moves to the left (when you click left chevron 2nd time). Any way to resolve this 'issue'? It should either move 2 slides on double-click, or discard the 2nd click and when opposite direction is clicked, perform that action properly.

The better way to do this is to use the template-url attribute and define your own carousel controls that way. I've done just that for my project (although I am stuck on getting the Next button to also fire a custom event in my controller as well).
<div class="col-xs-12 box-shadow" style="height: 50px; padding-top: 11px; background-color: #fff; z-index: 15;">Step {{ autoseq.wizardStep + 1 }} of 5</div>
<uib-carousel template-url="/tpl.html" active="autoseq.wizardStep" no-wrap="true" on-carousel-next="autoseq.onNext()" style="height: 395px;">
<uib-slide style="height: 395px; margin-top: 5px;" index="0">
...slide content..
</uib-slide>
</uib-carousel>
and my template is defined as such (in the same html file)
<script type="text/ng-template" id="/tpl.html">
<div class="carousel">
<div class="carousel-inner" ng-transclude></div>
<div class="carousel-controls">
<div class="carousel-control" style="display: table-cell; float: left; width: 30%;">
<a role="button" href class="left chevron-left" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">
<i class="fa fa-chevron-left"></i>
<span style="margin-left:5px;">Back</span>
</a>
</div>
<div style="display: table-cell; float: left; width: 40%;">
<ol class="carousel-indicators" ng-show="slides.length > 1">
<li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }">
</li>
</ol>
</div>
<div class="carousel-control" style="display: table-cell; float: left; width: 30%;">
<a role="button" href class="right chevron-right" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">
<span style="margin-right:5px;">Next</span>
<i class="fa fa-chevron-right"></i>
</a>
</div>
</div>
</div>
</script>

Heres a CSS Solution to manipulate the "arrow buttons" down to the position of your buttons. Took away the background gradient and placed your buttons inside the arrow buttons.
a.right.carousel-control {
position: absolute !important;
top: 100%;
width: 385px;
right: 16px;
height: 39px;
z-index: 2;
}
a.left.carousel-control {
position: absolute !important;
top: 100%;
width: 385px;
left: 16px;
height: 39px;
z-index: 2;
}
.carousel-control.left, .carousel-control.right {
background-image: none !important;
}
https://plnkr.co/edit/qlh8UOfa6RFbMa5BKGR2

I ran into same problem, had to create a custom directive.
.directive('carouselControls', ['$timeout', function($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$timeout(function() {
scope.slidesViewed = [];
scope.slidesRemaining = [];
var carouselScope = element.isolateScope();
scope.goNext = function() {
carouselScope.next();
};
scope.goPrev = function() {
carouselScope.prev();
};
scope.setActiveSlide = function(number) {
if (number < 0 || number > carouselScope.slides.length - 1) {
return;
}
var direction = (scope.getActiveSlide() > number) ? 'prev' : 'next';
carouselScope.select(carouselScope.slides[number], direction);
}
scope.getActiveSlide = function() {
var activeSlideIndex = carouselScope.slides.map(function(s) {
return s.slide.active;
}).indexOf(true);
console.log(activeSlideIndex);
return activeSlideIndex;
};
});
}
};
}]);
Here is working PLUNKR as well. Directive supports below 4 functions, simple call function using ng-click inside carousel-controls directive.
goNext()
goPrev()
setActiveSlide(slideIndex)
getActiveSlide()

Related

Dropdown menu click eventListener producing a different target element based on area clicked

I have a MaterializeCSS dropdown menu implemented with this HTML. The event listener works only if the dropdown item is clicked in the upper portion.
<div class="left">
<span id="notificationTotal" class="new badge red" style="display:none">
</span>
<a class="dropdown-trigger" href="#!" data-target="notificationsDropdown">
<i class="material-icons"> message</i>
</a>
</div>
<ul id="notificationsDropdown" class="dropdown-content">
</ul>
I'm using the following Javascript to populate the menu with notifications. This is working just fine.
// Reset Notification Dropdown
notificationsDropdown.innerHTML = '';
notifications.forEach(notif => {
const displayTime = moment(notif.date).fromNow();
let typeIcon = 'sms';
if (notif.type === 'chat') {
typeIcon = 'lock';
}
notificationsDropdown.innerHTML += `<li class="notification">
<a style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;font-size:14px" href="#" class="blue-text">
<span class="js-patientName">
${notif.contact.firstName} ${notif.contact.lastName}</span>
<span class="js-notificationPhone" style="display:none">${
notif.contact.phone
}</span>
<span class="js-patientId" style="display:none">${
notif.patientId
}</span>
<span class="js-patientDOB" style="display:none">${
notif.contact.birthDate
}</span>
<p style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;padding-top: 0;">
<i style="display: inline-flex; vertical-align:middle" class="tiny material-icons">${typeIcon}</i>
<span class="black-text" style="font-size:12px">
${displayTime}
</span>
</p>
</a></li>`;
});
notificationsDropdown.innerHTML += `<li class="divider" class="blue-text"></li><li>See All Notifications</li>`;
}
The dropdown gets populated and when a user clicks on a particular dropdown .li entry depending on the exact location they click, it may or not work. The user must click at the main top of the dropdown item.
This is the event listener code that extracts the values from the hidden span elements.
document
.querySelectorAll('#notificationsDropdown', '.li .a .notification')
.forEach(input =>
input.addEventListener('click', async e => {
// console.log('clicked', e.target);
console.log(e.target.parentNode);
const name = e.target.children[0].textContent.trim();
const phone = e.target.children[1].textContent.trim();
const patientId = e.target.children[2].textContent.trim();
const birthDate = e.target.children[3].textContent.trim();
console.log('patientid ', patientId);
const patient = {
name,
patientId,
phone,
birthDate
};
Is there a way I can rewrite the eventListener code to resolve this issue? Possibly instead of using e.target.children[insert_number_here].textContent I could use .closest('js-patientId') or similar ?
This is how the HTML is rendered to the page. This is an example of a single notification:
<ul
id="notificationsDropdown"
class="dropdown-content"
tabindex="0"
style="display: block; width: 177.297px; left: 1648.7px; top: 0px; height: 251px; transform-origin: 100% 0px; opacity: 1; transform: scaleX(1) scaleY(1);"
>
<li class="notification">
<a
style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;font-size:14px"
href="#"
class="blue-text"
>
<span class="js-patientName">ANDREW TAYLOR</span>
<span class="js-notificationPhone" style="display:none">
5555551212
</span>
<span class="js-patientId" style="display:none">
1
</span>
<span class="js-patientDOB" style="display:none">
1960-01-01
</span>
<p style="margin-top:0px;margin-bottom:0px;padding-bottom: 0;padding-top: 0;">
<i
style="display: inline-flex; vertical-align:middle"
class="tiny material-icons"
>
sms
</i>
<span class="black-text" style="font-size:12px">
2 hours ago
</span>
</p>
</a>
</li>
<li class="divider" />
<li>
<a href="/notifications" class="blue-text">
See All Notifications
</a>
</li>
</ul>;
For dropdowns, the better way to handle actions is through onChange event. So you could attach your eventHandler on onChange of dropDown itself rather than attaching onClick on each of dropDown items. So whenever you change the selected value the onChange event will be triggered and you can easily get the selected value.
I was able to remove the in each li. Then adjust the value assignment to use the following:
e.target.parentNode.children[0].textContent.trim();

Angular1.5.8, the component does not work

<div ng-app="myApp" ng-controller="controller" class="cell-list-box">
<div >
<button ng-click="changeArrayFun(1)">array1Button</button>
<button ng-click="changeArrayFun(2)">array2Button</button>
</div>
<ul class="cell_list_ul">
<li ng-repeat="item in show_array" class="cell_list">
<div class="course_cell">
<div class="img_box">
<a ng-if="item.course != 'course'&&item.coursetype ==1 "
target="_blank">
<div style="background-color: yellow; width: 20px; height: 20px" ></div>
</a>
<a ng-if="item.works_oldid"
target="_blank">
<div style="background-color: red; width: 20px; height: 20px" ></div>
</a>
</div>
</div>
</li>
</ul>
</div>
<script src="../angular.js"></script>
<script>
var ngmodule = angular.module('myApp',[]);
ngmodule.controller('controller', ['$scope', function ($scope) {
var array1 = [
{
"type":"coursewarp",
"coursetype":1,
},
];
var array2 = [
{
"ID":3194892,
"courseid":"0",
"taskid":34,
"works_oldid":"585be116e9c7a87881571958",
}
];
$scope.changeArrayFun = function (number) {
if(number === 1){
$scope.show_array = array1;
} else {
$scope.show_array = array2;
}
}
}]);
</script>
If I write all the code in my HTML. When I click the Button1, the yellow box will show. When I click the Button2, the red box will show. The code work well.I use the Angular component to write a "cell" component. IF I insert the cell code into the "cell" component, the code does not work.
The component templateUrl :
<div class="course_cell">
<div class="img_box">
<a ng-if="item.course != 'course'&&item.coursetype ==1 "
href="{{root_path}}/libs/v1/column/column.html?wrap_id={{item.wrap_id}}"
target="_blank">
<div style="background-color: yellow; width: 20px; height: 20px" ></div>
</a>
<a ng-if="item.works_oldid"
target="_blank">
<div style="background-color: red; width: 20px; height: 20px" ></div>
</a>
</div>
</div>
my component js ( test_cell.js ) :
function courseCellFactoryFun( app_name, root_path,api_set ) {
function courseCellComponentFun() {
var ctrl = this;
console.log(ctrl.cellData);
}
angular.module(app_name).component('courseCell', {
templateUrl:root_path + '/test_cell.html',
controller:courseCellComponentFun,
bindings:{
cellData:'='
}
})
}
The body will change :
<ul class="cell_list_ul">
<li ng-repeat="item in show_array" class="cell_list">
<course-cell cell-data="item"></course-cell>
</li>
</ul>
I add the code below at the end of the body.
<script src="./testCell/test_cell.js"></script>
<script>
courseCellFactoryFun('myApp','./testCell');
</script>
testCell is my component folder.
Then I run the demo, when I click the button, the colorBox does not show.
The default naming of a controller in a component in Angular 1.5+ is '$ctrl' so you must prefix this when accessing data and functions from your template e.g. :
<div class="course_cell">
<div class="img_box">
<a ng-if="$ctrl.item.course != 'course' && $ctrl.item.coursetype ==1 "
...

Angular Bootstrap UI - Only collapsing one element

I am using the angular bootstrap UI library:
https://angular-ui.github.io/bootstrap/
to create a collapsable feature on my site. The functionality itself is working, but it will expand/collapse every element I have the collapse feature on, instead of just the unique element that is clicked.
Here is my code:
var app = angular.module('someApp', ['ui.bootstrap']);
app.controller('collapseController', ['$scope', function ($scope) {
$scope.isCollapsed = true;
};
.passInfoDropdown {
margin-top: 10px;
margin-bottom: 10px;
margin-left: 5px;
font-weight: bold;
}
.dividerLine {
background-color: #DED7CF;
height: 2px;
}
.passInfoTableCellLeft {
width: 220px;
}
<div class="dividerLine"></div>
<div class="click" ng-click="isCollapsed = !isCollapsed">
<table>
<tr>
<td class="passInfoTableCellLeft"><div class="passInfoDropdown inline">TSA Information (optional)</div></td>
<td><i class="fa fa-chevron-down inline"></i></td>
</tr>
</table>
<div collapse="isCollapsed">
<div class="well well-lg">Some content</div>
</div>
</div>
<div class="dividerLine"></div>
<div class="click" ng-click="isCollapsed = !isCollapsed">
<table>
<tr>
<td class="passInfoTableCellLeft"><div class="passInfoDropdown inline">Loyalty Programs (optional)</div></td>
<td><i class="fa fa-chevron-down inline"></i></td>
</tr>
</table>
<div collapse="isCollapsed">
<div class="well well-lg">Some content</div>
</div>
</div>
How can I make it so just the unique element i click collapses?
At the moment both isCollapsed refer to the same propertie on your $scope object. If you want those to be separed, I see two options.
Two properties in the scope
$scope.isCollapsedContent1 = true;
$scope.isCollapsedContent2 = true;
or
$scope.isCollapsed = {};
$scope.isCollapsed['content1'] = true;
$scope.isCollapsed['content2'] = true;
Two ng-model
If you do not need to know whether the content is collapsed or not in your controller, you might as well put the isCollapsed value into your html and remove the propertie of your $scope.
<div class="click" ng-click="isCollapsedContent1 = !isCollapsedContent1" ng-model="isCollapsedContent1" ng-init="isCollapsedContent1=true">
Brandon, HI there. It also looks (in the full snippet code) like you have left out these....
<html ng-app="ui.bootstrap.demo">
and
<div ng-controller="CollapseDemoCtrl">
Have a look at the working Fiddle.

How to detect if content have more then one line in Angular?

I have template like this:
<span class="collapsibleBox">
<a ng-show="visible" ng-click="hide()" class="minus">
<i class="fa fa-minus-square-o"></i>
</a>
<a ng-show="!visible" ng-click="show()" class="plus">
<i class="fa fa-plus-square-o"></i>
</a>
<div ng-show="visible || preview" ng-class="preview ? 'previewBox' : 'contentBlock'" ng-bind-html="htmlContent"></div>
</span>
with css:
.api .previewBox {
height: 1.25em;
overflow: hidden;
}
And I need to not show plus and minus sign icons when div with class htmlContent is only one line, how can I do this in Angular.
I came up with this directive:
module.directive('oneLine', function($rootScope) {
return {
restrict: 'A',
scope: {
text: '=',
oneLine: '='
},
link: function($scope, $element, $attrs) {
angular.element(window).bind('resize', function() {
var height = $element.html('M').show().height();
$element.html($scope.text);
$scope.oneLine = height == $element.height();
$element.hide();
if (!$rootScope.$$phase) {
$rootScope.$apply(); // $scope throw "digest in progress" exception
}
}).trigger('resize');
}
};
});
and use it like this:
<span class="collapsibleBox">
<span ng-hide="hideButtons">
<a ng-show="visible" ng-click="hide()" class="minus">
<i class="fa fa-minus-square-o"></i>
</a>
<a ng-show="!visible" ng-click="show()" class="plus">
<i class="fa fa-plus-square-o"></i>
</a>
</span>
<div class="contentBlock" one-line="hideButtons" text="htmlContent"></div>
<div ng-show="visible || preview" ng-class="preview ? 'previewBox' : 'contentBlock'" ng-bind-html="htmlContent"></div>
</span>
You can set your CSS to only show one line, but show ellipses when the text overflows. You don't get +/- buttons, but it might be usable for you.
See http://www.w3schools.com/cssref/css3_pr_text-overflow.asp

Odd Angular/Bootstrap Dropdown Behavior

I've got an application requiring a series of dropdowns. The functionality requires me to be able to switch between normal dropdown behavior and multi-select behavior.
I have made slight modifications to the ui.boostrap.dropdown from the Angular Directives for Bootstrap (see Dropdown). Everything works well, except for a gray bar after clicking (looks like the :hover css stays active after clicking) when in Multiselect mode.
When I toggle off then back on, the highlight goes away, as if the hover event has somehow completed.
Process:
Open the dropdown
Click Multiselect
Move the mouse and Multiselect stays highlighted, as if the ":focus" tag is not removed.
Visually:
The mouse is over 345 in this image, Multiselect should not be highlighted.
Angular HTML for dropdown:
<div ng-controller="DropdownCompanyController as vm">
<div class="btn-group u-front_dropdown-wide" dropdown is-open="vm.isopen">
<button type="button" class="btn btn-default dropdown-toggle" dropdown-toggle>
{{ vm.selected }} <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" ng-click="vm.checkMultiSelect($event)">
<li ng-repeat="company in vm.companies"
ng-class="{ selected : company.selected }">
{{ company.name }}
</li>
<li class="divider"></li>
<li class="ng-scope" ng-class="{selected : vm.multi.select }">
<a href="#"
ng-click="vm.event.multiselect()"
ng-class="{ multi: vm.multi.select }">Multiselect</a>
<button
ng-hide="!vm.multi.select"
ng-class="{ multi_button: vm.multi.select }"
ng-click="vm.event.close_dropdown($event)">Close</button>
</li>
</ul>
</div>
</div>
Here's the event where the click on an element is handled:
vm.event = {};
vm.event.select = function(company) {
if (!vm.multi.select) {
clearCompanies(false);
company.selected = true;
vm.selected = company.name;
vm.isopen = false;
} else {
if (company.name !== vm.defaultCompany) {
company.selected = !company.selected;
vm.selected = vm.multi.title + countCompanies();
}
}
};
Link to Plunker.
I have had no luck tracking this down and my instinct is that the issue is in the CSS, but it's standard bootstrap CSS. Any assistance would be appreciated.
If you just dont want any item to have the highlight then in your own custom app.css file override the focus state with white:
.dropdown-menu > li > a:focus {
background-color: white;
}
Without doing a custom bootstrap build just add the hover following the focus to to keep the grey highlight:
.dropdown-menu > li > a:hover {
background-color: lightgray;
}

Categories

Resources