RESTLET - Angular.JS File Upload on Client Side - javascript

We are using Restlet for our API on the client side, everything server side is written in Laravel 5. We are having trouble in one portion. We have a couple endpoints that require you to upload a file. In Angular, I have only gotten that to work using the following method thus far:
var fd = new FormData();
fd.append('image', $scope.file);
$http.post(apiURL + "/upload", fd, {
transformRequest: angular.identity,
headers: {
'Content-Type': undefined
}
}).then(function(response) {
//yay it worked
}, function(response) {
//try again
});
I don't know why that is the only way I have been successful, but I would like to change it over to the Restlet endpoint I have created. For most of my calls, it is as simple as this:
$rootScope.restlet.getCompanyAll().then(function(response) {
$scope.companies = response.data;
});
and
var config = {
params: {
start: "2016-01-01",
end: "2016-01-31"
}
};
var id = 1;
$rootScope.restlet.getCompanyStatsCompany_id(id, config).then(function(response) {
$scope.companies = response.data;
});
Pretty simple, but when I try to implement the post of an image, it doesn't recognize it, and leaves the image out completely. Here is the code I am attempting to use, it works with the non-Restlet way, but doesn't work with Restlet.
var config = {
params: {
name: $scope.newCompany.name,
rep_id: $scope.newCompany.rep_id,
image: $scope.image_input
}
};
var id = 1;
$rootScope.restlet.postCompanyCreate(config).then(function(response) {
$scope.companies = response.data;
});
Has anyone gotten something like this to work? And if so, how does it work? Thanks! :)
EDIT 1:
Here is the HTML of the page I have set up. It does have a file input, but for some reason it Restlet doesn't like the file. I have tried a plain file input, along with an input with a directive on it. The current version I am using is an image, that when clicked is linked to an file input that is hidden. I am also using a custom directive on it currently.
HTML:
<div class="modal-body">
<form ng-submit="createCompany()">
<!-- OTHER FORM STUFF GOES HERE -->
<div class="col-sm-12">
<img src="" id="imagePreview" onClick="$('#imageUpload').trigger('click');" style="max-width: 100%;" />
<input type="file" style="display: none;" id="imageUpload" file="file" />
</div>
<!-- FORM SUBMIT AND RESET BUTTONS HERE -->
</form>
</div>
Custom Directive:
app.directive('file', function() {
return {
scope: {
file: '='
},
link: function(scope, el, attrs) {
el.bind('change', function(event) {
var file = event.target.files[0];
scope.file = file ? file : undefined;
scope.$apply();
});
}
};
});

You didn't post your HTML but I assume that you are using an input with a type of file to specify the file to upload and binding to it with ng-model or some native Angular binding mechanism. I have no idea why, but Angular doesn't support this. There are 2 common ways to implement this.
The 1st is to use a directive that works around this issue. There's one here: https://github.com/danialfarid/ng-file-upload.
The 2nd, and at least where I would start, is you can simply use document.getElementById to retrieve the file name from the input in your controller. IOW, $scope.file = document.getElementById("myFileThingy").value.

Related

How to add multiple files into a array and access files one by one in AngularJs

