how to get clipboard data in angular JS - angularjs

I was actually looking to get the content of clipboard using angular JS to simulate a copy paste thing.

I created a directive for copy to clipboard which is using the document.execCommand() method.
Directive
(function() {
app.directive('copyToClipboard', function ($window) {
var body = angular.element($window.document.body);
var textarea = angular.element('<textarea/>');
textarea.css({
position: 'fixed',
opacity: '0'
});
function copy(toCopy) {
textarea.val(toCopy);
body.append(textarea);
textarea[0].select();
try {
var successful = document.execCommand('copy');
if (!successful) throw successful;
} catch (err) {
console.log("failed to copy", toCopy);
}
textarea.remove();
}
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('click', function (e) {
copy(attrs.copyToClipboard);
});
}
}
})
}).call(this);
Html
<button copy-to-clipboard="Copy Me!!!!" class="button">COPY</button>

here's a concise version I use -
function copyToClipboard(data) {
angular.element('<textarea/>')
.css({ 'opacity' : '0', 'position' : 'fixed' })
.text(data)
.appendTo(angular.element($window.document.body))
.select()
.each(function() { document.execCommand('copy') })
.remove();
}

BTW, if using Angular to copy to clipboard with a Chrome Packaged App, do the following:
Add "clipboardRead" and "clipboardWrite" to the "permissions" in the manifest.json.
use ng-click in your view to feed the value to the controller $scope, like: data-ng-click="copyUrlToClipboard(file.webContentLink)"
Put a function in your controller like:
$scope.copyUrlToClipboard = function(url) {
var copyFrom = document.createElement("textarea");
copyFrom.textContent = url;
var body = document.getElementsByTagName('body')[0];
body.appendChild(copyFrom);
copyFrom.select();
document.execCommand('copy');
body.removeChild(copyFrom);
this.flashMessage('over5');
}

I had the same issue and I used angular-clipboard feature[1] which uses new Selection API and Clipboard API available in the latest browsers.
First we have to install angular-clipboard lib, i'm using bower.
$ bower install angular-clipboard --save
To import the module use following in html.
<script src="../../bower_components/angular-clipboard/angular-clipboard.js"></script>
To set values to element using $scope in controller
$scope.textToCopy = 'Testing clip board';
Load the clipboard module using,
angular.module('testmodule', ['angular-clipboard']);
This works for Chrome 43+, Firefox 41+, Opera 29+ and IE10+.
Its simple & worked fine.
[1] https://www.npmjs.com/package/angular-clipboard
Thanks,

A completely different approach:
I need to copy & paste text between windows, so I used this to save (copy) the data to local storage. Then, in the other window, I load it out of local storage, using the same key, and I can then 'paste' is as I like.

Related

ng-click doesn't work with external JavaScript

I am creating an ionic project and I am trying to integrate with Algolia autocomplete.js. I managed to make the search system work, however I added a ng-click on my search results and this function is not working as presented in this codepen that I did as example below:
http://codepen.io/marcos_arata/pen/VKVOky
Inside my algolia's result template:
<a ng-click="add_name({{{ name }}})">
Function that should be run when clicked:
$scope.add_name = function(name) {
alert('User added!');
console.log(name);
}
I tried to inject the results inside the scope but didn't work as well:
autocomplete('#search_name', { hint: false, debug: true, openOnFocus: true },[{
source: index.ttAdapter({ hitsPerPage: 15 }),
templates: {
header: '',
suggestion: function(hit) {
$scope.hit = hit;
return template.render(hit);
}
}
}]);
http://codepen.io/marcos_arata/pen/VKVOky
---- SOLVED ----
Instead of creating a ng-click function inside your templates, you can handle the event click of your search inside your "autocomplete:selected" function and use the dataset and suggestion results.
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
## create any functions with the suggestion and dataset results inside
});
EDITING THE ANSWER:
Here is the codepen:
Apparently the suggestion keep the name clicked, so you dont need an extra function:
.on('autocomplete:selected', function(event, suggestion, dataset) {
$scope.name = suggestion.name;
console.log($scope.name);
});

Barcode Scanner using Ionic/ngcordova, not getting data

