I have a question for all you AngularJs gurus out there. I am attempting to create modal wherein a user can submit files for upload. I have most of the problem sorted, however, I seem to be running into issues concerning scope. The technologies I am using are Angularjs, Angular UI Bootstrap and a custom file model directive.
I have a custom fileModel directive which on file selection updates the scope:
app.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]);
});
});
}
}
}]);
I use UI bootstrap to create the modal as per the documentation (http://angular-ui.github.io/bootstrap/#/modal). Please note the file-model="file" directive in the input field, this is what I am trying to access.
<div ng-controller="ModalDemoCtrl">
// Button to open model
<button class="btn btn-default" data-ng-click="open()">Upload File</button>
// Simple Form in model
<script type="text/ng-template" id="myModalContent.html">
<form name="form.myForm" class="form-horizontal" data-ng-submit="addFile()" role="form" novalidate>
<div class="modal-header">
<h3 class="modal-title">Upload File</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label for="file" class="col-sm-2 control-label">Input File</label>
<div class="col-sm-10">
<input type="file" name="file" file-model="file">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
</script>
</div>
Lastly I have the controller(s), again as per the Bootstrap UI documentation. Please note where I try to access $scope.file.
app.controller('ModalDemoCtrl', ['$scope', '$http', '$modal', function ($scope, $http, $modal) {
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
$http: function () {
return $http;
}
}
});
modalInstance.result.then(function () {
// do something
}, function () {
// do something
});
};
}]);
var ModalInstanceCtrl = function ($scope, $modalInstance, $http) {
$scope.addFile = function() {
var formData = new FormData();
formData.append('file', $scope.file);
$http.post(
'/valid/ajax/url', formData, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).success(function(data, status, headers, config) {
// do something on success
});
);
};
I know there are some issues with regards to scope within modals when using angular UI bootstrap... unfortunately I am not experienced enough to find a solution. Currently, I am unable to access the file a user selects to be sent via ajax. Please help. Thanks.
I saw that the variable "$scope.file" is not being set, it stays undefined. Therefore you can't post the file. An easy way to share data between controllers (you have two, the modal controller and you parent controller) is using a service or factory. Check documentation if you are not sure. Then you can change the save the file via service/factory and use it in both controllers.
First of all the service which just contains a getter and a setter:
app.service('fileService', function () {
var file;
var fileService = {};
fileService.getFile = function () {
return file;
};
fileService.setFile = function (newFile) {
file = newFile;
};
return fileService;
});
You can then use the service via dependency injection on the directive and the controller:
app.directive('myFileUpload', function (fileService) {
return function (scope, element) {
element.bind('change', function () {
fileService.setFile(element[0].files[0]);
});
}
});
app.controller('ModalInstanceCtrl', function ($scope, $modalInstance, $http, fileService) {
$scope.addFile = function () {
var file = fileService.getFile();
console.log(file);
$http.post(
'http://posttestserver.com/post.php', file, {
transformRequest: angular.identity,
headers: {'Content-Type': file.type}
}).success(function (data, status, headers, config) {
console.log(data);
});
};
});
I created a fiddle so you can test it there. The file is outputted on the console. For testing the post I used http://posttestserver.com/post.php
http://jsfiddle.net/dz5FK/
Related
I have one html file.
At that file, I have a button that should open a modal when I click it.
<button ng-click="open(value.Reservation.id)" class="btn btn-default btn-xs" ></button>
and below that button, in the same html file, I have the script.
<script type="text/ng-template" id="myModalContent.html">test</script>
and in the pendingController.js file,
myApp.controller('PendingCtrl', function ($rootScope, $scope, $location, $http, $modal, $log) {
$scope.open = function (id) {
//GET RESERVATION
$http({method: 'GET', url: 'admin/getUpdateReservation/'+ id +'.json'}).
success(function(data, status, headers, config) {
$scope.post = data.data;
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'UpdateReservationCtrl',
resolve: {
data: function () {
$scope.post.status = $scope.status;
$scope.post.locations = $scope.locations;
return $scope.post;
}
}
});
modalInstance.result.then(function (selectedItem) {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
}).
error(function(data, status, headers, config) {
alert('error');
});
};
})
And I have updateReservationController.js file.
hansApp.controller('UpdateReservationCtrl', function ($rootScope, $scope, $route, $http, $modalInstance, data) {
console.log('6');
})
the console.log('6'); works. But I can't see the modal page...
How can I make my modal works?
So First of all, dont use resolve if you dont wating for a promise...
What docs say to the resolve Key:
Object containing dependencies that will be injected into the
controller's constructor when all the dependencies have resolved. The
controller won't load if the promise is rejected.
It looks like you are using the Modal of AngularStarp (correct me if im wrong).
As they mention in they docs you have to do something like this:
modal = $modal({
controller: function($scope) {},
show: false,
});
and in your success-callback:
modal.$promise.then(modal.show);
I am trying to make a very minimalistic form in AngularJS (version 1).
I am trying to use ng-model and the $scope to update an object I've named fluff. Once a user clicks submit it should be used in this $http call.
I'm highly confused I thought ng-model would bind this to the object in the scope. But it always returns a blank cause the $scope.fluff is not updating.
Yet if I inject {{ fluff.link }} this will update based on the textbox.
Here is my form in the view:
<form name="fluffForm" ng-submit="submitform()">
<span>Link: <input type="text" name="link" ng-model="form.link"></span>
<span>Description: <input type="text" name="description" ng-model="form.desc"></span>
<button type="submit">submit</button>
</form>
</div>
Here is my controller:
(function(){
'use strict';
angular.module('fluff').controller('FormController', FormController);
FormController.$inject = ['$scope', '$rootScope', '$routeParams', '$window', '$http'];
function FormController( $scope, $rootScope, $routeParams, $window, $http){
var form = this;
$scope.fluff = {}; // form data in json object(?) to be posted to mongo database
$scope.submitform = function(){
$scope.fluff.link = form.link;
$scope.fluff.description = form.desc;
console.log('form-data', $scope.fluff);
$http({
method: 'POST',
url: 'http://fluff.link/share',
data: $scope.fluff,
headers: {'Content-type': 'application/x-www-form-urlenconded'}
}).success(function(data){
console.log('Call to API was successful');
if(data.errors){
console.log('Data Errors');
console.log('error:', $data.errors.name);
//show errors - part of the response in the REST API have to make this portion up myself
$scope.errorName = $data.errors.name;
} else {
console.log('returned share id', data);
var fluff = 'fluff/link/'+ data;
$window.location.href = fluff;
}
});
}
}
})();
Here is my route:
(function(){
'use strict';
angular.module('fluff').config(Config);
Config.$inject = ['$routeProvider'];
function Config($routeProvider){
$routeProvider.when('/', {
templateUrl: 'views/index.client.view.html',
controller: 'FormController',
controllerAs: 'form'
});
}
})();
Added some logs from the developer console in chrome:
in submitform FormController {link: "test", desc: "test"}
fluff.form.controller.js:24 form-data Object {link: undefined}
Got it to work! Will update with my answer when it allows!
So my problem here is that I wasn't using the form controller like I should have.
Here I have the template being loaded with the controller as form.
$routeProvider.when('/', {
templateUrl: 'views/index.client.view.html',
controller: 'FormController',
controllerAs: 'form'
});
In the template I have to use form:
<span>Link: <input type="text" name="link" ng-model="form.link"></span>
<span>Description: <input type="text" name="description" ng-model="form.desc"></span>
then in the controller I create a this object:
var vm = this;
vm is now linked to form.
So now I can do this:
var fluff = {};
fluff.link = form.link;
fluff.description = form.desc;
Now fluff has all the data it needs when my user clicks submit.
I am using Angular-ui to pop up a modal with a form in it. My code is:
app.controller('NewCaseModalCtrl', ['$http', '$scope','$modal', function ($http, $scope, $modal, $log) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function (size) {
var modalInstance = $modal.open({
templateUrl: 'modal-new-case.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
});
};
}]);
And then I have another controller that is inside the modal-new-case.html template, and I want it to run an httpd request and then close that modal, here is that code:
app.controller('CreateCaseFormCtrl', ['$http','$scope', function($http,$scope) {
$scope.formData = {};
$scope.processForm = function() {
$http.post('http://api.com/proj', $scope.formData).
success(function(data, status, headers, config) {
console.log("success " + data.id);
}).
error(function(data, status, headers, config) {
console.log("Error " + status + data);
});
};
}]);
Okay so inside my modal-new-case.html template, which is loaded when I do:
ng-controller="NewCaseModalCtrl"
I have this HTML:
<div ng-controller="CreateCaseFormCtrl">
<form ng-submit="processForm()">
<button class="btn btn-primary" ng-click="processForm()" >OK</button>
<button class="btn" ng-click="cancel()">Cancel</button>
</form>
</div>
So if you see, what I really want to do is to run that processForm() function, and when it returns with a success, I want to THEN call the function that will close the modal, which I believe "cancel()" would be fine.
But I don't know how to refer to it from the CreateCaseFormCtrl controller.
I appreciate any thoughts and help, and I would like to add that I am very unsophisticated when it comes to Angular, so if this is complicated, please remember that maybe I am not 100% clear on what every single thing in Angular is such as the factories and such. I guess I'm saying I'm very happy with a dirty solution that's fairly simple, since this isn't going to be long-term production programming code.
Step 1: remove the
ng-controller="CreateCaseFormCtrl"
from
<div ng-controller="CreateCaseFormCtrl">
<form ng-submit="processForm()">
<button class="btn btn-primary" ng-click="processForm()" >OK</button>
<button class="btn" ng-click="cancel()">Cancel</button>
</form>
</div>
Step 2: Change
controller: 'ModalInstanceCtrl', => controller: 'CreateCaseFormCtrl'
in
var modalInstance = $modal.open({
templateUrl: 'modal-new-case.html',
controller: 'CreateCaseFormCtrl', //Add here
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
Step 3: In CreateCaseFormCtrl add a service called $modalInstance
app.controller('CreateCaseFormCtrl', ['$http','$scope', '$modalInstance', function($http,$scope, $modalInstance) {
Step 4: Add the close and ok functions
$scope.cancel = function () {
$modalInstance.dismiss();
};
and $modalInstance.close(); in
$http.post('http://api.com/proj', $scope.formData).
success(function(data, status, headers, config) {
console.log("success " + data.id);
$modalInstance.close(); //add here
}).
error(function(data, status, headers, config) {
console.log("Error " + status + data);
});
use $modalInstance.dismiss API
in NewCaseModalCtrl:
controller('NewCaseModalCtrl', ['$scope', '$modalInstance', function ($scope, $modalInstance,
...
$modalInstance.close(data);
You an do it globally like this or from other controllers too:
//hide any open $mdDialog modals
angular.element('.modal-dialog').hide();
//hide any open bootstrap modals
angular.element('.inmodal').hide();
//hide any sweet alert modals
angular.element('.sweet-alert').hide();
I'm creating a webapp and I want to implement an option to add friends. I've created the add friend page as a modal with a text input field. I want to test this by displaying the input on my view page. How do I display this data onto my view page?
Here's what I currently have
index.html
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<form name = "addFriendForm">
<input ng-model = "user.name"class="form-control" type = "text" placeholder="Username" title=" Username" />
{{ user.name }}
</form>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
<button class="btn btn-default" ng-click="open()">Add Friend</button>
<div> Username: {{user.name}}</div>
</div>
my JavaScript file:
angular.module('ui.bootstrap.demo', ['ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.user = {name: ""}
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function () {
$scope.user.name = user.name;}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close($scope.user.name);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
plunkr: http://plnkr.co/edit/JIoiNx47KXsY8aqbTUDS?p=preview
Resolve - plunkr
You could make use of modalInstance's resolve property; this acts as the link between the modal instance and the parent controller.
You inject the object in to the ModalInstanceController, and assign it to the scope of your modal instance.
UI Bootstraps resolve works exactly the same as ngRouter's; as such if for whatever reason resolve cannot resolve an object, the modal will not open.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
user: function() {
return $scope.user;
}
}
});
Scope - plunkr
An alternative, and arguably simpler method would be to pass in the parents scope in to the modal. Note that currently this doesn't work when using controllerAs syntax on the parent controller.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
scope: $scope
});
I would suggest using the promise from the result to set the value to your controller's variables instead. The "resolve" from my understanding is for passing controller's variable to the dialog for using inside the dialog, not to be changed directly. In your code example, passing result you set in $modalInstance.close() to modalInstance.result.then() will do the trick. In other words, changing line #18 and #19 to the following,
modalInstance.result.then(function (data) {
$scope.user.name = data;
To improve the code a bit, you can actually pass the entire user object to allow adding additional attributes such as email, address, etc. to your user object easily. I had create an example at http://plnkr.co/edit/oztYRNrCRlF1Pw289i1M?p=preview.
I have a controller (code below) that links to a d3-cloud directive and works perfectly. Data is added in the controller and passed to the directive.
myApp.controller('DownloadsCloudCtrl', ['$scope',
'$rootScope',
'requestService',
'$cookieStore',
function($scope, $rootScope, requestService, $cookieStore){
$scope.d3Data = [
{
'kword': 'a',
'count': 141658,
},{
'kword': 'b',
'count': 105465,
}
];
}]);
Now I'm trying to pull data from a JSON request service by switching my controller to the following code. When I do a console.log in the controller underneath the $scope.d3Data = data line, everything appears to be working properly (the proper data is returned).
However, something breaks when trying to link the controller to the directive, for some reason the directive is getting an undefined/null data set.
I'm wondering if the issue is in the order with which the code executes. Perhaps the controller tries to pass data to the directive before the JSON service has finished, thus resulting in no graph being drawn. Could this be happening, and if so, how can I go about fixing it?
myApp.controller('DownloadsCloudCtrl', ['$scope',
'$rootScope',
'requestService',
'$cookieStore',
function($scope, $rootScope, requestService, $cookieStore){
$rootScope.$on('updateDashboard', function(event, month, year) {
updateDashboard(month, year);
});
var updateDashboard = function(month, year) {
requestService.getP2PKeywordData(month, year).then(function(data) {
$scope.d3Data = data;
});
};
updateDashboard($cookieStore.get('month'), $cookieStore.get('year'));
}]);
EDIT: Directive code:
myApp.directive('d3Cloud', ['$window',
'd3Service',
'd3Cloud',
function($window,
d3Service,
d3Cloud) {
return {
// Restrict usage to element/attribute
restrict: 'EA',
// Manage scope properties
scope: {
// Bi-directional data binding
data: '=',
// Bind to DOM attribute
label: '#'
},
// Link to DOM
link: function(scope, element, attrs) {
// Load d3 service
d3Service.d3().then(function(d3) {
// Re-render on window resize
window.onresize = function() {
scope.$apply();
};
// Call render function on window resize
scope.$watch(function() {
return angular.element($window)[0].innerWidth;
}, function() {
scope.render(scope.data);
});
// Render d3 chart
scope.render = function(data) {
// d3 specific appends... not important
HTML Code: (simple enough)
<div class="col-md-6">
<div class="module">
<div class="inner-module" ng-controller="DownloadsCloudCtrl">
<div class="module-graph">
<d3-cloud data="d3Data"></d3-cloud>
</div>
</div>
</div>
</div>
try adding a $scope.$apply() after $scope.d3Data = data;
$scope.d3Data = data;
$scope.$apply();
if this doesn't work, you can always pass a function down into the directive and set it to update the data and then manually call it from the controller:
so controller logic:
$scope.updateDirective = function () {}; // this will be overridden in directive
directive logic:
scope: {
data: '=',
update: '&updateDirective'
label: '#'
}
scope.updateDirective = function () {
scope.render(scope.data); // call to update function
};
markup:
<div class="col-md-6">
<div class="module">
<div class="inner-module" ng-controller="DownloadsCloudCtrl">
<div class="module-graph">
<d3-cloud data="d3Data" update-directive="updateDirective"></d3-cloud>
</div>
</div>
</div>
</div>