I am trying to add multiple files into a file array and later on access the each files in AngularJs.So far i have created a custom directive to read files looking at some posts.it was working fine with a single file.But my requirement is that i want to upload multiple files and add them into array and later on access the created file array when accessing the data.I am newbie to AngularJs please help me to achieve this.
HTML code snippet
<div class="input input-file ">
<span class="button"><input on-read-file="readFileContent($fileContent)"
type="file" id="attachments" name="file" data-ng-model="attachments"
onchange="this.parentNode.nextSibling.value = this.value">Browse</span>
<input type="text" placeholder="Attach some files" readonly="">
</div>
Custom directive for read input files
var mimeType,fileName;
testModule.directive('onReadFile', function ($parse) {
return {
restrict: 'A',
scope: false,
link: function(scope, element, attrs) {
var fn = $parse(attrs.onReadFile);
element.on('change', function(onChangeEvent) {
var reader = new FileReader();
reader.onload = function(onLoadEvent) {
scope.$apply(function() {
fn(scope, {$fileContent:onLoadEvent.target.result});
});
};
reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
//Get the Uploaded file mime type
mimeType=(onChangeEvent.srcElement || onChangeEvent.target).files[0].type;
fileName=(onChangeEvent.srcElement || onChangeEvent.target).files[0].name;
});
}
};
});
Read File Content Method
$scope.readFileContent = function($fileContent){
$scope.content = $fileContent;
};
I suggest to check this post on StackOverflow:
Upload multiple files in angular
One of the answers contains this working example:
http://plnkr.co/edit/B13t84j5IPzINMh1F862?p=preview
It uses a directive named ng-file-model:
<input type="file" ng-file-model="files" multiple />

Displaying a selected file from an <input> in AngularJS

