Using ZeroClipboard on a Bootstrap Modal - html

I have a Twitter Bootstrap modal that I am displaying where I want to include a "Copy to Clipboard" button. I am attempting to use the ZeroClipboard component https://github.com/jonrohan/ZeroClipboard
Below is my sample code. Button "Copy1" is on a page directly and that works. Button "Copy2" is on the modal and that is not working. Internet Explorer appears to "lock up" when "Copy2" is pressed.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link type="text/css" rel="stylesheet" href="/Content/bootstrap.min.css" />
<script src="/Scripts/jquery-1.9.1.min.js"></script>
<script src="/Scripts/ZeroClipboard.min.js"></script>
<script src="/Scripts/bootstrap.min.js"></script>
</head>
<body>
<div class="row">
<div class="span6">
<!-- Textbox and copy button pair #1 (not on modal) -->
<input type="text" id="Input1" />
<button class="btn" type="button" id="Copy1" data-clipboard-target="Input1">Copy Input #1</button>
</div>
<div class="span6">
<a class="btn" type="button" href="#Modal1" data-toggle="modal">Show Modal Dialog</a>
</div>
</div>
<div class="modal hide fade" role="dialog" id="Modal1">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>Copy to Clipboard Modal</h3>
</div>
<div class="modal-body">
<p>
<!-- Textbox and copy button pair #2 (on modal) -->
<input type="text" id="Input2" />
<button class="btn" type="button" id="Copy2" data-clipboard-target="Input2">Copy Input #2</button>
</p>
</div>
<div class="modal-footer">
<p>
<button class="btn" type="button" data-dismiss="modal">Close</button>
</p>
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
ZeroClipboard.setDefaults({
moviePath: '/Scripts/ZeroClipboard.swf'
});
var clip1 = new ZeroClipboard($("#Copy1"));
var clip2 = new ZeroClipboard($("#Copy2"));
});
</script>
</body>
</html>
Can anyone offer some guidance on what is going wrong? I understand that Bootstrap takes the modal out of the DOM tree until displayed, but I'm not sure how to accommodate for that.
Edit: Corrected id of 2nd input to be "Input2" to match the button's target.
In addition, I have attempted the following javascript:
//var clip2 = new ZeroClipboard($("#Copy2"));
$("#Modal1").on('shown', function () {
var clip2 = new ZeroClipboard($("#Copy2"));
});
Also, it would appear that the issue is browser-specific.
The original code and my modified code locks up Internet Explorer 10. But Google Chrome is OK under both code attempts.

Your 2nd copy button is targeting a nonexistent ID:
<button class="btn" type="button" id="Copy2" data-clipboard-target="Input2">Copy Input #2</button>
Your markup is incorrect:
<input type="text" id="Text2" />
Should be:
<input type="text" id="Input2" />

When we use "ZeroClipboard" with "Bootstrap modal", it creates conflict in "IE".
We need to make changes in bootstrap's "js" file.
There is method defined for, “ Modal.prototype.enforceFocus “ element in “Bootstrap.js” .
Here, in “If” condition, we need to add following condition,
!$(e.target).closest('[id ^= "ZeroClipboardMovie_"]').length)
Resultant code will look as follows,
if (this.$element[0] !== e.target && !this.$element.has(e.target).length && !$(e.target).closest('[id ^= "ZeroClipboardMovie_"]').length) {
this.$element.focus()
}
where, [id ^= "ZeroClipboardMovie_"] is ZeroClipboard container element.
In above code, it is taking container of recent "ZeroClipboard". You can change it to, appropriate "zeroclipboard-container" as per the requirement.
You can also checkout this blog post for more detail- http://wisdmlabs.com/blog/ie-issue-zeroclipboard-bootstrap-modal/

if (!checkIsBelowIE8()) {
if (/MSIE|Trident/.test(window.navigator.userAgent)) {
(function ($) {
var zcClass = '.' + ZeroClipboard.config('containerClass');
var proto = $.fn.modal.Constructor.prototype;
proto.enforceFocus = function () {
$(document)
.off('focusin.bs.modal') /* Guard against infinite focus loop */
.on('focusin.bs.modal', $.proxy(function (e) {
if (this.$element[0] !== e.target &&
!this.$element.has(e.target).length &&
/* Adding this final condition check is the only real change */
!$(e.target).closest(zcClass).length
) {
this.$element.focus();
}
}, this));
};
})(window.jQuery);
}
var client = new ZeroClipboard($(".clipboard"));
client.on("copy", function (e) {
var clipboard = e.clipboardData;
clipboard.setData("text/plain", $.trim($(e.target).parent().prev().find("input").val()));
showMsg("success", "copy success");
});
}
function checkIsBelowIE8() {
try {
var b_name = navigator.appName;
var b_version = navigator.appVersion;
var version = b_version.split(";");
var trim_version = version[1].replace(/[ ]/g, "");
if (b_name == "Microsoft Internet Explorer") {
/*if IE6 or IE7*/
if (trim_version == "MSIE8.0" || trim_version == "MSIE7.0" || trim_version == "MSIE6.0") {
return true;
} else {
return false;
}
} else {
return false;
}
} catch (e) {
return false;
}
}
hope to help you