I've got a controller:
.controller('BarCodeScanCtrl', function($scope, $cordovaBarcodeScanner) {
$scope.scanBarcode = function() {
$cordovaBarcodeScanner.scan().then(function(barcodeData) {
// Success! Barcode data is here
console.log(barcodeData.text);
alert('barcode scanned:' + barcodeData);
}, function(error) {
alert('Error');
console.log(error);
// An error occurred
});
};
});
I'm using the QR code generator: http://www.qr-code-generator.com/ but I can't seem to retrieve any of the data I'm inputting. It's returning the barCode object the values of text & format properties are empty. The cancel property is true.
{
"cancelled": true,
"text":"",
"format": ""
}
Any ideas?
Have uploaded a working sample in github, please have a look.
Steps:
1) Create a project
2) cd into the project 3) Add required platforms
4) Add ngcordova-min.js file to the js folder 5) Add the ngcordova
js file path in index.html just before cordova path 6) Add ngcordova
dependancy to app.js 7) Add plugin 8) Add the function in html page
9) Add the logic in controller 10) Test the app
I supposed you are using Ionic. You should use on-touch directive from ionic not ng-click, because somehow when you are using ng-click it triggered the scan view twice and cause a force quit (CMIIW).
use it like this in your views:
<button on-touch="scanBarcode()">Scan</button>
Hope it works!
If the cancel property is true you stop the scanning function manually.
inject $ionicPlatform in your controller
.controller('BarCodeScanCtrl', function($scope, $cordovaBarcodeScanner, $ionicPlatform) {
$scope.scanQR = function () {
$ionicPlatform.ready(function() {
$cordovaBarcodeScanner.scan()
.then(function(barcodeData) {
alert('result = '+barcodeData.text);
alert('type = '+barcodeData.format);
alert('cancelled = '+barcodeData.cancelled);
}, function(error) {
alert(error);
});
});
}
});

Text Placeholders in CKEDITOR (angular context)

