How to pass a model to AngularJS fileuploader? - angularjs

I want to upload few files to server with angularjs-file-uploader. I have an array of viewModels, I want to upload files and get the IDs of uploaded files and assign them to corresponding viewModels.
Is there any way to pass my viewModels to fileUploader? Here are the codes I have written so far.
<div ng-if="vm.isFile(v.formItem)" class="form-group form-md-line-input form-md-floating-label no-hint">
<div class="form-group">
<input type="file" name="v.id" nv-file-select uploader="vm.uploader" />
<span class="help-block m-b-none">{{v.formItem.description}}</span>
</div>
</div>
I want to pass a "v.formItem" which has a property for uploadedFileId. so that I can assign uploaded file's Id to my Model.
this.uploader = new FileUploader({
url: 'File/UploadFile',
autoUpload: true,
removeAfterUpload: true,
queueLimit: 1
});
this.uploader.onSuccessItem = function (fileItem, response, status, headers) {
if (response.success) {
formItem.uploadedFileId = response.result.id ;
}
};

I found the solution. I just needed to use "options" property for file input. like this:
<input type="file" name="v.id" options={formItem:v.formItem} nv-file-select uploader="vm.uploader" />
javascript:
this.uploader.onSuccessItem = function (fileItem, response, status, headers) {
if (response.success) {
var formItem = fileItem.formItem;
formItem.uploadedFileId = response.result.id ;
}
};

Related

Angular 1 dynamic form object