I have a working example using standard Javascript, but I'd like to make this work more natively with AngularJS.
Specifically, I need to update the span with the filename of the file selected by the user.
Here's what I implemented using native Javascript:
<span>
<input ng-model="uploadDownloads" type="file" style="visibility:hidden; width: 1px;" id=uploadDownloads name=uploadDownloads onchange="$(this).parent().find('span').html($(this).val().replace('C:\\fakepath\\', ''))" /> <!-- Chrome security returns 'C:\fakepath\' -->
<input class="btn btn-primary" type="button" value="choose file" onclick="$(this).parent().find('input[type=file]').click();"/> <!-- on button click fire the file click event -->
<span class="badge badge-important" ></span>
</span>
The filereader function is in angular already :
$scope.add = function(valid){
if(valid){
$scope.data = 'none';
var f = document.getElementById('uploadDownloads').files[0];
var r = new FileReader();
r.onloadend = function(e){
$scope.data = e.target.result;
$scope.notPass = false;
$modalInstance.close({
'data':$scope.data,
'fileName':$scope.fileName,
'fileExplain':$scope.fileExplain
});
};
/*activate the onloadend to catch the file*/
r.readAsBinaryString(f);
} else {
$scope.notPass = true;
}
};
The problem is to activate the onclick and the onchange with Angular instead the JavaScript so that my <span> gets updated with the selected filename.
This question builds upon an existing question and answer. Specifically, however, I have modified the code from that answer to accomodate what appears to be the specific question here, which is how do you update a <span> to have the filename selected by a user in a way that's idiomatic to angularjs.
Here's a codepen with a working sample.
Here's the relevant part of the html file:
<body ng-controller="AppController">
<input ng-model="uploadDownloads" type="file" fd-input file-name="fileName"/>
<span class="badge badge-important">Output here: {{fileName}}</span>
</body>
What's key here is that you have a custom directive called fd-input that has a two-way binding to an attribute it defines called file-name. You can pass one of your $scope variables into that attribute and the directive will bind the filename to it. Here's the controller and the directive.
(function() {
'use strict';
angular.module('app', [])
.controller('AppController', AppController)
.directive('fdInput', fdInput);
function AppController($scope) {
$scope.fileName = '';
}
function fdInput() {
return {
scope: {
fileName: '='
},
link: function(scope, element, attrs) {
element.on('change', function(evt) {
var files = evt.target.files;
console.log(files[0].name);
console.log(files[0].size);
scope.fileName = files[0].name;
scope.$apply();
});
}
}
};
})();
As mentioned above, the directive is taken directly from another SO answer. I have modified it to add a scope that does a two way binding to a file-name attribute:
...
return {
scope: {
fileName: '='
},
...
I then assign files[0].name to the two-way binding:
...
scope.fileName = files[0].name;
scope.$apply();
...
Checkout the codepen. That should do it. You could just use the parent scope in the directive, but that's not a good idea as it limits you to using this directive once per controller. Also, if you want to list multiple files, you'll have to update this code to return an array of those files instead.
Hope this help.

Want to submit data from a form as a new Object in a JSON array using Angular

I am looking to create a simple input box with a submit button that will add information to a JSON file. I am using Angular to retrieve that data, create a new JSON object and post this object with the new data to a file called pets.json.
HTML
<form ng-submit="submit()" ng-controller="PetPosting">
Enter text and hit enter:
<input type="text" ng-model="text" name="text" />
<input type="submit" id="submit" value="Submit" />
</form>
Angular JS
var app = angular.module('app', ['ui.bootstrap', 'bootstrapLightbox']);
//load images from pets.json and display them in HTML
app.controller('GalleryCtrl', function ($scope, Lightbox, $http) {
$http.get('pets.json')
.then(function(res){
$scope.images = res.data;
});
});
/*Add new data to JSON file*/
app.controller('PetPosting',function($scope, $http) {
$scope.submit = function() {
if ($scope.text) {
$http({
method: "post",
url: "pets.json",
data: {
"thumbUrl": $scope.text,
}
});
}
}
});
I don't understand fully how this works. Any insight or direction on how to figure out how to do this simple task would be greatly appreciated :)
Thats not the correct implementation of "$http", the "url" property is a server endpoint that receives your Data, for that you would need some server-side code to append to the .json file.
If your wanting to save information Local to the user the LocalStorage API maybe sufficient and is very easy to implement

Updating multi-model form from Angular to Sinatra

I'm currently having an issue with updating a form in Angular and pushing the update through to Sinatra.
It is supposed to:
When clicked, the form to edit the current item is shown (current data for each field is displayed from the item scope).
When submitted, it is attempting to update to a different scope (updateinfo). I am not sure but do I need a way of using multiscope or one scope to allow it to update?
At present the script sends the correct downloadID parameter, but the JSON from the scope submitted is as I believe, incorrect.
Also, I'm not sure whether the Sinatra app.rb syntax is correct, for someone new to these frameworks, it has been hard to find useful documentation online.
If anybody could help it would be very much appreciated.
downloads.html
<div ng-show="showEdit">
<form ng-submit="updateinfo(item.downloadID); showDetails = ! showDetails;">
<div class="input-group"><label name="title">Title</label><input type="text"
ng-model="item.title"
value="{{item.title}}"/></div>
<div class="input-group"><label name="caption">Download caption</label><input type="text"
ng-model="item.caption"
value="{{item.caption}}"/>
</div>
<div class="input-group"><label name="dlLink">Download link</label><input type="url"
ng-model="item.dlLink"
value="{{item.dlLink}}"/>
</div>
<div class="input-group"><label name="imgSrc">Image source</label><input type="url"
ng-model="item.imgSrc"
value="{{item.imgSrc}}"/>
</div>
<!-- download live input types need to be parsed as integers to avoid 500 internal server error -->
<div class="input-group"><label name="imgSrc">
<label name="dlLive">Download live</label><input type="radio" ng-model="download.dl_live"
value="1"/>
<label name="dlLive">Not live</label><input type="radio" ng-model="download.dl_live"
value="0"/></div>
<div class="input-group"><label name="imgSrc"><input type="submit"/></div>
</form>
controllers.js
$scope.loadData = function () {
$http.get('/view1/downloadData').success(function (data) {
$scope.items = data;
});
};
$scope.loadData();
$scope.updateinfo = function(downloadID) {
id = downloadID
var result = $scope.items.filter(function( items ) {
return items.downloadID == id;
});
console.log(result);
updatedata = $scope.items
$http({
method : 'PUT',
url : '/view1/downloadedit/:downloadID',
data : result
});
};
app.rb
#edit download
put '/view1/downloadedit' do
puts 'angular connection working'
ng_params = JSON.parse(request.body.read)
puts ng_params
#download = Download.update(ng_params)
end
The wrong scope was attempting to be used. Once the scope was corrected to items, the correct JSON was being routed:
$scope.updateinfo = function(downloadID) {
id = downloadID
var result = $scope.items.filter(function( items ) {
return items.downloadID == id;
});
console.log(result);
updatedata = $scope.items
$http({
method : 'PUT',
url : '/view1/downloadedit/:downloadID',
data : result
});

Unobtrusive JavaScript ajax with mootools?

Unobtrusive JavaScript with jQuery is available in MVC3.But how can I use the unobtrusive Javascript ajax with mootools?
yeah, this is trivial to do. have a look at the recently released http://mootools.net/blog/2011/12/20/mootools-behavior/, I think it supports it.
I have used this approach in my Modal.BootStrap (view source on github, link's there) as well whereby it uses data attributes to fetch data from an ajax resource, it's not quite the same but it certainly is a start.
I just spent 10 mins making this and it's a good start:
http://jsfiddle.net/dimitar/zYLtQ/
(function() {
var ajaxify = this.ajaxify = new Class({
Implements: [Options,Events],
options: {
mask: "form[data-ajax=true]",
props: {
ajaxLoading: "data-ajax-loading",
ajaxMode: "data-ajax-mode",
ajaxUpdate: "data-ajax-update",
ajaxSuccessEvent: "data-event-success"
}
},
initialize: function(options) {
this.setOptions(options);
this.elements = document.getElements(this.options.mask);
this.attachEvents();
},
attachEvents: function() {
this.elements.each(function(form) {
var props = {};
Object.each(this.options.props, function(value, key) {
props[key] = form.get(value) || "";
});
form.store("props", props);
form.addEvent("submit", this.handleSubmit.bind(this));
}, this);
},
handleSubmit: function(e) {
e && e.stop && e.stop();
var form = e.target, props = form.retrieve("props"), self = this;
var updateTarget = document.getElement(props.ajaxUpdate);
new Request({
url: form.get("action"),
data: form,
onRequest: function() {
if (props.ajaxLoading) {
var loading = document.getElement(props.ajaxLoading);
if (loading && updateTarget) {
updateTarget.set("html", loading.get("html"));
}
}
},
onSuccess: function() {
if (!updateTarget)
return;
if(props.ajaxMode != 'append') {
updateTarget.set("html", this.response.text);
}
else {
updateTarget.adopt(new Element("div", { html: this.response.text }));
}
if (props.ajaxSuccessEvent)
self.fireEvent(props.ajaxSuccessEvent, this.response);
}
}).send();
}
});
})();
new ajaxify({
onContactFormSuccess: function(responseObj) {
console.log(responseObj.text);
alert("we are done.");
}
});
works with a DOM of:
<form action="/echo/html/" data-ajax="true" data-ajax-loading="#loading" data-ajax-mode="replace" data-ajax-update="#update" data-event-success="contactFormSuccess" method="post">
<input name="delay" value="4" type="hidden" />
<input name="html" value="Thanks for your submission, this is the jsfiddle testing response" type="hidden" />
<input name="name" placeholder="your name" />
<button>submit</button>
</form>
<div id="update">The update will go here.</div>
<div id="loading">loading...</div>
you should be able to build on that. on refactor i'd move the request events into their own methods and add some more proofing etc but it's fine. i don't know all mvc does but one thing that is missing is form validation events. i also added a custom event that is fired when done so your ajaxifier instance can do something particular to that form (see data-event-success="contactFormSuccess")
also, it can use default request options if not implicitly specified, even what request object to create - Request, Request.HTML, Request.JSON etc. Events like onRequest, spinners etc are also feasible... I think you just need to work your way through the options that mvc provides and build them to get started.
Confirm data-ajax-confirm
HttpMethod data-ajax-method
InsertionMode data-ajax-mode *
LoadingElementDuration data-ajax-loading-duration **
LoadingElementId data-ajax-loading
OnBegin data-ajax-begin
OnComplete data-ajax-complete
OnFailure data-ajax-failure
OnSuccess data-ajax-success
UpdateTargetId data-ajax-update
Url data-ajax-url

Categories

Resources