I am not very familiar with the CKEDITOR API yet and now I got stuck trying to find the way to create placeholders inside of the CKEDITOR editable area.The expected behaviour for the placeholder - to dissappear on user interaction with it, allowing to edit the content instead.
I know that there is already a placeholder plugin (http://ckeditor.com/addon/placeholder) but its behaviour is not what I am looking for.
To be more specific, the question is: is it possible to subscribe for some events on the particular element inside of the CKEDITOR?
Working in the angular context I am able to compile my html before it is passed to the CKEDITOR ng-model
$scope.html = $compile('<div><span text-placeholder >Placeholder</span></div>')($scope).html();
But then I fail trying to set click events inside of the directive:
.directive('textPlaceholder', [ function () {
return {
restrict: 'A',
link: function ($scope, $element) {
//THIS DOES NOT WORK UNFORTUNATELY
$element.on('click', function () {
console.log('clicked');
})
}
}
}])
Any thoughts?
UPDATE: For now I came up with the solution to implement simple plugin and then reference it in the CKEDITOR config:
(function () {
CKEDITOR.plugins.add('text-placeholder', {
init: function (editor) {
editor.on('key', function (evt) {
var el = $(CKEDITOR.instances.editor1.getSelection().getNative().baseNode.parentElement);
if (el.hasClass('text-placeholder')) {
el.remove();
}
});
}
});
})();
Looks ugly for me. Any feedback is appreciated.
This seems to be a final Solution:
CKEDITOR.plugins.add('text-placeholder', {
init: function (editor) {
editor.on('contentDom', function () {
var editable = editor.editable();
editable.attachListener(editable, 'click', function (event) {
var $placeholder = $(event.data.$.target).closest('.text-placeholder');
if ($placeholder.length > 0) {
var selection = editor.getSelection();
selection.selectElement(selection.getStartElement());
}
});
});
}
});
This applies the selection on the element with "text-placeholder" class when user focuses it inside of the editable area
Update:
See example
You inspired me to write one myself, using the above example as a starting point. In my use case I wanted to take placeholder text from an attribute on the editor -- data-placeholder -- and display it in the editor. When the editor gets focus, the placeholder text disappears. When the editor blurs -- if no user content has been entered -- the placeholder text is displayed again. Additionally, I set a data-placeholder-showing attribute so that I can, for example, use CSS to make the placeholder text gray. Here's my code:
CKEDITOR.plugins.add('text-placeholder', {
init: function (editor) {
var placeholder = editor.element.getAttribute('data-placeholder');
editor.on('contentDom', function () {
if (placeholder) {
editor.setData(placeholder);
editor.element.setAttribute('data-placeholder-showing', true);
}
});
editor.on('focus', function() {
if (editor.getData() === placeholder) {
editor.element.setAttribute('data-placeholder-showing', false);
editor.setData('');
}
});
editor.on('blur', function() {
if (placeholder && editor.getData().length === 0) {
editor.element.setAttribute('data-placeholder-showing', true);
editor.setData(placeholder);
}
});
}
});

Why will my twitter widget not render if i change the view in angularjs?

Hi and thanks for reading.
I have a angular app im making and ive stumbled on a problem. set up as so
index.html-
<html ng-app="myApp">
...
<div ng-view></div>
<div ng-include="'footer.html'"></div>
...
</html>
I wont bother putting my routes its pretty simple /home is shows the /home/index.html and so on...
/home/index.html (default view when you come to the site)
<div class="responsive-block1">
<div class="tweet-me">
<h1> tweet me </h1>
</div>
<div class="twitter-box">
<twitter-timeline></twitter-timeline>
</div>
twitter timeline directive
directives.directive("twitterTimeline", function() {
return {
restrict: 'E',
template: '<a class="twitter-timeline" href="https://twitter.com/NAME" data-widget-id="XXXXXXXXXXXXXX">Tweets by #NAME</a>',
link: function(scope, element, attrs) {
function run(){
(!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs"));
console.log('run script');
};
run();
}
};
});
So I have just created a basic twitter directive using the tag from twitter. But when I change the view example to /blog then go back to /home the twitter widget no longer renders at all.
Im also using an $anchorScroll and if i jump to anyway on the page with this the widget also disappears. Any info would be great thanks.
See this post: https://dev.twitter.com/discussions/890
I think that you may be able to get the widget to re-render by calling
twttr.widgets.load().
If you find that this does not work, you will need to wrap this code into $timeout in your controller:
controller('MyCtrl1', ['$scope', '$timeout', function ($scope, $timeout) {
$timeout = twttr.widgets.load();
}])
To build on Sir l33tname's answer:
In services declaration:
angular.module('app.services', []).
service('tweetWidgets', function() {
this.loadAllWidgets = function() {
/* widgets loader code you get when
* declaring you widget with Twitter
* this code is the same for all widgets
* so calling it once will reference whatever
* widgets are active in the current ng-view */
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
};
this.destroyAllWidgets = function() {
var $ = function (id) { return document.getElementById(id); };
var twitter = $('twitter-wjs');
if (twitter != null)
twitter.remove();
};
});
Then in controller declarations:
angular.module('app.controllers', []).
controller('view_1_Controller', tweetWidgets) {
// load them all
tweetWidgets.loadAllWidgets();
}).
controller('view_2_Controller', tweetWidgets) {
// now destroy them :>
tweetWidgets.destroyAllWidgets();
});
Now whenever you leave view #1 to go to view #2, your controller for view #2 will remove the widgets associated with view #1 and when you return to view #1 the widgets will be re-instatiated.
The problem is because when Angular switches views the script tag that was originally inserted is not removed from the document. I fixed this on my own website by removing the Twitter script element whenever my Twitter timeline directive is not in the view. See the code below with comments.
function (scope, el, attrs) {
el.bind('$destroy', function() {
var twitterScriptEl = angular.element('#twitter-wjs');
twitterScriptEl.remove();
});
// function provided by Twitter that's been formatted for easier reading
function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https';
// If the Twitter script element is already on the document this will not get called. On a regular webpage that gets reloaded this isn't a problem. Angular views are loaded dynamically.
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + "://platform.twitter.com/widgets.js";
js.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");
}
Basically it's what Loc Nguyen say.
So every time you recreate it you must remove it first.
var $ = function (id) { return document.getElementById(id); };
function loadTwitter() {!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");}
var twitter = $('twitter-wjs');
twitter.remove();
loadTwitter();
Answer by #b1r3k works without problems :
put this in your controller:
$timeout(function () { twttr.widgets.load(); }, 500);
For those trying to load twttr.widgets.load() inside their controller, you will most likely get an error that twttr is not defined AT SOME POINT in your UX, because the async call to load the twitter script may not be completed by the time you controller instantiates and references twttr.
So I created this TwitterService
.factory('TwitterService', ['$timeout', function ($timeout) {
return {
load: function () {
if (typeof twttr === 'undefined') {
(function() {
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
})();
} else {
$timeout = twttr.widgets.load();
};
}
}
}])
and then call TwitterService.load() inside the controllers that require your widgets. This worked pretty well. It basically just checks if the twttw object exists and if it does, just reload the script... otherwise just reload the script.
Not sure if this is the best implementation, but it seems like all other solutions have edge cases where it will throw an error. I have yet to find one with this alternative.

Twitter typeahead.js: Possible to use Angular JS as template engine? If not how do I replace "{{}}" for Hogan/Mustache js?

I am working with twitter's typeahead.js and I was wondering if it was possible to modify hogan.js to use something other than {{}}?
I am looking at the minified code now and I have no idea what to change for something so simple. Doing a find and replace breaks it.
I am asking this mainly because I'm using Angular JS but twitter's typeahead requires a templating engine, causing hogan and angular's {{}} to clash. An even better solution would be simply modifying Angular JS (I know it's not a templating engine) and ditching Hogan to fit the following criteria:
Any template engine will work with typeahead.js as long as it adheres to the following API:
// engine has a compile function that returns a compiled template
var compiledTemplate = ENGINE.compile(template);
// compiled template has a render function that returns the rendered template
// render function expects the context to be first argument passed to it
var html = compiledTemplate.render(context);
Ignore the documentation on this, just look at the source code:
function compileTemplate(template, engine, valueKey) {
var renderFn, compiledTemplate;
if (utils.isFunction(template)) {
renderFn = template;
} else if (utils.isString(template)) {
compiledTemplate = engine.compile(template);
renderFn = utils.bind(compiledTemplate.render, compiledTemplate);
} else {
renderFn = function(context) {
return "<p>" + context[valueKey] + "</p>";
};
}
return renderFn;
}
It happens you can just pass a function to template, callable with a context object which contains the data you passed in the datum objects at the time of instantiation, so:
$('#economists').typeahead({
name: 'economists',
local: [{
value: 'mises',
type: 'austrian economist',
name: 'Ludwig von Mises'
}, {
value: 'keynes',
type: 'keynesian economist',
name: 'John Maynard Keynes'
}],
template: function (context) {
return '<div>'+context.name+'<span>'+context.type.toUpperCase()+'</span></div>'
}
})
If you want to use Hogan.js with Angular, you can change the delimiters by doing something like:
var text = "my <%example%> template."
Hogan.compile(text, {delimiters: '<% %>'});
It appears that the template engine result that typeahead.js expects is an html string and not the dom element (in dropdown_view.js). So I am not sure there is a good solution for using an angular template. As a test I was able to get it binding the result to an angular template but it has to render to an element and then get the html value from the element after binding with the data. I don't like this approach but I figured someone might find it useful. I think I will go with a template function like in the previous post.
Jade template looks like
.result
p {{datum.tokens}}
p {{datum.value}}
Directive
angular.module('app').directive('typeahead', [
'$rootScope', '$compile', '$templateCache',
function ($rootScope, $compile, $templateCache) {
// get template from cache or you can load it from the server
var template = $templateCache.get('templates/app/typeahead.html');
var compileFn = $compile(template);
var templateFn = function (datum) {
var newScope = $rootScope.$new();
newScope.datum = datum;
var element = compileFn(newScope);
newScope.$apply();
var html = element.html();
newScope.$destroy();
return html;
};
return {
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
element.typeahead({
name: 'server',
remote: '/api/search?q=%QUERY',
template: templateFn
});
element.on('$destroy', function () {
element.typeahead('destroy');
});
element.on('typeahead:selected', function () {
element.typeahead('setQuery', '');
});
}
};
}
]);

Resources