HTML5 File upload with AngularJS - html

I'm trying to get angular to read the contents of a file that the user selects through an <input type="file" control. Even though angular does not have directives for file upload controls, it should be easy to fix that with a call to $apply:
function MyController($scope) {
$('#myFile').on('change', function() {
var that = this;
$scope.$apply(function() { $scope.files = that.files });
});
}
Unfortunately, the event is never fired. It's like the selector is unable to refer to the correct DOM element: even though the selector finds the element, the list of files is always empty. This also happens if i poke around with the js console. The DOM inspector instead has the file list among its properties.
It's driving me crazy, but the only way I've got it to work so far is to use an inline event handler that assigns to a global variable. Why is the jquery selector returning another item? Is there some template compilation mumbo-jumbo that angular does which confuses selectors?

Here is what I do:
http://plnkr.co/edit/JPxSCyrxosVXfZnzEIuS?p=preview
app.directive('filelistBind', function() {
return function( scope, elm, attrs ) {
elm.bind('change', function( evt ) {
scope.$apply(function() {
scope[ attrs.name ] = evt.target.files;
console.log( scope[ attrs.name ] );
});
});
};
});
template:
<input type="file" filelist-bind name="files"/>
<p>selected files : <pre>{{ files | json }}</pre></p>
This kind of task, you definitely want to make use of directive.
But I think that your main concern is how to access the selected file
objects and my example should clarify that.

If you are looking for file upload with angular you can use this plugin
https://github.com/danialfarid/angular-file-upload
It is basically a directive like tosh's answer that takes care of non-HTML5 browsers with FileAPI flash polyfill and has $http.uploadFile function to upload the actual file via AJAX.

This site uses Angular service for HTML5 File Upload. A simple way is to setup a controller which calls the service and updates the UI when the asynchronous call is completed.
controller:
myapp.controller('fileUploadCtrl', ['$scope', '$q', 'FileInputService', function ($scope, $q, FileInputService) {
$scope.fileInputContent = "";
$scope.onFileUpload = function (element) {
$scope.$apply(function (scope) {
var file = element.files[0];
FileInputService.readFileAsync(file).then(function (fileInputContent) {
$scope.fileInputContent = fileInputContent;
});
});
};
}]);
service:
myapp.service('FileInputService', function ($q) {
this.readFileAsync = function (file) {
var deferred = $q.defer(),
fileReader = new FileReader(),
fileReader.readAsText(file);
fileReader.onload = function (e) {
deferred.resolve(e.target.result);
};
return deferred.promise;
};
});
template:
Choose File <input type="file" onchange="angular.element(this).scope().onFileUpload(this)">
<br />
{{fileInputContent}}
Reference: You can find the full source code and reference on this site.

Related

Determine file type using Electron dialog

I used HTML5 File API to drag a file into an Electron application and obtained file details (name, mime-type, size, etc.). How can I achieve the same when selecting a file via Electron's dialog module? Below is the code (renderer process) that leverages HTML5's File API:
const {dialog} = require('electron').remote;
// Using jQuery ($)
var holder = $('#holder');
holder.on('drag dragstart dragend dragover dragenter dragleave drop', function(evt) {
evt.preventDefault();
evt.stopPropagation();
})
.on('drop', function(evt) {
let file = evt.originalEvent.dataTransfer.files[0];
console.log(file.name);
console.log(file.type);
console.log(file.size);
})
.on('click', function(evt) {
dialog.showOpenDialog({
properties: [ 'openFile' ]
}, function(file) {
console.log(file); // just displays local, full path
// code to get name, type, size... how do I?
});
});
Check this library: mmmagic, it will do just what you want.

TinyMCE and AngularJS - not loading after NgSwitch

