file in the controller says undefined - angularjs

I am having trouble while uploading a file. I have created one service:
angular
.module('app')
.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}}]);
then i have a directive:
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};}]);
And in the controller:
$scope.uploadFile = function(){
var file = $scope.myFile;
console.log('file is ' );
console.dir(file);
var uploadUrl = "/fileUpload";
fileUpload.uploadFileToUrl(file, uploadUrl);
};
HTML:
Browse <input type="file" file-model="myFile"/>
<button ng-click="uploadFile()">upload me</button>
As you can see i am using console.log in the controller and what i am having is
file is undefined
Thanks in advance!

change
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
scope: {
percent: "=fileModel"
},
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
scope.$watch('percent', function(value) {
console.log(value)
});
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};}]);
check the console value inside the percentage watch once you upload file

Related

Angular thumbnail from local video file

I am trying to generate thumbnails using angular-thumbnails (https://github.com/aztech-digital/angular-thumbnails) from local video files instead of video urls, this is what I have so far, it only works with some videos: https://plnkr.co/edit/j7LR7FU0itRmPmLJWgwT?p=preview
var app = angular.module('plunker', ['angular-thumbnails']);
app.controller('MainCtrl', function($scope) {
$scope.$watch('myFile', function(newVal, oldVal) {
var reader = new FileReader();
reader.addEventListener('load', function() {
$scope.myVideoData = reader.result;
console.log('myVideoData', $scope.myVideoData);
}, false);
if (newVal) {
reader.readAsDataURL(newVal); 
}
});
});
app.directive('fileModel', function() {
return {
restrict: 'A',
scope: {
fileModel: '='
},
link: function(scope, element, attrs) {
element.bind('change', function(e) {
scope.$apply(function() {
scope.fileModel = e.target.files[0];
});
});
}
};
});

How to update which template is shown based on what the user selects from the select box with a custom directive

I am trying to create a page that dynamically loads a template based on the option that the user chooses from a select box. I currently have it loading the template on page load but after that it does not change based on user action.
.directive('ngUsersearch', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {
var getTemplate = function(contentType) {
var templateLoader,
baseUrl = 'view2/components/',
templateMap = {
beer: 'beerList.html',
brewery: 'breweryList.html',
event: 'eventList.html',
guild: 'guildList.html'
};
var templateUrl = baseUrl + templateMap[contentType];
templateLoader = $http.get(templateUrl, {cache: $templateCache});
return templateLoader;
}
var linker = function(scope, element, attrs) {
var loader = getTemplate(scope.ngModel);
var promise = loader.success(function(html) {
element.html(html);
}).then(function (response) {
element.replaceWith($compile(element.html())(scope));
});
}
return {
restrict:"E",
scope: {
ngModel: '='
},
link: linker
}
}]);
Here is my HTML:
<select ng-model="userFilter">
<option value="beer">Beer</option>
<option value="brewery">Brewery</option>
<option value="event">Event</option>
<option value="guild">Guild</option>
</select>
<ng-usersearch ng-model="userFilter"></ng-usersearch>
you forgot listen the change event of the model;
var linker = function(scope, element, attrs) {
scope.$watch('ngModel', function(newValue, oldValue) {
var loader = getTemplate(newValue);
var promise = loader.success(function(html) {
element.html(html);
}).then(function (response) {
element.replaceWith($compile(element.html())(scope)); // you compile and you have isolated scope?
});
});
}
on your compile the only scope available would be ngModel
This solution worked for me. I switched the way that the directive was loading the template. This can be done at the link function, but after the directive is set up and a part of the DOM, I was trying to remove the directive itself from the DOM by replacing it, which does not play well with how Angular's selectors work. So, now I am just replacing its contents. Also, in order to get the ng-repeat to work within the custom directive I had to add the search-results='searchResults' and then define that in the directives scope as well.
HTML:
<ng-usersearch ng-model="userFilter" search-results='searchResults'></ng-usersearch>
Controller:
.controller('View2Ctrl', [ '$scope', 'Restangular', function($scope, Restangular) {
$scope.userSearch = "";
$scope.userFilter = "beer";
$scope.search = function(userSearch, userFilter) {
$scope.searchResults = ("No " + userFilter + " Information Available");
Restangular.all('search?q=' + userSearch + '&type=' + userFilter + '&withBreweries=Y').customGET().then(function(data) {
$scope.searchResults = data;
});
};
}])
Directive:
.directive('ngUsersearch', ['$http', '$templateCache', '$compile', function($http, $templateCache, $compile) {
var getTemplate = function(contentType) {
var templateLoader,
baseUrl = 'view2/components/',
templateMap = {
all: 'all.html',
beer: 'beerList.html',
brewery: 'breweryList.html',
event: 'eventList.html',
guild: 'guildList.html'
};
var templateUrl = baseUrl + templateMap[contentType];
templateLoader = $http.get(templateUrl, {cache: $templateCache.get()});
return templateLoader;
}
var link = function(scope, element) {
scope.$watch('ngModel', function(newValue, oldValue) {
var loader = getTemplate(newValue);
var promise = loader.success(function(html) {
var rendered = $compile(html)(scope);
element.empty();
element.append(rendered); });
});
}
return {
restrict:"E",
scope: {
ngModel: '=',
searchResults: '='
},
link: link
}
}]);
I hope this helps other coders because I struggled with this for a day.

