I'm using angular-ui bootstrap time picker.
Code for start time
<button type="button" class="btn btn-default dropdown-toggle" ng-click="openStartTime($event, 'time')">
<i class="glyphicon glyphicon-time"></i>
</button>
<div dropdown is-open="timepickerOpened1">
<span class="dropdown-menu" role="menu">
<timepicker ng-model="meetingInfo.startTime" ng-change="changed()" show-meridian="true"></timepicker>
</span>
</div>
and end time
<button type="button" class="btn btn-default dropdown-toggle" ng-click="openEndTime($event, 'time')">
<i class="glyphicon glyphicon-time"></i>
</button>
<div dropdown is-open="timepickerOpened2">
<span class="timePickerCls dropdown-menu" role="menu">
<timepicker ng-model="meetingInfo.endTime" ng-change="changed()" show-meridian="true"></timepicker>
</span>
</div>
My Controller Code..
$scope.openStartTime = function ($event, type) {
$event.preventDefault();
$event.stopPropagation();
if (type == 'date') {
$scope.datepickerOpened1 = true;
$scope.timepickerOpened1 = false;
} else if (type == 'time') {
$scope.timepickerOpened1 = true;
$scope.datepickerOpened2 = false;
}
};
$scope.openEndTime = function ($event, type) {
$event.preventDefault();
$event.stopPropagation();
if (type == 'date') {
$scope.datepickerOpened2 = true;
$scope.timepickerOpened2 = false;
} else if (type == 'time') {
$scope.timepickerOpened2 = true;
$scope.datepickerOpened2 = false;
}
};
$scope.format = 'hh:mm a';
My Question :How can I add validation to check whether the End Time entered is always greater than the Start Time?Thanks
You can add statement to change function like
if ($scope.end <= $scope.start) {
$scope.end = new Date($scope.start.getTime() + $scope.mstep * 60000)
}
Please see demo below
var app = angular.module('app', ['ui.bootstrap']);
app.controller('homeCtrl', function($scope, $log) {
$scope.hstep = 1;
$scope.mstep = 1;
$scope.start = new Date();
$scope.end = new Date();
$scope.changed = function() {
if ($scope.end <= $scope.start) {
$scope.end = new Date($scope.start.getTime() + $scope.mstep * 60000)
}
$log.log('Event starts at: ' + $scope.start);
$log.log('Event finish at: ' + $scope.end);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
Start:
<timepicker ng-model="start" ng-change="changed()" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></timepicker>
End:
<timepicker ng-model="end" ng-change="changed()" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></timepicker>
</span>
</div>
</div>
Related
Please help me with this.
What's wrong? Thanks!
I had the same problem and it fixed adding this js part which I extracted from here
$(document).ready(function() {
$('.stepper').activateStepper();
})
function validateStepOne() {
// Extract the checked checkboxes from the first step
if($('.step').first().find('input[type="checkbox"]:checked').length)
return true;
return false;
}
function validateStepThree() {
var validation = true;
if($('.step:nth-child(3) input[type="text"]').val().indexOf('materialize') === -1)
validation = false;
if($('.step:nth-child(3) input[type="checkbox"]:checked').length === 0)
validation = false;
return validation;
}
function nextStepThreeHandler() {
if(validateStepThree())
$('.stepper').nextStep();
else {
$('.stepper ').destroyFeedback(); $('.stepper').getStep($('.stepper').getActiveStep()).addClass('wrong');
}
}
/* Materializecss Stepper - By Kinark 2016
// https://github.com/Kinark/Materialize-stepper
// JS v2.1.3
*/
var validation = $.isFunction($.fn.valid) ? 1 : 0;
$.fn.isValid = function() {
if(validation){
return this.valid();
} else {
return true;
}
};
if (validation) {
$.validator.setDefaults({
errorClass: 'invalid',
validClass: "valid",
errorPlacement: function (error, element) {
if(element.is(':radio') || element.is(':checkbox')) {
error.insertBefore($(element).parent());
} else {
error.insertAfter(element); // default error placement.
// element.closest('label').data('error', error);
// element.next().attr('data-error', error);
}
},
success: function (element) {
if(!$(element).closest('li').find('label.invalid:not(:empty)').length){
$(element).closest('li').removeClass('wrong');
}
}
});
// When parallel stepper is defined we need to consider invisible and
// hidden fields
if($('.stepper.parallel').length) $.validator.setDefaults({ignore:''});
}
$.fn.getActiveStep = function() {
var active = this.find('.step.active');
return $(this.children('.step:visible')).index($(active))+1;
};
$.fn.activateStep = function(callback) {
if($(this).hasClass('step')) return;
var stepper = $(this).closest('ul.stepper');
stepper.find('>li').removeAttr("data-last");
if(window.innerWidth < 993 || !stepper.hasClass('horizontal')) {
$(this).addClass("step").stop().slideDown(400, function(){
$(this).css({'height':'auto', 'margin-bottom': '','display': 'inherit'});if(callback)callback();
stepper.find('>li.step').last().attr('data-last', 'true');
});
} else {
$(this).addClass("step").stop().css({'width':'0%','display': 'inherit'}).animate({width:'100%'}, 400, function(){
$(this).css({'height':'auto', 'margin-bottom': '','display': 'inherit'});if(callback)callback();
stepper.find('>li.step').last().attr('data-last', 'true');
});
}
};
$.fn.deactivateStep = function(callback) {
if(!$(this).hasClass('step')) return;
var stepper = $(this).closest('ul.stepper');
stepper.find('>li').removeAttr("data-last");
if(window.innerWidth < 993 || !stepper.hasClass('horizontal')) {
$(this).stop().css({'transition':'none', '-webkit-transition':'margin-bottom none'}).slideUp(400, function(){
$(this).removeClass("step").css({'height':'auto','margin-bottom':'','transition':'margin-bottom .4s','-webkit-transition':'margin-bottom .4s'});
if(callback)callback();
stepper.find('>li').removeAttr("data-last");
stepper.find('>li.step').last().attr('data-last', 'true');
});
} else {
$(this).stop().animate({width:'0%'}, 400, function(){
$(this).removeClass("step").hide().css({'height':'auto', 'margin-bottom': '','display': 'none', 'width': ''});
if(callback)callback();
stepper.find('>li.step').last().attr('data-last', 'true');
});
}
};
$.fn.showError = function(error) {
if(validation) {
var name = this.attr('name');
var form = this.closest('form');
var obj = {};
obj[name] = error;
form.validate().showErrors(obj);
this.closest('li').addClass('wrong');
} else {
this.removeClass('valid').addClass('invalid');
this.next().attr('data-error', error);
}
};
$.fn.activateFeedback = function() {
var active = this.find('.step.active:not(.feedbacking)').addClass('feedbacking').find('.step-content');
active.prepend('<div class="wait-feedback"> <div class="preloader-wrapper active"> <div class="spinner-layer spinner-blue"> <div class="circle-clipper left"> <div class="circle"></div></div><div class="gap-patch"> <div class="circle"></div></div><div class="circle-clipper right"> <div class="circle"></div></div></div><div class="spinner-layer spinner-red"> <div class="circle-clipper left"> <div class="circle"></div></div><div class="gap-patch"> <div class="circle"></div></div><div class="circle-clipper right"> <div class="circle"></div></div></div><div class="spinner-layer spinner-yellow"> <div class="circle-clipper left"> <div class="circle"></div></div><div class="gap-patch"> <div class="circle"></div></div><div class="circle-clipper right"> <div class="circle"></div></div></div><div class="spinner-layer spinner-green"> <div class="circle-clipper left"> <div class="circle"></div></div><div class="gap-patch"> <div class="circle"></div></div><div class="circle-clipper right"> <div class="circle"></div></div></div></div></div>');
};
$.fn.destroyFeedback = function() {
var active = this.find('.step.active.feedbacking');
if(active) {
active.removeClass('feedbacking');
active.find('.wait-feedback').remove();
}
return true;
};
$.fn.resetStepper = function(step) {
if(!step) step = 1;
var form = $(this).closest('form');
$(form)[0].reset();
Materialize.updateTextFields();
return $(this).openStep(step);
};
$.fn.submitStepper = function(step) {
var form = this.closest('form');
if(form.isValid()) {
form.submit();
}
};
$.fn.nextStep = function(callback, activefb, e) {
var stepper = this;
var settings = $(stepper).data('settings');
var form = this.closest('form');
var active = this.find('.step.active');
var next = $(this.children('.step:visible')).index($(active))+2;
var feedback = active.find('.next-step').length > 1 ? (e ? $(e.target).data("feedback") : undefined) : active.find('.next-step').data("feedback");
// If the stepper is parallel, we want to validate the input of the current active step. Not all elements.
if((settings.parallel && $(active).validateStep()) || (!settings.parallel && form.isValid())) {
if(feedback && activefb) {
if(settings.showFeedbackLoader) stepper.activateFeedback();
return window[feedback].call();
}
active.removeClass('wrong').addClass('done');
this.openStep(next, callback);
return this.trigger('nextstep');
} else {
return active.removeClass('done').addClass('wrong');
}
};
$.fn.prevStep = function(callback) {
var active = this.find('.step.active');
if(active.hasClass('feedbacking')) return;
var prev = $(this.children('.step:visible')).index($(active));
active.removeClass('wrong');
this.openStep(prev, callback);
return this.trigger('prevstep');
};
$.fn.openStep = function(step, callback) {
var settings = $(this).closest('ul.stepper').data('settings');
var $this = this;
var step_num = step - 1;
step = this.find('.step:visible:eq('+step_num+')');
if(step.hasClass('active')) return;
var active = this.find('.step.active');
var next;
var prev_active = next = $(this.children('.step:visible')).index($(active));
var order = step_num > prev_active ? 1 : 0;
if(active.hasClass('feedbacking')) $this.destroyFeedback();
active.closeAction(order);
step.openAction(order, function(){
if(settings.autoFocusInput) step.find('input:enabled:visible:first').focus();
$this.trigger('stepchange').trigger('step'+(step_num+1));
if(step.data('event')) $this.trigger(step.data('event'));
if(callback)callback();
});
};
$.fn.closeAction = function(order, callback) {
var closable = this.removeClass('active').find('.step-content');
if(window.innerWidth < 993 || !this.closest('ul').hasClass('horizontal')) {
closable.stop().slideUp(300,"easeOutQuad", callback);
} else {
if(order==1) {
closable.animate({left: '-100%'},function(){closable.css({display: 'none', left: '0%'}, callback);});
} else {
closable.animate({left: '100%'},function(){closable.css({display: 'none', left: '0%'}, callback);});
}
}
};
$.fn.openAction = function(order, callback) {
var openable = this.removeClass('done').addClass('active').find('.step-content');
if(window.innerWidth < 993 || !this.closest('ul').hasClass('horizontal')) {
openable.slideDown(300,"easeOutQuad", callback);
} else {
if(order==1) {
openable.css({left: '100%', display: 'block'}).animate({left: '0%'}, callback);
} else {
openable.css({left: '-100%', display: 'block'}).animate({left: '0%'}, callback);
}
}
};
$.fn.activateStepper = function(options) {
var settings = $.extend({
linearStepsNavigation: true,
autoFocusInput: true,
showFeedbackLoader: true,
autoFormCreation: true,
parallel: false // By default we don't assume the stepper is parallel
}, options);
$(document).on('click', function(e){
if(!$(e.target).parents(".stepper").length){
$('.stepper.focused').removeClass('focused');
}
});
$(this).each(function(){
var $stepper = $(this);
if(!$stepper.parents("form").length && settings.autoFormCreation) {
var method = $stepper.data('method');
var action = $stepper.data('action');
var method = (method ? method : "GET");
action = (action ? action : "?");
$stepper.wrap( '<form action="'+action+'" method="'+method+'"></form>' );
}
$stepper.data('settings', {linearStepsNavigation: settings.linearStepsNavigation,autoFocusInput: settings.autoFocusInput,showFeedbackLoader:settings.showFeedbackLoader, parallel:$stepper.hasClass('parallel')});
$stepper.find('li.step.active').openAction(1);
$stepper.find('>li').removeAttr("data-last");
$stepper.find('>li.step').last().attr('data-last', 'true');
$stepper.on("click", '.step:not(.active)', function () {
var object = $($stepper.children('.step:visible')).index($(this));
if($stepper.data('settings').parallel && validation) { // Invoke parallel stepper behaviour
$(this).addClass('temp-active');
$stepper.validatePreviousSteps()
$stepper.openStep(object + 1);
$(this).removeClass('temp-active');
} else if(!$stepper.hasClass('linear')) {
$stepper.openStep(object+1);
} else if(settings.linearStepsNavigation) {
var active = $stepper.find('.step.active');
if($($stepper.children('.step:visible')).index($(active))+1 == object) {
$stepper.nextStep(undefined, true, undefined);
} else if ($($stepper.children('.step:visible')).index($(active))-1 == object) {
$stepper.prevStep(undefined);
}
}
}).on("click", '.next-step', function(e) {
e.preventDefault();
$stepper.nextStep(undefined, true, e);
}).on("click", '.previous-step', function(e) {
e.preventDefault();
$stepper.prevStep(undefined);
}).on("click", "button:submit:not(.next-step, .previous-step)", function (e) {
e.preventDefault();
feedback = e ? $(e.target).data("feedback") : undefined;
var form = $stepper.closest('form');
if(form.isValid()) {
if(feedback) {
stepper.activateFeedback();
return window[feedback].call();
}
form.submit();
}
}).on("click", function () {
$('.stepper.focused').removeClass('focused');
$(this).addClass('focused');
});
});
};
/**
* Return the step element on given index.
*
* #param step, index of the step to be returned
* #returns {*}, the step requested
*/
$.fn.getStep = function(step) {
var settings = $(this).closest('ul.stepper').data('settings');
var $this = this;
var step_num = step - 1;
step = this.find('.step:visible:eq('+step_num+')');
return step;
};
/**
* Run validation over all previous steps from the steps this
* function is called on.
*/
$.fn.validatePreviousSteps = function() {
var active = $(this).find('.step.temp-active');
var index = $(this.children('.step')).index($(active));
// We assume that the validator is set to ignore nothing.
$(this.children('.step')).each(function(i) {
if (i >= index) {
$(this).removeClass('wrong done');
} else {
$(this).validateStep();
}
});
};
/**
* Validate the step that this function is called on.
*/
$.fn.validateStep = function() {
var stepper = this.closest('ul.stepper');
var form = this.closest('form');
var step = $(this);
// Retrieve the custom validator for that step if exists.
var validator = step.find('.next-step').data("validator");
if(this.validateStepInput()) { // If initial base validation succeeded go on
if(validator) { // If a custom validator is given also call that validator
if (window[validator].call()) {
step.removeClass('wrong').addClass('done');
return true;
}
else {
step.removeClass('done').addClass('wrong');
return false;
}
}
step.removeClass('wrong').addClass('done');
return true;
} else {
step.removeClass('done').addClass('wrong');
return false;
}
};
/**
* Uses the validation variable set by the stepper constructor
* to run standard validation on the current step.
* #returns {boolean}
*/
$.fn.validateStepInput = function() {
var valid = true;
if (validation) {
// Find all input fields dat need validation in current step.
$(this).find('input.validate').each(function() {
if (!$(this).valid()) {
valid = false;
return false;
}
});
}
return valid;
};
<head>
<!-- Materializecss compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/css/materialize.min.css">
<!--Import Google Icon Font-->
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/materialize-stepper#2.1.4/materialize-stepper.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<!-- Materializecss compiled and minified JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.7/js/materialize.min.js"></script>
<!-- jQueryValidation Plugin -->
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/jquery.validate.min.js"></script>
</head>
<body class="container">
<form>
<ul class="stepper parallel horizontal">
<li class="step active">
<div class="step-title waves-effect waves-dark">Step 1</div>
<div class="step-content">
<div class="row">
<div class='form-field col s12'>
<p>Step with custom validation</p>
<span>For this step we want to have a custom validator that checks if if at least one checkbox is checked</span>
<p>
<input name='checkbox1' type="checkbox" class="filled-in"
id="checkbox1" value='checkbox1'/>
<label for="checkbox1">Checkbox 1</label>
</p>
<p>
<input name='checkbox2' type="checkbox" class="filled-in"
id="checkbox2" value='checkbox2'/>
<label for="checkbox2">Checkbox 2</label>
</p>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn next-step" data-validator="validateStepOne">CONTINUE</button>
<button class="waves-effect waves-dark btn-flat previous-step">BACK</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect waves-dark">Step 2</div>
<div class="step-content">
<div class="row">
<div class='form-field col s12'>
<p>Step with no custom validation</p>
<div class="input-field col s12">
<input type="text" name="textfield" class="required validate"/>
<label for="textfield">Random textfield</label>
</div>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn next-step">CONTINUE</button>
<button class="waves-effect waves-dark btn-flat previous-step">BACK</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect waves-dark">Step 3</div>
<div class="step-content">
<div class="row">
<div class='form-field'>
<p>Step with feedback and custom validation</p>
<p>
<input name='checkbox3' type="checkbox" class="filled-in"
id="checkbox3" value='checkbox3'/>
<label for="checkbox3">Checkbox 3</label>
</p>
<p>
<input name='checkbox4' type="checkbox" class="filled-in"
id="checkbox4" value='checkbox4'/>
<label for="checkbox4">Checkbox 4</label>
</p>
</div>
<div class="input-field col s12">
<input type="text" id="textfield2" name="textfield2" class="required validate"/>
<label for="textfield2">This field should contain the word materialize</label>
</div>
</div>
<div class="step-actions">
<button class="waves-effect waves-dark btn next-step" data-feedback="nextStepThreeHandler" data-validator="validateStepThree">SUBMIT</button>
<button class="waves-effect waves-dark btn-flat previous-step">BACK</button>
</div>
</div>
</li>
<li class="step">
<div class="step-title waves-effect waves-dark">Step 4</div>
<div class="step-content">
<div class="row">
<div class='form-field'>
<p>Submit phase</p>
</div>
</div>
<div class="step-actions">
<input type="submit" class="waves-effect waves-dark btn next-step" value="SUBMIT"/>
<button class="waves-effect waves-dark btn-flat previous-step">BACK</button>
</div>
</div>
</li>
</ul>
</form>
</body>
Bulma dropdown doesn't seem to toggle on click. Below is the code snippet from the documentation:https://bulma.io/documentation/components/dropdown/
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.0/css/bulma.min.css" rel="stylesheet"/>
<div class="dropdown is-active">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
<span>Dropdown button</span>
<span class="icon is-small">
<i class="fa fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a href="#" class="dropdown-item">
Dropdown item
</a>
<a class="dropdown-item">
Other dropdown item
</a>
<a href="#" class="dropdown-item is-active">
Active dropdown item
</a>
<a href="#" class="dropdown-item">
Other dropdown item
</a>
<hr class="dropdown-divider">
<a href="#" class="dropdown-item">
With a divider
</a>
</div>
</div>
</div>
You need to toggle the class is-active using JavaScript. When .dropdown has .is-active it changes the display of .dropdown-menu from none to block.
Here is a basic way to implement it.
var dropdown = document.querySelector('.dropdown');
dropdown.addEventListener('click', function(event) {
event.stopPropagation();
dropdown.classList.toggle('is-active');
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.0/css/bulma.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
<div class="dropdown">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
<span>Dropdown button</span>
<span class="icon is-small">
<!--fontawesome required for the icon-->
<i class="fa fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a href="#" class="dropdown-item">
Dropdown item
</a>
<a class="dropdown-item">
Other dropdown item
</a>
<a href="#" class="dropdown-item is-active">
Active dropdown item
</a>
<a href="#" class="dropdown-item">
Other dropdown item
</a>
<hr class="dropdown-divider">
<a href="#" class="dropdown-item">
With a divider
</a>
</div>
</div>
</div>
Here's a full solution that has worked well for me using vanilla JS and making sure dropdowns close when you click out of them or press the Esc key.
// Get all dropdowns on the page that aren't hoverable.
const dropdowns = document.querySelectorAll('.dropdown:not(.is-hoverable)');
if (dropdowns.length > 0) {
// For each dropdown, add event handler to open on click.
dropdowns.forEach(function(el) {
el.addEventListener('click', function(e) {
e.stopPropagation();
el.classList.toggle('is-active');
});
});
// If user clicks outside dropdown, close it.
document.addEventListener('click', function(e) {
closeDropdowns();
});
}
/*
* Close dropdowns by removing `is-active` class.
*/
function closeDropdowns() {
dropdowns.forEach(function(el) {
el.classList.remove('is-active');
});
}
// Close dropdowns if ESC pressed
document.addEventListener('keydown', function (event) {
let e = event || window.event;
if (e.key === 'Esc' || e.key === 'Escape') {
closeDropdowns();
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" rel="stylesheet">
<body style="margin: 2rem">
<div class="dropdown">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-ui-actions">
<span>Actions</span>
<span class="icon is-small">
<i class="fas fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-ui-actions" role="menu">
<div class="dropdown-content">
<a href="#" class="dropdown-item">
Option 1
</a>
<a href="#" class="dropdown-item">
Option 2
</a>
<a href="#" class="dropdown-item">
Option 3
</a>
</div>
</div>
</div>
</body>
correction of te answer above:
I added the closeDropdowns() function, because when we click in a second menu, both menus stay opened.
// Get all dropdowns on the page that aren't hoverable.
const dropdowns = document.querySelectorAll('.dropdown:not(.is-hoverable)');
if (dropdowns.length > 0) {
// For each dropdown, add event handler to open on click.
dropdowns.forEach(function(el) {
el.addEventListener('click', function(e) {
closeDropdowns();
e.stopPropagation();
el.classList.toggle('is-active');
});
});
// If user clicks outside dropdown, close it.
document.addEventListener('click', function(e) {
closeDropdowns();
});
}
/*
* Close dropdowns by removing `is-active` class.
*/
function closeDropdowns() {
dropdowns.forEach(function(el) {
el.classList.remove('is-active');
});
}
// Close dropdowns if ESC pressed
document.addEventListener('keydown', function (event) {
let e = event || window.event;
if (e.key === 'Esc' || e.key === 'Escape') {
closeDropdowns();
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" rel="stylesheet">
<body style="margin: 2rem">
<div class="dropdown">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-ui-actions">
<span>Actions</span>
<span class="icon is-small">
<i class="fas fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-ui-actions" role="menu">
<div class="dropdown-content">
<a href="#" class="dropdown-item">
Option 1
</a>
<a href="#" class="dropdown-item">
Option 2
</a>
<a href="#" class="dropdown-item">
Option 3
</a>
</div>
</div>
</div>
</body>
I sniffed the javascript source listener on the docs page (I don't know why they don't make this more obvious tbh) and here it is for anyone else who may benefit.
Please note below I commented out a cookies aspect which I did not have defined and was throwing an error and seemed unimportant for my purposes.
"use strict";
document.addEventListener("DOMContentLoaded", function () {
// Search
var setSearchToggle = function setSearchToggle() {
var icon = document.getElementById("searchIcon");
var search = document.getElementById("search");
var input = document.getElementById("algoliaSearch");
if (!icon) {
return;
}
icon.addEventListener("click", function (event) {
search.classList.toggle("bd-is-visible");
if (search.classList.contains("bd-is-visible")) {
algoliaSearch.focus();
}
});
window.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
return search.classList.remove("bd-is-visible");
}
});
};
setSearchToggle();
// Navbar
var setNavbarVisibility = function setNavbarVisibility() {
var navbar = document.getElementById("navbar");
if (!navbar) {
return;
}
var cs = getComputedStyle(navbar);
var paddingX = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);
var brand = navbar.querySelector(".navbar-brand");
var end = navbar.querySelector(".navbar-end");
var search = navbar.querySelector(".bd-search");
var original = document.getElementById("navbarStartOriginal");
var clone = document.getElementById("navbarStartClone");
var rest = navbar.clientWidth - paddingX - brand.clientWidth - end.clientWidth - search.clientWidth;
var space = original.clientWidth;
var all = document.querySelectorAll("#navbarStartClone > .bd-navbar-item");
var base = document.querySelectorAll("#navbarStartClone > .bd-navbar-item-base");
var more = document.querySelectorAll("#navbarStartOriginal > .bd-navbar-item-more");
var dropdown = document.querySelectorAll("#navbarStartOriginal .bd-navbar-dropdown > .navbar-item");
var count = 0;
var totalWidth = 0;
var showMore = function showMore() {};
var hideMore = function hideMore() {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = all[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var item = _step.value;
totalWidth += item.offsetWidth;
if (totalWidth > rest) {
break;
}
count++;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var howManyMore = Math.max(0, count - base.length);
if (howManyMore > 0) {
for (var i = 0; i < howManyMore; i++) {
more[i].classList.add("bd-is-visible");
dropdown[i].classList.add("bd-is-hidden");
}
}
for (var j = howManyMore; j < more.length; j++) {
more[j].classList.remove("bd-is-visible");
}
for (var k = howManyMore; k < dropdown.length; k++) {
dropdown[k].classList.remove("bd-is-hidden");
}
};
setNavbarVisibility();
// Cookies
// var cookieBookModalName = "bulma_closed_book_modal";
// var cookieBookModal = Cookies.getJSON(cookieBookModalName) || false;
// Dropdowns
var $dropdowns = getAll(".dropdown:not(.is-hoverable)");
if ($dropdowns.length > 0) {
$dropdowns.forEach(function ($el) {
$el.addEventListener("click", function (event) {
event.stopPropagation();
$el.classList.toggle("is-active");
});
});
document.addEventListener("click", function (event) {
closeDropdowns();
});
}
function closeDropdowns() {
$dropdowns.forEach(function ($el) {
$el.classList.remove("is-active");
});
}
// Toggles
var $burgers = getAll(".navbar-burger");
if ($burgers.length > 0) {
$burgers.forEach(function ($el) {
if (!$el.dataset.target) {
return;
}
$el.addEventListener("click", function () {
var target = $el.dataset.target;
var $target = document.getElementById(target);
$el.classList.toggle("is-active");
$target.classList.toggle("is-active");
});
});
}
// Modals
var rootEl = document.documentElement;
var $modals = getAll(".modal");
var $modalButtons = getAll(".modal-button");
var $modalCloses = getAll(".modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button");
if ($modalButtons.length > 0) {
$modalButtons.forEach(function ($el) {
$el.addEventListener("click", function () {
var target = $el.dataset.target;
openModal(target);
});
});
}
if ($modalCloses.length > 0) {
$modalCloses.forEach(function ($el) {
$el.addEventListener("click", function () {
closeModals();
});
});
}
function openModal(target) {
var $target = document.getElementById(target);
rootEl.classList.add("is-clipped");
$target.classList.add("is-active");
}
function closeModals() {
rootEl.classList.remove("is-clipped");
$modals.forEach(function ($el) {
$el.classList.remove("is-active");
});
}
document.addEventListener("keydown", function (event) {
var e = event || window.event;
if (e.keyCode === 27) {
closeModals();
closeDropdowns();
}
});
// Clipboard
var $highlights = getAll(".highlight");
var itemsProcessed = 0;
if ($highlights.length > 0) {
$highlights.forEach(function ($el) {
var copyEl = '<button class="button bd-copy">Copy</button>';
var expandEl = '<button class="button is-small bd-expand">Expand</button>';
$el.insertAdjacentHTML("beforeend", copyEl);
var $parent = $el.parentNode;
if ($parent && $parent.classList.contains("bd-is-more")) {
var showEl = '<button class="button is-small bd-show"><span class="icon"><i class="fas fa-code"></i></span><strong>Show code</strong></button>';
$parent.insertAdjacentHTML("afterbegin", showEl);
} else if ($el.firstElementChild.scrollHeight > 480 && $el.firstElementChild.clientHeight <= 480) {
$el.insertAdjacentHTML("beforeend", expandEl);
}
itemsProcessed++;
if (itemsProcessed === $highlights.length) {
addHighlightControls();
}
});
}
function addHighlightControls() {
var $highlightButtons = getAll(".highlight .bd-copy, .highlight .bd-expand");
$highlightButtons.forEach(function ($el) {
$el.addEventListener("mouseenter", function () {
$el.parentNode.classList.add("bd-is-hovering");
$el.parentNode.parentNode.classList.add("bd-is-hovering");
});
$el.addEventListener("mouseleave", function () {
$el.parentNode.classList.remove("bd-is-hovering");
$el.parentNode.parentNode.classList.remove("bd-is-hovering");
});
});
var $highlightExpands = getAll(".bd-snippet .bd-expand");
$highlightExpands.forEach(function ($el) {
$el.addEventListener("click", function () {
$el.parentNode.firstElementChild.style.maxHeight = "none";
});
});
var $highlightShows = getAll(".bd-snippet .bd-show");
$highlightShows.forEach(function ($el) {
$el.addEventListener("click", function () {
var text = $el.querySelector("strong").textContent;
var newText = text === "Show code" ? "Hide code" : "Show code";
$el.querySelector("strong").textContent = newText;
$el.parentNode.classList.toggle("bd-is-more-clipped");
});
});
}
setTimeout(function () {
new Clipboard(".bd-copy", {
target: function target(trigger) {
return trigger.previousElementSibling.firstElementChild;
}
});
new Clipboard(".bd-clipboard");
}, 100);
// Events
var resizeTimer = void 0;
function handleResize() {
window.clearTimeout(resizeTimer);
resizeTimer = window.setTimeout(function () {
setNavbarVisibility();
}, 10);
}
window.addEventListener("resize", handleResize);
// Utils
function getAll(selector) {
var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;
return Array.prototype.slice.call(parent.querySelectorAll(selector), 0);
}
});
I am looking for a component like this to be included in my project:
http://geodan.github.io/duallistbox/sample-100.html
I want to install it with npm.
The problem is that I tested some of the examples which are over there, but without success (I get exceptions, or there is no npm, only bower)
These are the examples I tested.
https://github.com/alexklibisz/angular-dual-multiselect-directive
https://github.com/frapontillo/angular-bootstrap-duallistbox
http://www.bootply.com/mRcBel7RWm
Any recommendations about one with AngularJs, Bootstrap, and npm?
This might solve your requirement: Dual list Box
app.js
angular.module('plunker', [])
.controller('MainCtrl', function($scope, utils) {
$scope.list1 = [],
$scope.list2 = [];
utils.insertData($scope.list1, 5);
})
.factory('utils', function Utils() {
return {
insertData: function(list, numItems) {
for (var i = 0; i < numItems; i++) {
list.push({
id: i + 1,
title: 'item' + (i + 1)
});
}
},
getIndexesFromList: function(list) {
var newList = [];
for (var i in list) {
if (typeof list[i].id === "number" && newList.indexOf(list[i].id) === -1) newList.push(list[i].id)
}
return newList;
},
getAllSelectedItems: function(list) {
var newList = [];
newList = list.filter(function(el) {
return el.active === true;
});
return newList;
},
addListIfNoExists: function(list2, newListToAppend) {
var indexes = this.getIndexesFromList(list2);
var newList = [];
for (var i in newListToAppend) {
if (indexes.indexOf(newListToAppend[i].id) === -1) list2.push(newListToAppend[i])
}
return list2;
}
}
})
.directive('dualList', function(utils) {
function _controller($scope) {
$scope.selectAllItem = function(list, checked) {
list.map(function(item) {
item.active = checked
return item;
});
};
$scope.getAllSelectedItems = function(list) {
return utils.getAllSelectedItems(list);
}
$scope.moveItemToRightList = function() {
var newListToAppend = $scope.list1.filter(function(el) {
if (el.active === true) {
el.active = false;
return el;
}
});
if (newListToAppend.length > 0) {
$scope.list1 = $scope.list1.filter(function(el) {
return utils.getIndexesFromList(newListToAppend).indexOf(el.id) === -1;
});
$scope.list2 = utils.addListIfNoExists($scope.list2, newListToAppend);
if ($scope.list1.length === 0) $scope.checked1 = false;
}
};
$scope.moveItemToLeftList = function() {
var newListToAppend = $scope.list2.filter(function(el) {
if (el.active === true) {
el.active = false;
return el;
}
});
if (newListToAppend.length > 0) {
$scope.list2 = $scope.list2.filter(function(el) {
return utils.getIndexesFromList(newListToAppend).indexOf(parseInt(el.id)) === -1;
});
$scope.list1 = utils.addListIfNoExists($scope.list1, newListToAppend);
if ($scope.list2.length === 0) $scope.checked2 = false;
}
};
}
return {
restrict: "E",
scope: true,
controller: _controller,
templateUrl: "dualList.html"
};
});
dualList.html
<div class="container">
<br />
<div class="row">
<div class="dual-list list-left col-md-5">
<div class="well text-right">
<div class="row">
<div class="col-md-3">
<div class="checkbox">
<label>
<input type="checkbox"
ng-model="checked1"
ng-click="selectAllItem(list1, checked1)">
Todo {{getAllSelectedItems(list1).length}}/{{list1.length}}
</label>
</div>
</div>
<div class="col-md-9">
<div class="input-group">
<span class="input-group-addon glyphicon glyphicon-search"></span>
<input type="text"
name="SearchDualList"
ng-model="search1"
class="form-control"
placeholder="search" />
</div>
</div>
</div>
<ul class="list-group">
<a class="list-group-item"
href=""
data-id="{{item.id}}"
ng-click="item.active = !item.active"
ng-class="{active: item.active}"
ng-repeat="item in list1|filter: search1">{{item.title}}</a>
</ul>
<p ng-if="(list1 | filter:search1).length == 0">Sin Datos</p>
</div>
</div>
<div class="list-arrows col-md-1 text-center">
<button ng-click="moveItemToLeftList()"
class="btn btn-default btn-sm move-left">
<span class="glyphicon glyphicon-chevron-left"></span>
</button>
<button ng-click="moveItemToRightList()"
class="btn btn-default btn-sm move-right">
<span class="glyphicon glyphicon-chevron-right"></span>
</button>
</div>
<div class="dual-list list-right col-md-5">
<div class="well">
<div class="row">
<div class="col-md-3">
<div class="checkbox">
<label>
<input type="checkbox"
ng-model="checked2"
ng-click="selectAllItem(list2, checked2)">
Todo {{getAllSelectedItems(list2).length}}/{{list2.length}}
</label>
</div>
</div>
<div class="col-md-9">
<div class="input-group">
<span class="input-group-addon glyphicon glyphicon-search"></span>
<input type="text"
name="SearchDualList"
ng-model="search2"
class="form-control"
placeholder="search" />
</div>
</div>
</div>
<ul class="list-group">
<a class="list-group-item"
href=""
data-id="{{item.id}}"
ng-click="item.active = !item.active"
ng-class="{active: item.active}"
ng-repeat="item in list2|filter: search2">{{item.title}}</a>
</ul>
<p ng-if="(list2 | filter:search2).length == 0">Sin Datos</p>
</div>
</div>
</div>
</div>
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script data-require="jquery#*" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://code.angularjs.org/1.3.0/angular.js"></script>
<link data-require="bootstrap#*" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<link data-require="bootstrap#*" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" />
<link data-require="font-awesome#*" data-semver="4.3.0" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
<script>
document.write('<base href="' + document.location + '" />');
</script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<dual-list data-list1="list1" data-list2="list2"></dual-list>
</body>
</html>
style.css
.dual-list .list-group {
margin-top: 8px;
}
.list-arrows {
padding-top: 100px;
}
.list-arrows button {
margin-bottom: 20px;
}
.list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus {
border-color: white;
}
.input-group-addon {
top: 0;
}
I think this link will help you.
Try this npm for angular 2/4 just you need to install with npm
https://www.npmjs.com/package/ng2-dual-list-box
Attempting with the below jQuery method: But this does not work in my Angular environment, assuming because I'm attempting to do this outside of angular JS scope methods, but I don't know how to implement this using Angular scope methods (new to angular) any advice?
$('.song-thumb .hover-play').on('mouseenter', function(e) {
var elem = $('section.suggestedAlbums img');
elem.trigger(e.type);
elem.addClass('hoverclass');
});
$('.song-thumb .hover-play').on('mouseleave', function(e) {
var elem = $('section.suggestedAlbums img');
elem.trigger(e.type);
elem.removeClass('hoverclass');
});
var count = 0;
$('section.suggestedAlbums img').hover(function() {
count++;
$('[remove]').html('<label remove><br>It triggers the hover event(' + count + ') too.<br></label>');
});
Full page code:
<script>
$('.song-thumb .hover-play').on('mouseenter', function(e) {
var elem = $('section.suggestedAlbums img');
elem.trigger(e.type);
elem.addClass('hoverclass');
});
$('.song-thumb .hover-play').on('mouseleave', function(e) {
var elem = $('section.suggestedAlbums img');
elem.trigger(e.type);
elem.removeClass('hoverclass');
});
var count = 0;
$('section.suggestedAlbums img').hover(function() {
count++;
$('[remove]').html('<label remove><br>It triggers the hover event(' + count + ') too.<br></label>');
});
</script>
<!-- <h2>{{trackListData.listTitle}}</h2>
--><div ng-repeat="track in trackListData.tracks track by $index " class="feature-item-homepage fade-animate-nostagger">
<div class="song-thumb" ng-class="{active: $root.currentPlaying.song_id == track.id}">
<!--3 Cases-->
<!--Not the song being played-->
<div class="hover-play" ng-if="$root.currentPlaying.song_id != track.id" ng-click="$root.playSong(track);">
<i class="fa fa-plus add-to-playlist" ng-init="playlistOption = false" ng-click="$root.initPlaylistOption($event, track.id); $event.stopPropagation();"></i>
<i class="fa fa-play play-song"></i>
<img src="img/producer_icon_white.png" class="prod_logo">
<div class="song-title" style="width: 80%;padding-top:25px;">
<a class="song-linking" href="#/song/{{track.id}}">
<h4>{{track.title}}</h4>
<h5>{{track.artist.user_name}}</h5>
</a>
</div>
</div>
<!--The current song but paused-->
<div class="hover-play" ng-if="$root.currentPlaying.song_id == track.id && !$root.isPlaying" play-music>
<i class="fa fa-plus add-to-playlist" ng-init="playlistOption = false" ng-click="$root.initPlaylistOption($event, track.id); $event.stopPropagation();"></i>
<i class="fa fa-play play-song"></i>
</div>
<!--The current song but playing-->
<div class="hover-play" ng-if="$root.currentPlaying.song_id == track.id && $root.isPlaying" pause-music>
<i class="fa fa-plus add-to-playlist" ng-init="playlistOption = false" ng-click="$root.initPlaylistOption($event, track.id); $event.stopPropagation();"></i>
<i class="fa fa-pause pause-song"></i>
</div>
<img ng-src="{{(track.albums[0].album_picture? $root.fileServer +'uploads/' + track.albums[0].album_picture:(track.artist.profile_picture? $root.fileServer +'uploads/' + track.artist.profile_picture:'img/defaultalbum.png'))}}" />
</div>
<!-- <div class="song-title-wrap">
<div class="song-title">
<a class="song-linking" href="#/song/{{track.id}}">
<h4>{{track.title}}</h4>
<h5>{{track.artist.user_name}}</h5>
</a>
</div>
</div> -->
</div>
Add a controller to a containing div:
<div ng-controller='MyController'>
Add ngMouseleave and ngMouseenter to the .song-thumb .hover-play element
<div class="hover-play" ng-if="$root.currentPlaying.song_id != track.id" ng-click="$root.playSong(track);" ng-mouseenter='state.hoverPlay=true;' ng-mouseleave='state.hoverPlay=false'>
Add the controller
angular.controller('MyController', [ '$scope', function ($scope) {
$scope.state = {
hoverPlay: false
};
}]);
Use ngClass to add/remove the class
<img ng-class="{ 'hoverclass': state.hoverPlay }" . . . >
I am trying to work how to add a class with ngClick. I have uploaded up my code onto plunker Click here. Looking at the angular documentation i can't figure out the exact way it should be done. Below is a snippet of my code. Can someone guide me in the right direction
<div ng-show="isVisible" ng-class="{'selected': $index==selectedIndex}" class="block"></div>
Controller
var app = angular.module("MyApp", []);
app.controller("subNavController", function ($scope){
$scope.toggle = function (){
$scope.isVisible = ! $scope.isVisible;
};
$scope.isVisible = false;
});
I want to add or remove "active" class in my code dynamically on ng-click, here what I have done.
<ul ng-init="selectedTab = 'users'">
<li ng-class="{'active':selectedTab === 'users'}" ng-click="selectedTab = 'users'"><a href="#users" >Users</a></li>
<li ng-class="{'active':selectedTab === 'items'}" ng-click="selectedTab = 'items'"><a href="#items" >Items</a></li>
</ul>
You just need to bind a variable into the directive "ng-class" and change it from the controller. Here is an example of how to do this:
var app = angular.module("ap",[]);
app.controller("con",function($scope){
$scope.class = "red";
$scope.changeClass = function(){
if ($scope.class === "red")
$scope.class = "blue";
else
$scope.class = "red";
};
});
.red{
color:red;
}
.blue{
color:blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="ap" ng-controller="con">
<div ng-class="class">{{class}}</div>
<button ng-click="changeClass()">Change Class</button>
</body>
Here is the example working on jsFiddle
There is a simple and clean way of doing this with only directives.
<div ng-class="{'class-name': clicked}" ng-click="clicked = !clicked"></div>
you can also do that in a directive, if you want to remove the previous class and add a new class
.directive('toggleClass', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
if(element.attr("class") == "glyphicon glyphicon-pencil") {
element.removeClass("glyphicon glyphicon-pencil");
element.addClass(attrs.toggleClass);
} else {
element.removeClass("glyphicon glyphicon-ok");
element.addClass("glyphicon glyphicon-pencil");
}
});
}
};
});
and in your template:
<i class="glyphicon glyphicon-pencil" toggle-class="glyphicon glyphicon-ok"></i>
You have it exactly right, all you have to do is set selectedIndex in your ng-click.
ng-click="selectedIndex = 1"
Here is how I implemented a set of buttons that change the ng-view, and highlights the button of the currently selected view.
<div id="sidebar" ng-init="partial = 'main'">
<div class="routeBtn" ng-class="{selected:partial=='main'}" ng-click="router('main')"><span>Main</span></div>
<div class="routeBtn" ng-class="{selected:partial=='view1'}" ng-click="router('view1')"><span>Resume</span></div>
<div class="routeBtn" ng-class="{selected:partial=='view2'}" ng-click="router('view2')"><span>Code</span></div>
<div class="routeBtn" ng-class="{selected:partial=='view3'}" ng-click="router('view3')"><span>Game</span></div>
</div>
and this in my controller.
$scope.router = function(endpoint) {
$location.path("/" + ($scope.partial = endpoint));
};
var app = angular.module("MyApp", []);
app.controller("subNavController", function ($scope){
$scope.toggle = function (){
$scope.isVisible = ! $scope.isVisible;
};
$scope.isVisible = false;
});
<div ng-show="isVisible" ng-class="{'active':isVisible}" class="block"></div>
I used Zack Argyle's suggestion above to get this, which I find very elegant:
CSS:
.active {
background-position: 0 -46px !important;
}
HTML:
<button ng-click="satisfaction = 'VeryHappy'" ng-class="{active:satisfaction == 'VeryHappy'}">
<img src="images/VeryHappy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Happy'" ng-class="{active:satisfaction == 'Happy'}">
<img src="images/Happy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Indifferent'" ng-class="{active:satisfaction == 'Indifferent'}">
<img src="images/Indifferent.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'Unhappy'" ng-class="{active:satisfaction == 'Unhappy'}">
<img src="images/Unhappy.png" style="height:24px;" />
</button>
<button ng-click="satisfaction = 'VeryUnhappy'" ng-class="{active:satisfaction == 'VeryUnhappy'}">
<img src="images/VeryUnhappy.png" style="height:24px;" />
</button>
If you prefer separation of concerns such that logic for adding and removing classes happens on the controller, you can do this
controller
(function() {
angular.module('MyApp', []).controller('MyController', MyController);
function MyController() {
var vm = this;
vm.tab = 0;
vm.setTab = function(val) {
vm.tab = val;
};
vm.toggleClass = function(val) {
return val === vm.tab;
};
}
})();
HTML
<div ng-app="MyApp">
<ul class="" ng-controller="MyController as myCtrl">
<li ng-click="myCtrl.setTab(0)" ng-class="{'highlighted':myCtrl.toggleClass(0)}">One</li>
<li ng-click="myCtrl.setTab(1)" ng-class="{'highlighted':myCtrl.toggleClass(1)}">Two</li>
<li ng-click="myCtrl.setTab(2)" ng-class="{'highlighted':myCtrl.toggleClass(2)}">Three</li>
<li ng-click="myCtrl.setTab(3)" ng-class="{'highlighted':myCtrl.toggleClass(3)}">Four</li>
</ul>
CSS
.highlighted {
background-color: green;
color: white;
}
I can't believe how complex everyone is making this. This is actually very simple. Just paste this into your html (no directive./controller changes required - "bg-info" is a bootstrap class):
<div class="form-group col-md-12">
<div ng-class="{'bg-info': (!transport_type)}" ng-click="transport_type=false">CARS</div>
<div ng-class="{'bg-info': transport_type=='TRAINS'}" ng-click="transport_type='TRAINS'">TRAINS</div>
<div ng-class="{'bg-info': transport_type=='PLANES'}" ng-click="transport_type='PLANES'">PLANES</div>
</div>
for Reactive forms -
HTML file
<div class="col-sm-2">
<button type="button" [class]= "btn_class" id="b1" (click)="changeMe()">{{ btn_label }}</button>
</div>
TS file
changeMe() {
switch (this.btn_label) {
case 'Yes ': this.btn_label = 'Custom' ;
this.btn_class = 'btn btn-danger btn-lg btn-block';
break;
case 'Custom': this.btn_label = ' No ' ;
this.btn_class = 'btn btn-success btn-lg btn-block';
break;
case ' No ': this.btn_label = 'Yes ';
this.btn_class = 'btn btn-primary btn-lg btn-block';
break;
}