I hope I am clear enough with this request for assistance, as it is hard to explain and I can't post all the code here. I have downloaded code to enable TinyMCE to be used in a NgRepeat with AngularJS:
angular.module('ui.tinymce', [])
.value('uiTinymceConfig', {})
.directive('uiTinymce', ['uiTinymceConfig', function (uiTinymceConfig) {
uiTinymceConfig = uiTinymceConfig || {};
var generatedIds = 0;
return {
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
var expression, options, tinyInstance;
// generate an ID if not present
if (!attrs.id) {
attrs.$set('id', 'uiTinymce' + generatedIds++);
}
options = {
// Update model when calling setContent (such as from the source editor popup)
setup: function (ed) {
ed.on('init', function (args) {
ngModel.$render();
});
// Update model on button click
ed.on('ExecCommand', function (e) {
ed.save();
ngModel.$setViewValue(elm.val());
if (!scope.$$phase) {
scope.$apply();
}
});
// Update model on keypress
ed.on('KeyUp', function (e) {
ed.save();
ngModel.$setViewValue(elm.val());
if (!scope.$$phase) {
scope.$apply();
}
});
},
mode: 'exact',
elements: attrs.id
};
if (attrs.uiTinymce) {
expression = scope.$eval(attrs.uiTinymce);
} else {
expression = {};
}
angular.extend(options, uiTinymceConfig, expression);
setTimeout(function () {
tinymce.init(options);
});
ngModel.$render = function () {
if (!tinyInstance) {
tinyInstance = tinymce.get(attrs.id);
}
if (tinyInstance) {
tinyInstance.setContent(ngModel.$viewValue || '');
}
};
}
};
}]);
var gwApp = angular.module('gwApp', ['ui.tinymce']);
I don't really understand this code, but it works fine initially. My page starts with a list of Posts. I click on 'Show Reply' for the first post, and using NgSwitch the multiple replies become visible (nested NgRepeat). I submit a new reply message (the reply text is entered using tinymce) using a RESTful API service and a http call (too much code to post here). Then after clicking the submit button for the new reply message, the NgSwitch kicks in again unexpectedly to make the replies no longer visible. When I expand the replies again, the tinymce is just a regular textarea again, and the proper editor is gone.
I know this is not very clear, but I'm hoping someone can make sense of what I've written and can help me solve this problem..
I was having the same problem using ng-switch and ng-show so i added:
scope.$watch('onHidden()',function(){ tinymce.editors = [] });
after the setTimeout function.
Also replace the
ed.on('init',function(args){ ngModel.$render(); });
with
ed.on('init',function(args){ ed.setContent(ngModel.$viewValue); });
and remove the $render function.
This is the link to the working code in JsFiddle

Clicking, pasting text and uploading files from extension

So I'm basically developing an automated click-paste-and-upload system for mutiple texts and files inside a google page.
This method helped me get the instances of objects that I'm looking for: buttons, textboxes, richtextboxes, etc.
Now I want to work with them.
So for example I know the id of a button , and the function subscribed to its click event. How do I trigger the click event from the extension ? I've tried injecting a script with the click event handler (discovered with DOM inspector) at "document_startup" but I don't get an error or anything else.
Here's the content script! The loggerhead function should have inserted the script but I don't think it did. What might be the reason for the blow code not giving anything?
// Runs a function for every added DOM element that matches a filter
// filter -- either function(DOM_node){/*...*/}, returns true or false
// OR a jQuery selector
// callback -- function(DOM_node){/*...*/}
function watchNodes(filter, callback){
observer = new MutationObserver( function (mutations) {
mutations.forEach( function (mutation){
if(typeof filter === "function"){
$(mutation.addedNodes).filter(
function(i){ return filter(this); }
).each(
function(i){ callback(this); }
);
} else {
$(mutation.addedNodes).filter(filter).each(
function(i){ callback(this); }
);
}
});
});
// For every added element, a mutation will be processed
// with mutation.taget == parent
// and mutation.addedNodes containing the added element
observer.observe(document, { subtree: true, childList: true });
}
function loggerhead(node) {
console.log("passhead");
//also inject jquery
var jqueryEl = document.createElement('script');
jqueryEl.setAttribute('src', chrome.extension.getURL('jquery-1.11.1.min.js'));
jqueryEl.setAttribute('type', 'text/javascript');
var scriptEl = document.createElement('script');
scriptEl.setAttribute('src', chrome.extension.getURL('script.js'));
scriptEl.setAttribute('type', 'text/javascript');
node.appendChild(jqueryEl);
node.appendChild(scriptEl);
}
watchNodes("head", loggerhead);
// method not working
//var gmailHead = jQuery("head", document).get(0);
script.js contains the function of subscribed to the click event of the button that I've managed to find through the DOM inspector:
function Cdb(b){return function(){if(Vbb()){return Ddb(b,this,arguments)}else{var a=Ddb(b,this,arguments);a!=null&&(a=a.val);return a}}}
You should try to call the existing click handler like
buttonElement.click()

Repeatedly Grab DOM in Chrome Extension