How to test a directive having hardcoded AJAX call?

I am not sure how can I test this directive, would someone provide a code snippet? Here is my directive:
.directive('checkUnique', ['$http', function($http) {
return {
require: 'ngModel',
link: function(scope, ele, attrs, c) {
var origonalVal = attrs.ignoreUniqueValue;
scope.$watch(attrs.ngModel, function() {
var toURL= 'to/an/api';
$http({
method: 'GET',
url: toURL
}).success(function(isUnique,status,headers,cfg) {
var iu = isUnique == 'true' ? true : false;
c.$setValidity('unique', iu);
}).error(function(data,status,headers,cfg) {
c.$setValidity('unique', false);
});
});
}
}
}])
First of all it is not a good idea to have this logic in the link function of your directive. Here a setup that I would use (simplified and not tested):
var myApp = angular.module('myApp', []);
myApp.factory('dataService', function($q, $http){
return {
isUnique: function(){
return $q(function(resolve, reject){
$http({
method: 'GET',
url: 'to/an/api'
}).success(function(isUnique,status,headers,cfg) {
resolve(isUnique == 'true');
}).error(function(data,status,headers,cfg) {
reject();
});
});
}
}
});
myApp.controller('UniqueController', function($scope, dataService){
var vm = this,
unWatchNgModel;
unWatchNgModel = $scope.$watch('ngModel', onNgModelChanged);
$scope.$on('$destroy', onDestroy);
function onNgModelChanged(){
dataService.isUnique().then(function(unique){
vm.ngModelCtrl.$setValidity('unique', unique);
});
}
function onDestroy(){
unWatchNgModel();
}
});
myApp.directive('checkUnique', ['$http', function($http) {
return {
require: ['checkUnique', 'ngModel'],
scope: {
ngModel: '='
}
controller: 'UniqueController',
controllerAs: 'unique',
bindToController: true
link: link
};
function link(scope, ele, attrs, ctrls) {
var checkUniqueCtrl = ctrls[0],
ngModelCtrl = ctrls[1];
checkUniqueCtrl.ngModelCtrl = ngModelCtrl;
}
}]);
To test this (the ajax part), use a setup like this:
// Note that you need the 'ngMockE2E' module to have access to the $httpBackend service.
describe('dataService', function() {
'use strict';
var dataService;
beforeEach(function() {
module('myApp');
inject(function($injector) {
dataService = $injector.get('dataService');
$httpBackend = $injector.get('$httpBackend');
});
});
describe('isUnique', function() {
it('should return true if the API returns true as value.', function() {
// Setup
var successSpy = jasmine.createSpy('success');
$httpBackend.expectGET(endpoint).respond(200, 'true');
// Execute
dataService.isUnique(successSpy);
$httpBackend.flush();
// Test
expect(successSpy).toHaveBeenCalledWith(true);
});
it('should return false if the API does not return true as value.', function() {
// Setup
var successSpy = jasmine.createSpy('success');
$httpBackend.expectGET(endpoint).respond(200, 'bogus');
// Execute
dataService.isUnique(successSpy);
$httpBackend.flush();
// Test
expect(successSpy).toHaveBeenCalledWith(false);
});
});
});