I'm using Angular 1 and creating a dynamic form. It works by looping through some objects and rendering dynamically binded input fields like:
<div class="quest-form form-group" ng-repeat="task in tasks">
<div ng-if="task.Class == 'TaskText'" ng-class="'task ' + task.Class">
<input ng-model="questForm.Task[task.ID].Value" ng-name="task_{{task.ID}}" ng-required="task.Required == 1" type="text" class="form-control" placeholder="{{task.Title}}" />
</div>
...
...
</div>
I also have a upload field in the loop:
<div ng-if="task.Class == 'TaskUpload'" ng-class="'task ' + task.Class">
<input class="btn btn-primary upload-btn" ngf-max-size="10MB" type="file" ng-model="upload" ngf-multiple="false" ngf-select="uploadFile(upload, task.ID, $invalidFiles)" />
<input class="" ng-model="questForm.Task[task.ID].FileUploadID" ng-required="task.Required == 1" ng-name="task_{{task.ID}}" type="text" />
</div>
When the file uploaded event is called I'm trying to set the value of the hidden field which is ng-model="questForm.Task[task.ID].FileUploadID" like this:
$scope.uploadFile = function(file,taskID) {
file.upload = Upload.upload({
url: assetsURL+'/home/UploadFile',
data: {file: file}
});
file.upload.then(function (response) {
$scope.questForm.Task[taskID].FileUploadID = response.data; // THIS MESSES UP
}, function (response) {
...
});
};
I get the following error, it's like $scope.questForm.Task[128] does not exist even though the hidden input looks correct and is binded to the $scope.questForm.Task[128].
angular.js:14362 TypeError: Cannot read property '128' of undefined
at file.upload.then.$scope.errorMsg (http://localhost/carl-mygps-app/js/controllers/quest-details-controller.js:120:26)
at processQueue (http://localhost/carl-mygps-app/bower_components/angular/angular.js:16689:37)
at http://localhost/carl-mygps-app/bower_components/angular/angular.js:16733:27
at Scope.$eval (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18017:28)
at Scope.$digest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:17827:31)
at Scope.$apply (http://localhost/carl-mygps-app/bower_components/angular/angular.js:18125:24)
at done (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12233:47)
at completeRequest (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12459:7)
at XMLHttpRequest.requestLoaded (http://localhost/carl-mygps-app/bower_components/angular/angular.js:12387:9) Possibly unhandled rejection: {}
I have tried defining blank objects in the scope like:
$scope.questForm = [];
$scope.questForm.Task = {};
But I should not need to because they are created in the template? confused. Thanks all.
Actually nope. Having your template compiled does not mean all the ng-models are initialized. While ng-model is smart enough to create all the intermediate objects, if they don't exist, it doesn't do so until $viewValue is changed. In your case if you upload a file without editing any other input first, $viewValue for inputs has never changed, and thus you have to initialize questForm, questForm.Task, and questForm.Task[taksID] yourself.
if (!$scope.questForm) {
$scope.questForm = {};
}
if (!$scope.questForm.Task) {
$scope.questForm.Task = {};
}
if (!$scope.questForm.Task[taskID]) {
$scope.questForm.Task[taskID] = {};
}
$scope.questForm.Task[taskID].FileUploadID = response.data;
Or you can initialize questForm and questForm.Task at the beginning. And only check if questForm.Task[taskID] exists before initializing it.

Is this the correct way of binding factory to controller?

I have the following code in my module:
.controller('ModalInstanceCtrl', function($rootScope, $scope, emailService) {
$scope.emailService = emailService; // Good or not; if not, why?
$scope.showed = false;
$rootScope.$watch('showed', function () { $scope.showed = $rootScope.showed; }); // In case you wonder why I did this - I'm using this trick to prevent watch from firing twice, because that would happen if I remove the watch below and put its code here.
$scope.$watch('showed', function () {
if (!$rootScope.showed) return;
$scope.selected = 0;
$scope.primary = true;
$scope.verified = true;
if (emailService.emails.length == 0) emailService.load();
});
$scope.EmailSelected = function () {
emailService.setCurrent($scope.selected);
$scope.primary = emailService.emails[$scope.selected].primary;
$scope.verified = emailService.emails[$scope.selected].verified;
};
});
.factory('emailService', function($resource, $http) {
var emails = []; // [{email: 'sample#email.dom', verified: true, primary: false}, ...]
var selected = 0;
function sendreq(action, email){
$http({
method: 'POST',
url: '/email/',
data: "action_" + action + "=&email=" + email,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function(response) {
console.log(response.data);
return true;
}, function(data){
return data;
});
}
return {
emails: emails,
selected: selected,
setCurrent: function(curr){
selected = curr;
},
load: function(){
$resource('/api/email/?format=json').query({},
function success(result) {
emails.push.apply(emails, result);
});
},
add: function(email) {
for (var e in emails) if (emails[e].email == email) return false;
return sendreq('add', email);
},
remove: function() {
sendreq('remove', emails[selected].email);
}
}
})
And this code in my HTML template:
<div ng-repeat="e in emailService.emails">
<input type="radio" ng-model="$parent.selected" ng-value="$index" ng-change="EmailSelected()" id="email_{{ $index }}" name="email">
<label for="email_{{ $index }}" ng-bind='e.email'></label> <span ng-show="e.verified">Verified</span> <span ng-show="e.primary">Primary</span>
</div>
<div><button ng-disabled="primary" ng-click="emailService.remove()">Remove</button></div>
<form novalidate>
<input class="form-control" type="email" name="email" ng-model="email" placeholder="Email">
<input type="submit" ng-disabled="email === undefined" ng-click="emailService.add(email)" value="Add Email Address">
</form>
And I want to ask, whether I've correctly assembled the module and template because I'm working with AngularJS for the first time. Specifically, I want to ask if it's correct to bind the whole factory to the scope? Also if someone has more time he can look at the other code to see if everything is right or not. Feel free to write any suggestions about my code.
Thanks in advance!
It always depends on particular case.
This way boilerplate wrapper methods
$scope.add = (...args) => emailService.add(...args);
can be omitted, as well as their tests in controller spec.
Another benefit is that it provides existing object for proper data binding and scope inheritance of scalar scope properties:
<parent-scope>
<p ng-init="emailService.selected = 0"></p>
<child-scope>
<p ng-init="emailService.selected = 1"></p>
{{ emailService.selected === $parent.emailService.selected }}
</child-scope>
</parent-scope>
This certainly would not work as expected if there's no emailService object. This is particularly useful when controllerAs syntax isn't used.
There's nothing wrong with exposing a service to scope - if its API matches the controller scope. And this may indicate an antipattern if it doesn't - or if there are too many services that are abused like that.
Why do you want to bind the entire service ? I do not see a need to that in your code. You are invoking parts of the service using the service handler, there is no specific need to put the entire service on scope.

angularjs and grails 2.5.0 save success, no errors but not saving to database

I've been looking for similar situations like mine has and I've seen that it is common to have this error but I can not find the right solution with my problem. I would really appreciate any help.
My problem right now is that when I execute this it return and HTTP 200 and also prints a line on my terminal which confirms me that it creates the JSON file. There are no errors but when I checked on my database, there's nothing in it.
What I've tried so far:
newBook.save(flush: true)
edit my datasource from create-drop to update (but my whole database disappears so I returned the data. I only need to save it during a demo so I understand that the data will be lost every time the application restarts)
Postman GET and POST
GET: 200 OK
POST: 200 OK
but when I GET again, nothing is showing
debugging using console and println (but there might be something that I missed since I'm just starting to learn both AngularJS and Grails)
I am hoping that these data can give a detailed information about my problem, I am open for any questions.
Here are my sample codes:
back-end codes: Grails 2.5.0
Domain class
class Book {
String name
int page
boolean new
static belongsTo = [student: student]
static constraints = {
}
}
Controller
import grails.converters.JSON
import org.springframework.security.access.annotation.Secured
#Secured('permitAll')
class BookController {
def index() {
render Book.list() as JSON
}
def save() {
def newBook = new Book(request.JSON)
newBook.save(flush: true)
println request.JSON
render(['success': true] as JSON)
}
def show() {
def Book = Book.get(params.id)
render Book as JSON
}
}
front-end codes: AngularJS
Controller
app.controller('Book', function($scope, store, $state, bookFactory){
$scope.book = {}
$scope.saveBook = function() {
$scope.book.assessment = $scope.getBook();
console.log($scope.book);
bookFactory.save($scope.book,function (result){
console.log(result)
}, function (error) {
console.log(error)
})
}
$scope.getBook = function() {
var result = "";
if ($scope.book.page > 10){
result = "many pages";
}else if ($scope.book.new && $scope.book.page > 20){
result = "new and many pages";
}else {
result = "old book";
}
return result;
};
})
Service
app.factory('bookFactory', ['$resource', function ($resource) {
return $resource('api/book',
{
'update': {method: 'PUT'}
})
}])
app.js
.state("book", {
url: "/book",
templateUrl: "assets/app/partials/book.html",
controller: 'Book',
data: {
requiresLogin: true
}
})
HTML
Book Name:
<input type="text" ng-model="book.name" name="bookName" required="" class="form-control" />
Number of Pages:
<input type="number" ng-model="book.page" name="numberPage" required="" class="form-control" placeholder="Number of Pages"/>
Bought within the last 30 days?
<div class="form-group">
<div class="radio">
<label>
<input type="radio" name="new" value=true ng-model="book.new" >
<font color=green>YES</font>
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="new" value=false ng-model="book.new">
<font color=red>NO</font>
</label>
</div>
</div>
<a ui-sref="book" class="btn btn-primary" name="bookBtn" ng-click="saveBook()"> Next </a>

How to get binding value in input hidden value Angular

I have file upload and I can upload file then get controller value put in input hidden value because I will be post form.
HTML CODE
<input type="file" class="form-control" id="i_file5" file-model="process.step.file5" ng-file-select="onFileSelect($files5)" />
<input type="hidden" ng-model="{{files.name}}"/>
Controller Code
$scope.uploadResult = [];
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
$upload.upload({
url: 'lib/fileupload/',
file: $file,
progress: function(e){}
}).then(function(response) {
// file is uploaded successfully
$timeout(function() {
$scope.uploadResult.push(response.data);
console.log($scope.uploadResult);
});
});
}
}
I am assuming you have already specify controller in HTML. If not please implement as shown in below example. For ng-model you should not use {{ }} as you are binding the value not evaluating it.
<div ng-controller="files">
----
<input type="text" data-ng-model="files.name" style="display:none"/>
</div>

ngResource GET with filter values

I'm writing a small test application where I can retrieve my customers from a parse.com database.
I have the following form in html
...
<body ng-app="myApp">
<div ng-controller="CustomerController">
<button ng-click="getCustomers()">Get customers</button>
<ul>
<li ng-repeat="customer in customers">{{ customer.name }}</li>
</ul>
</div>
</body>
...
My angular app is the following:
Module
var app = angular
.module('myApp', ['ngResource'])
.constant('myConfig', {
'api_url': 'https://api.parse.com/1/classes/',
'parse_application_id': 'xxxxxxxxxxxxx',
'parse_rest_api_key': 'xxxxxxxxxxxxx'
});
Factory
app.factory('CustomersService', function($resource, myConfig) {
return $resource(myConfig.api_url + 'Customer', {}, {
query: {
method: 'GET',
isArray: false,
headers: {
'X-Parse-Application-Id': myConfig.parse_application_id,
'X-Parse-REST-API-Key': myConfig.parse_rest_api_key
}
},
create: {
method: 'POST',
headers: {
'X-Parse-Application-Id': myConfig.parse_application_id,
'X-Parse-REST-API-Key': myConfig.parse_rest_api_key
}
}
})
});
Controller:
app.controller('CustomerController', function($scope, CustomersService, CustomerService) {
$scope.getCustomers = function() {
CustomersService.query().$promise.then(function(result) {
$scope.customers = result.results;
});
};
});
So when I click my button, everything works like it should.
But I also want to add a filter by name when I want to retrieve customers from the database.
When I execute the following in Postman
https://api.parse.com/1/classes/Customer?where={"name":"aaaaa"}
this works and only gets the customer with the name "aaaaa". So I know that the syntax is OK.
So I will add a textbox where the user can enter a customername and after that I want to click on the search button.
But how can I manage the ?where={"name":"aaaaa"} into the angular stuff when I click the button? I also want to expand the filter with other columns from that customer.
Something like this should work (assuming everything goes in the where object)
Add some search fields that bind to a scoped object's properties. We'll call it search
<label for="search_name">Name</label>
<input type="text" ng-model="search.name" name="name" id="search_name">
<label for="search_city">City</label>
<input type="text" ng-model="search.city" name="city" id="search_city">
Then you can execute the query action with
CustomersService.query({where: $scope.search}).$promise...
That should create a query param like
?where=%7B%22name%22%3A%22aaaaa%22%2C%22city%22%3A%22London%22%7D
which is the URI encoded value
?where={"name":"aaaaa","city":"London"}

Resources