I'm trying to teach myself how to write Chrome extensions and ran into a snag when I realized that my jQuery was breaking because it was getting information from the extension page itself and not the tab's current page like I had expected.
Quick summary, my sample extension will refresh the page every x seconds, look at the contents/DOM, and then do some stuff with it. The first and last parts are fine, but getting the DOM from the page that I'm on has proven very difficult, and the documentation hasn't been terribly helpful for me.
You can see the code that I have so far at these links:
Current manifest
Current js script
Current popup.html
If I want to have the ability to grab the DOM on each cycle of my setInterval call, what more needs to be done? I know that, for example, I'll need to have a content script. But do I also need to specify a background page in my manifest? Where do I need to call the content script within my extension? What's the easiest/best way to have it communicate with my current js file on each reload? Will my content script also be expecting me to use jQuery?
I know that these questions are basic and will seem trivial to me in retrospect, but they've really been a headache trying to explore completely on my own. Thanks in advance.
In order to access the web-pages DOM you'll need to programmatically inject some code into it (using chrome.tabs.executeScript()).
That said, although it is possible to grab the DOM as a string, pass it back to your popup, load it into a new element and look for what ever you want, this is a really bad approach (for various reasons).
The best option (in terms of efficiency and accuracy) is to do the processing in web-page itself and then pass just the results back to the popup. Note that in order to be able to inject code into a web-page, you have to include the corresponding host match pattern in your permissions property in manifest.
What I describe above can be achieved like this:
editorMarket.js
var refresherID = 0;
var currentID = 0;
$(document).ready(function(){
$('.start-button').click(function(){
oldGroupedHTML = null;
oldIndividualHTML = null;
chrome.tabs.query({ active: true }, function(tabs) {
if (tabs.length === 0) {
return;
}
currentID = tabs[0].id;
refresherID = setInterval(function() {
chrome.tabs.reload(currentID, { bypassCache: true }, function() {
chrome.tabs.executeScript(currentID, {
file: 'content.js',
runAt: 'document_idle',
allFrames: false
}, function(results) {
if (chrome.runtime.lastError) {
alert('ERROR:\n' + chrome.runtime.lastError.message);
return;
} else if (results.length === 0) {
alert('ERROR: No results !');
return;
}
var nIndyJobs = results[0].nIndyJobs;
var nGroupJobs = results[0].nGroupJobs;
$('.lt').text('Indy: ' + nIndyJobs + '; '
+ 'Grouped: ' + nGroupJobs);
});
});
}, 5000);
});
});
$('.stop-button').click(function(){
clearInterval(refresherID);
});
});
content.js:
(function() {
function getNumberOfIndividualJobs() {...}
function getNumberOfGroupedJobs() {...}
function comparator(grouped, individual) {
var IndyJobs = getNumberOfIndividualJobs();
var GroupJobs = getNumberOfGroupedJobs();
nIndyJobs = IndyJobs[1];
nGroupJobs = GroupJobs[1];
console.log(GroupJobs);
return {
nIndyJobs: nIndyJobs,
nGroupJobs: nGroupJobs
};
}
var currentGroupedHTML = $(".grouped_jobs").html();
var currentIndividualHTML = $(".individual_jobs").html();
var result = comparator(currentGroupedHTML, currentIndividualHTML);
return result;
})();

backbone.js fetch json success will not hit

i use fetch from backbone.js to load a json model but success will not hit.
var DialogModel = Backbone.Model.extend({
url : function() {
return '/messages/getDialog';
},
parse : function(res) {
return res.dialog;
}
});
var DialogView = Backbone.View.extend({
el: $("#page"),
initialize: function() {
var onDataHandler = function() {
this.render();
};
this.model = new DialogModel();
this.model.fetch({ success : onDataHandler});
},
render: function(){
var data = {
dialogModel : this.model
};
var form = new Backbone.Form({
model: data
});
$(this.el).html(form.render().el);
}
});
What happens now:
DialogView initialize is called.
this.model.fetch is called but the onDataHandler function will not be hit if success.
/messages/getDialog throws a json file back.
The json file is loading well as i can see in the network browser.
Thanks for your help!
Oleg
The problem you're having is due to a typical JS gotcha and not related to Backbone itself. Try
var that = this;
this.model.fetch({
success : function () {
that.render();
}
});
The way you're currently passing onDataHandler is problematic as it will cause this to refer to the global object instead of the DialogView, when the function is called.
This fiddle demonstrates the problematic version vs one that works.
(You may also want to take a look at JS strict mode which can shield you from this type of errors.)
Even better is to listen for an event:
this.model.on("sync", this.render).fetch();
I ran across this question while looking for something else, but the currently accepted answer drives me nuts. There's no good reason to be sprinkling this and that all over your code. Backbone (underscore) includes a context parameter that you can bind to.
that = this makes no sense. If you must implement obsolete 2007-era Crockford patterns, then say var self = this. Saying that = this is like saying left = right. Everyone Stop.