Angular.js Accessing input field value inside controller $scope

I've got a directive which defines a input field of type="file", which I can print and is not empty, namely:
<form class="form-horizontal" role="form">
<input type="file" file-model="myFile"/>
{{myFile}} <-- this prints fine
<button type="submit" class="btn btn-success" ng-click="saveData()">Post</button>
</form>
which I can see if called in the view
app.js
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
What I am trying to do now is access the field inside my controller:
.controller('Ctrl', function($scope, fileUpload) {
...
$scope.myFile; <-- initialise it
$scope.saveData = function() {
var file = $scope.myFile;
console.log(file); <-- prints out as undefined
}
.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function(file, uploadUrl){
var fd = new FormData();
fd.append('file', file);
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
}
}]);
But file is undefined.
Any ideas why that would happen and how to access the value of the field?
If you want to bring in attribute values to your directive, I recommend doing it like so.
.directive('myDirective', ['$parse', function ($parse) {
return {
restrict: 'A',
scope: {
fileModel: '=fileModel'
}
link: function(scope, element, attrs) {
var model = scope.fileModel;
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
Note I changed your directive name since you already had an attribute with that name.
<input type="file" my-directive file-model="myFile"/>
I'm not sure what you are trying to do after you have the attribute value, but if you console.log(scope.fileModel) you can see what built in options are available. This is an example of isolated scope within directives.
Update with controller access
To access within your controller, you could emit the value from your directive:
scope.$emit('myFile', scope.fileModel);
and then listen for the event in your controller:
$scope.$on('myFile', function (event, myFile) {
$scope.myFile = myFile;
};
Update with working fiddle
http://jsfiddle.net/jonesmac82/376SS/26/

Sibling directives sharing scope

I'm running into an issue where, through a directive, I am trying to set a property on the scope of a controller. The issue is that, for some reason, the scope on the directive seems to be isolating itself, but only in this instance. It works fine in other places of the application. So when I attempt to use $scope.files in my controller, it's coming back as undefined.
Controller:
app.controller('newProjectModalController', function($scope, $modalInstance, $http, $location, account, $http){
$scope.account = account.data;
$scope.project = {
name: '',
client: '',
users: [],
image: '/assets/images/add-project-photo.jpg'
};
$scope.cancel = function(){
$modalInstance.dismiss('cancel');
};
$scope.updateImage = function(item){
var filereader = new FileReader();
filereader.readAsDataURL($scope.files.item(0));
filereader.onload = function(event){
$scope.project.image = event.target.result;
}
}
$scope.submit = function(){
var formData = new FormData();
formData.append('file', $scope.files.item(0));
$http.post($scope.api_url + '/Project', $scope.project)
.success(function(data, status, headers, config){
$modalInstance.close();
$location.path('/project/' + data.id);
});
};
});
Directive:
app.directive('fileUpload', function($parse){
return {
restrict: 'A',
transclude: true,
template: '<input type="file" name="file" multiple style="height:100%;width:100%;display:inline-block;opacity:0.0;position:absolute;top:0;left:0" />',
link: function(scope, element, attrs){
var onFileChange = $parse(attrs.fileUpload);
var file = element.children('input');
file.on('change', function(){
scope.files = file[0].files;
onFileChange(scope);
})
}
}
});
Template:
<div class="row fieldset not" id="photo">
<div class="col-sm-8 col-sm-offset-2">
<h2 class="tight">Add project photo</h2>
<div class="add-project-photo" file-upload="updateImage()" style="background-image: url({{project.image}})"></div>
<span class="note">300px <i>by</i> 120px</span>
</div>
</div>
The template is only partial... the file itself is rather large
Edit: I should mention that the modal is being built using UI-Bootstrap
Since you are not transcluding anything, you can remove transclude: true from your fileUpload directive. You can also set scope: false to tell the directive to use parent (controller) scope.
app.directive('fileUpload', function($parse){
return {
restrict: 'A',
scope: false,
template: '<input type="file" name="file" multiple style="height:100%;width:100%;display:inline-block;opacity:0.0;position:absolute;top:0;left:0" />',
link: function(scope, element, attrs){
var onFileChange = $parse(attrs.fileUpload);
var file = element.children('input');
file.on('change', function(){
scope.files = file[0].files;
onFileChange(scope);
})
}
}
});

Resources