Related

Html button text change

I like to have this button.
When you click on it the button text should change to "Updating...".
Then two seconds later it should change to "Updated!" and the button shouldn't be clickable after that.
Current Html Button script:
<a class="btn-primary-md" id="buyButton" onclick="text = 'Updated!'">Update</a>
So to sum up, you have a button/link and onclick you'll want:
The text of that element to change.
The element to be disabled.
After two seconds the value to change again to another
value.
You can do this by extracting the JavaScript from the HTML for readability and maintainability like this:
var button = document.getElementById("btn");
button.addEventListener("click", function () {
button.disabled = true;
button.innerHTML = "Waiting...";
setTimeout(function () {
button.innerHTML = "Thank you!";
button.disabled = false;
}, 2000);
});
<button id="btn" type="button">
Click me
</button>
<input onclick="change()" type="button" value="Updated" id="buyButton" />
Javascript:
function changeBtnTxt()
{
document.getElementById("buyButton").value="Update";
}
Maybe...
<input onclick="changeBtnTxt()" type="button" value="Update" id="buyButton" />
function changeBtnTxt()
{
document.getElementById("buyButton").value="Updated";
}
This is the correct one! Sorry!

Angular JS - Modal not shown properly

I am learning to use Modal in AngularJS and Bootstrap 4 framework. I have manage to show it, but it is not shown properly, i.e. blocking components in background and cannot be shown if animation: true. I am not sure what causing this because I have inject ngAnimate and ui.bootstrap in my app controller.
Angular and ui-bootstrap version used
Angular v1.6.4
UI-bootstrap v2.5.0
Below I will provide my code.
View.html
<div class="container-fluid content-container" ng-app="listEmployee">
<div class="change-password-modal-container"></div>
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title" id="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body" id="modal-body">
<ul>
<li ng-repeat="item in ctrl.items">
{{ item }}
</li>
</ul>
Selected: <b>{{ ctrl.selected.item }}</b>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ctrl.ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="ctrl.cancel()">Cancel</button>
</div>
</script>
... <!--other html elements -->
</div>
Below is my code in controller which is related to showing the modal
Controller.js
var ctrl = this;
ctrl.items = ['item1', 'item2', 'item3'];
ctrl.animationsEnabled = false;
ctrl.open = function (size, parentSelector) {
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.change-password-modal-container ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
animation: ctrl.animationsEnabled,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
controllerAs: 'ctrl',
size: size,
appendTo: parentElem,
resolve: {
items: function () {
return ctrl.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
ctrl.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
The z-index of my content is 1
mystyle.css
#content {
z-index: 1;
}
Below is the screenshot when the animation:false
Below is the screenshot when the animation:true (i.e. modal is not visible, it's like the modal is behind the screen)
Any help would be appreciated.
After reading this issue in GitHub, turns out this is the problem when using Bootstrap 4. Bootstrap 4 has different class names with Bootstrap 3, in this case, it is .in in version 3 become .show in version 4.
Following as suggested by IdanCo in the thread, which I rewrite below (so it will be easier for others to read) fixed this issue.
Change the class names from ui-bootstrap-tpls-###.js as below.
'class': 'modal-backdrop',
'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}',
'uib-modal-animation-class': 'fade',
'modal-in-class': 'in' //change this
'modal-in-class': 'show' //to this
});
if (modal.backdropClass) {
backdropDomEl.addClass(modal.backdropClass);
## -477,7 +477,7 ##
'ng-style': '{\'z-index\': 1050 + $$topModalIndex*10, display: \'block\'}',
'tabindex': -1,
'uib-modal-animation-class': 'fade',
'modal-in-class': 'in' //change this
'modal-in-class': 'show' //to this
}).append(content);
if (modal.windowClass) {
angularDomEl.addClass(modal.windowClass);
Hope this update could help someone in the future.

How to use ng-show and ng-hide with buttons in AngularJS

I have two buttons Start and Stop,when I click the Start button I want the Stop button to show and the Start button to be hidden vice versa when I click the Stop button I want the Start button to be shown and the Stop button to be hidden.Only one button must be shown at any one time.Here is what I tried but it doesn't seem to work.Where am I going wrong?
<span ng-hide="Model.StartEvent == true">
<button ng-click="Model.StartEvent()" id="btnEventStart">Start</button>
</span>
<span ng-show="Model.StopEvent == false">
<button ng-click="Model.StopEvent()" id="btnStopEvent" class="tooltip">Stop</button>
</span>
You could do even less
<button ng-click="goEvent();" ng-hide="going">Start</button>
<button ng-click="goEvent();" ng-show="going">Stop</button>
...
$scope.going = false;
$scope.goEvent = function(){
$scope.going = !$scope.going;
if($scope.going){
$scope.go();
}else{
$scope.stop();
}
}
you should use same variable for both...
<button ng-click="Model.toggleShow(); Model.StartEvent()" ng-show="Model.showStartEvent">Start</button>
<button ng-click="Model.toggleShow(); Model.StopEvent()" ng-show="!Model.showStartEvent">Stop</button>
Modal.toggleShow = function(){
Model.showStartEvent = !Model.showStartEvent;
}
also you can call toggle right inside the startevent/stopevent rather than having two function calls on ng-click
Html:
<div ng-controller="YourController">
<span ng-hide="EventRunning"><button ng-click="StartEvent($event)"id="btnEventStart">Start</button></span>
<span ng-show="EventRunning"><button ng-click="StopEvent($event)" id="btnStopEvent">Stop</button></span>
</div>
Use single semaphore for hide and show EventRunning
Controller:
{ ...
$scope.EventRunning = false;
$scope.StartEvent = function (event) {
event.preventDefault();
$scope.EventRunning = true;
// your code
}
$scope.StopEvent = function (event) {
event.preventDefault();
$scope.EventRunning = false;
// your code
}
... }

How to change class of particular input in angularjs

I want to create function (in angularjs controller) which add particular class to the parent div of the empty input when clicking on the button. And remove that class when i type something in the input.
I have a working example here: http://jsfiddle.net/fUdLv/
HTML:
<div ng-app="authorization">
<div ng-controller="authCtrl as auth">
<div ng-class="{'error':auth.needEmail}">
<input type="text" ng-model="auth.email" ng-keypress="auth.clearEmail()">
</div>
<div ng-class="{'error':auth.needPassword}">
<input type="password" ng-model="auth.password" ng-keypress="auth.clearPassword()">
</div>
<div>
<button ng-click="auth.signin()">Sign in</button>
</div>
</div>
</div>
Javascript:
angular.module('authorization', [])
.controller('authCtrl', function() {
this.needEmail = false;
this.needPassword = false;
this.signin = function pay() {
if (!this.email){
this.needEmail = true;
}
if (!this.password){
this.needPassword = true;
}
};
this.clearEmail = function(){
this.needEmail = false;
}
this.clearPassword = function(){
this.needPassword = false;
}
});
But i'm sure it is very bad code, because i have a particular function for working with particular input. How can i generalize this function for working on every input (for example, if i would have three or four inputs it is not very smart solution to create function for each of them)
How about use <form> and FormController to control state?
You probably need to read this guide from official site.

Edit In Place Content Editing

When using ng-repeat what is the best way to be able to edit content?
In my ideal situation the added birthday would be a hyperlink, when this is tapped it will show an edit form - just the same as the current add form with an update button.
Live Preview (Plunker)
HTML:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.0/css/bootstrap-combined.min.css"
rel="stylesheet">
</head>
<body ng-app="birthdayToDo" ng-controller="main">
<div id="wrap">
<!-- Begin page content -->
<div class="container">
<div class="page-header">
<h1>Birthday Reminders</h1>
</div>
<ul ng-repeat="bday in bdays">
<li>{{bday.name}} | {{bday.date}}</li>
</ul>
<form ng-show="visible" ng-submit="newBirthday()">
<label>Name:</label>
<input type="text" ng-model="bdayname" placeholder="Name" ng-required/>
<label>Date:</label>
<input type="date" ng-model="bdaydate" placeholder="Date" ng-required/>
<br/>
<button class="btn" type="submit">Save</button>
</form>
</div>
<div id="push"></div>
</div>
<div id="footer">
<div class="container">
<a class="btn" ng-click="visible = true"><i class="icon-plus"></i>Add</a>
</div>
</div>
</body>
App.js:
var app = angular.module('birthdayToDo', []);
app.controller('main', function($scope){
// Start as not visible but when button is tapped it will show as true
$scope.visible = false;
// Create the array to hold the list of Birthdays
$scope.bdays = [];
// Create the function to push the data into the "bdays" array
$scope.newBirthday = function(){
$scope.bdays.push({name:$scope.bdayname, date:$scope.bdaydate});
$scope.bdayname = '';
$scope.bdaydate = '';
};
});
You should put the form inside each node and use ng-show and ng-hide to enable and disable editing, respectively. Something like this:
<li>
<span ng-hide="editing" ng-click="editing = true">{{bday.name}} | {{bday.date}}</span>
<form ng-show="editing" ng-submit="editing = false">
<label>Name:</label>
<input type="text" ng-model="bday.name" placeholder="Name" ng-required/>
<label>Date:</label>
<input type="date" ng-model="bday.date" placeholder="Date" ng-required/>
<br/>
<button class="btn" type="submit">Save</button>
</form>
</li>
The key points here are:
I've changed controls ng-model to the local scope
Added ng-show to form so we can show it while editing
Added a span with a ng-hide to hide the content while editing
Added a ng-click, that could be in any other element, that toggles editing to true
Changed ng-submit to toggle editing to false
Here is your updated Plunker.
I was looking for a inline editing solution and I found a plunker that seemed promising, but it didn't work for me out of the box. After some tinkering with the code I got it working. Kudos to the person who made the initial effort to code this piece.
The example is available here http://plnkr.co/edit/EsW7mV?p=preview
Here goes the code:
app.controller('MainCtrl', function($scope) {
$scope.updateTodo = function(indx) {
console.log(indx);
};
$scope.cancelEdit = function(value) {
console.log('Canceled editing', value);
};
$scope.todos = [
{id:123, title: 'Lord of the things'},
{id:321, title: 'Hoovering heights'},
{id:231, title: 'Watership brown'}
];
});
// On esc event
app.directive('onEsc', function() {
return function(scope, elm, attr) {
elm.bind('keydown', function(e) {
if (e.keyCode === 27) {
scope.$apply(attr.onEsc);
}
});
};
});
// On enter event
app.directive('onEnter', function() {
return function(scope, elm, attr) {
elm.bind('keypress', function(e) {
if (e.keyCode === 13) {
scope.$apply(attr.onEnter);
}
});
};
});
// Inline edit directive
app.directive('inlineEdit', function($timeout) {
return {
scope: {
model: '=inlineEdit',
handleSave: '&onSave',
handleCancel: '&onCancel'
},
link: function(scope, elm, attr) {
var previousValue;
scope.edit = function() {
scope.editMode = true;
previousValue = scope.model;
$timeout(function() {
elm.find('input')[0].focus();
}, 0, false);
};
scope.save = function() {
scope.editMode = false;
scope.handleSave({value: scope.model});
};
scope.cancel = function() {
scope.editMode = false;
scope.model = previousValue;
scope.handleCancel({value: scope.model});
};
},
templateUrl: 'inline-edit.html'
};
});
Directive template:
<div>
<input type="text" on-enter="save()" on-esc="cancel()" ng-model="model" ng-show="editMode">
<button ng-click="cancel()" ng-show="editMode">cancel</button>
<button ng-click="save()" ng-show="editMode">save</button>
<span ng-mouseenter="showEdit = true" ng-mouseleave="showEdit = false">
<span ng-hide="editMode" ng-click="edit()">{{model}}</span>
<a ng-show="showEdit" ng-click="edit()">edit</a>
</span>
</div>
To use it just add water:
<div ng-repeat="todo in todos"
inline-edit="todo.title"
on-save="updateTodo($index)"
on-cancel="cancelEdit(todo.title)"></div>
UPDATE:
Another option is to use the readymade Xeditable for AngularJS:
http://vitalets.github.io/angular-xeditable/
I've modified your plunker to get it working via angular-xeditable:
http://plnkr.co/edit/xUDrOS?p=preview
It is common solution for inline editing - you creale hyperlinks with editable-text directive
that toggles into <input type="text"> tag:
<a href="#" editable-text="bday.name" ng-click="myform.$show()" e-placeholder="Name">
{{bday.name || 'empty'}}
</a>
For date I used editable-date directive that toggles into html5 <input type="date">.
Since this is a common piece of functionality it's a good idea to write a directive for this. In fact, someone already did that and open sourced it. I used editablespan library in one of my projects and it worked perfectly, highly recommended.