Multiple file upload directives and one submit button - angularjs

I have multiple categories of file upload and the HTML given to me has these different categories and each having a file upload control for multiple files. All these sections(and hence the file upload for each sections) are shown in a single HTML. I created a file upload directive and I am using it for each upload section. This works and can handle a button called "Upload" click event and this button is a part of directive.
Now, there is a single button called "Upload All" on click of which I have to update all the files belonging to different sections at one go. So I have to upload files from all the directives on click of this button. How can i have this functionality.

I'm not sure if there are better ways of doing this but you could try:
Passing a value from the controller to all directives and then on each directive attach a $watch on that value. Then clicking the Upload All button would change the value, triggering all the directives to run.
Controller:
app.controller = function('Ctrl', function($scope) {
$scope.uploadAllFlag = false;
$scope.uploadAll = function() {
$scope.uploadAllFlag = !$scope.uploadAllFlag;
}
});
Directive:
app.directive = function('directive', function() {
return {
scope: { flag: '=' }
link: function(scope) {
scope.$watch('flag', function(){
//do your upload stuff
}
}
});
Or alternativelly, inject the $rootScope on your controller and broadcast an event which you could catch on the directives:
app.controller = function('Ctrl', function($rootScope, $scope) {
$scope.uploadAll = function(){
$rootScope.$broadcast('uploadAll');
};
});
app.directive = function('directive', function(){
link: function(scope) {
scope.$on('uploadAll', function(){
//do something
}
}
});

I created a simple jsfiddle. The main idea is included there: http://jsfiddle.net/VpmV2/45/
[1]http://jsfiddle.net/VpmV2/45/

Related

How to make custom directive with dialog box template using Angular JS?

I need to show a confirmation box on different pages. So i have decided to create a custom directive for performing this task. I have a html template for confirmation box.There are two buttons and some text in this template. One button is for cancelling the dialog box and one for submitting it. So the functionality will be different for each page when we click on submit button. I have couple of questions regarding this issue.
How to create this kind of directive to show a dialog box on some condition?
How to pass text from my controller to this template?
How to override the "Submit" button functionality.
I had similar requirement where I wanted a custom modal pop-up to alert the user to continue with his actions such as delete, modify etc..,
So I wrote a custom directive. Below is the code.
(function(){
'use strict';
angular.module('mainApp').directive('confirm', ['$log','$uibModal', function($log,$uibModal){
var link = function($scope,elem,attr){
elem.bind('click',function(){
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'templates/shared/_confirm_modal.html',
controller: 'confirmDirectiveCtrl',
size: 'sm'
,backdrop: 'static' //disables modal closing by click on the backdrop.
,resolve: {
requiredVerbose: function(){
var requiredVerbose = {
modalTitle : attr.modalTitle
,message : attr.message
,confirmVerbose : attr.confirmVerbose
,cancelVerbose : attr.cancelVerbose
} ;
return requiredVerbose;
}
}
});
modalInstance.result.then(function(){
$scope.confirmFn();
}, function(){
if($scope.cancelFn){
$scope.cancelFn();
}
});
});
}
return{
restrict : 'A'
,scope : {
confirmFn : '&'
,cancelFn : '&'
}
,compile : function compile(elem,attr){
if(attr.confirmType && attr.confirmType=='delete')
{
attr.modalTitle = 'Warning';
attr.confirmVerbose = 'Delete';
attr.cancelVerbose = 'No';
attr.message = 'Are you sure, you want to delete?'
}
else{
if(!attr.modalTitle){attr.modalTitle = 'Warning'}
if(!attr.confirmVerbose){attr.confirmVerbose = 'Ok'}
if(!attr.cancelVerbose){attr.cancelVerbose = 'cancel'}
if(!attr.message){attr.message = 'Are you sure?'}
}
return{
post : link
}
}
}
}]);
angular.module('mainApp').controller('confirmDirectiveCtrl', ['$scope','$uibModalInstance','requiredVerbose',
function($scope,$uibModalInstance, requiredVerbose){
$scope.modalTitle= requiredVerbose.modalTitle;
$scope.message = requiredVerbose.message;
$scope.confirmVerbose = requiredVerbose.confirmVerbose;
$scope.cancelVerbose= requiredVerbose.cancelVerbose;
$scope.ok = function(){
$uibModalInstance.close($scope.timeline);
};
$scope.cancel = function(){
$uibModalInstance.dismiss();
};
}]);
}());
To answer your questions,
This is attribute type directive. And the element on which you add this directive tag is bound to onclick function which generates the required popup.
How to pass text?
You can pass the required text through attributes. I wanted this directive to work only for two kinds of alerts and hence had only two different sets of texts. If you want custom texts everytime, you can pass them to directive through attrs.
How to override the submit functionality?
You can pass your custom submit and cancel to this directive and bind them to the popup submit and cancel functions. The above code does the same.
Edit :
HTML template and explanation:
Below is an example describing on how you can use this directive.
<i class="fa fa-trash-o"
confirm
confirm-fn="deletePlaylist($index)"
confirm-type="delete">
</i>
The above template is an trash icon. The attributes are
directive name : confirm
confirm-fn : The function that should be called after user seleting ok/submit etc..,
confirm-type : This attribute defines what type of popup you want to show. In my case, I often use 'delete' type and hence wrote the required verbose related to it. By default, I already defined the verbose(title, message, ok-button, cancel-button).
If you want your custom messages add them in the attributes. Below is one such example.
<i class="fa fa-trash-o"
confirm
confirm-fn="doingGreatFn()"
cancel-fn="justFineFn()"
modal-title="My Modal"
message="How are you doing?"
confirm-verbose="Great"
cancel-verbose="Just Fine">
</i>
I hope, this helps
You can create a directive like below to handle both submit & cancel at any page for different functionalities in any controller. I've created an isolated scope directive but you can use change it according to your need by creating child scope scope : true; or bindToController:true (controller specific)
app.directive('confirm', ['$log', '$modal' ,'$parse','$timeout','factory', function($log, $modal,$parse,$timeout,factory) {
return {
restrict: 'E',
template:'<button type="button" class="btn form-btn" '+
'ng-click="openModal()" ng-disabled="disable" >'+
'{{buttonName}}</button>',
replace: true,
transclude: false,
scope: {
name :'=name', //can set button name ..basically u can send a text
disable :'=disable' //set as an attribute in HTML to disable button
},
link: function ($scope, element, attrs) {
$scope.buttonName = $scope.name;
$scope.openModal= function() {
$scope.modal = $modal.open({
templateUrl: 'customConfirmModal.html',
scope:$scope,
persist: true,
backdrop: 'static'
});
};
$scope.cancel = function(){
$scope.modal.dismiss();
};
$scope.submit= function(){
factory.customSubmitCall($scope);//call the factory method which will call different functions depending on the need..
};
}
Create a factory to contain different functions which can be called at any controller by injecting factory.
app.factory('factory', ['$http','$rootScope','$filter',function($http,$rootScope,$filter){
factory.customSubmitCall = function ($scope){
if($rootScope.page ==1){ //check on which page you are performing action
$scope.pageOneSubmit(); //page specific function in that controller..
}else{
$scope.submit();
}
};
return factory;
}]);
In your HTML
<confirm name="Confirm" disable="disable"> </confirm>

Open brand new $modal with Angular UI.Bootstrap every time

I'm using AngularJS with ui.bootstrap and have a modal directive, which works fine.
myApp.directive("modalButton", ['$uibModal', function ($uibModal) {
return {
restrict: "A",
scope: {
modalUrl: '#',
modalModel: '='
},
link: function (scope, element, attrs) {
element.bind('click', function () {
var modalInstance = $uibModal.open({
templateUrl: scope.modalUrl, // Here I want to use an url instead.
controller: 'editDeliveryCtrl as ctrl',
resolve: {
model: function () {
return scope.modalModel;
}
}
});
});
}
};
}]);
And used like:
<button type="button" class="btn btn-xs btn-default" modal-model="ctrl.currentDelivery" modal-button modal-url="#Url.Action("AddDelivery", "ProvisionContract", new {provisionContractId = Model.Id})">
<span class="fa fa-plus" area-hidden="true"></span>
</button>
When a user clicks on a button with the directive a modal is shown. However, I'm using MVC.Net and the modals I'm loading is often a form with #Html.AntiForgeryToken() or other code-behind logic. So what I really want is to reload the template every time (make a new request to the server).
I've read question below and that may be a good answer, but what I really want is to not use templates at all. I would like $uibModal to load it as brand new HTML instead.
How to remove angular $modal cache?
Can I make $uibModal reload the template as brand new HTML each time? More like $.load in jQuery?
All templates you load in the Angular app are stored in the $templateCache so they will not be downloaded the next time you are referring to them.
In your case, this is not the desired behavior as the HTML contents of the same URL may change over time.
Therefore, you have to remove the cached URL from the cache before opening the modal with
$templateCache.remove(scope.modalUrl);
Also, see How to remove angular $modal cache.
Another idea is to override the $templateCache's put method so it does not store some particular paths.
angular.module('yourApp').decorator('$templateCache', function($delegate) {
var originalPut = $delegate.put;
$delegate.put = function (templatePath, contents) {
// specifiy conditions which would tell what paths should not be cached
if (templatePath == '/this-should-not-be-cached.html' || templatePath.match(/some-regex/)) {
return;
}
else {
originalPut(templatePath, contents);
}
};
return $delegate;
});
You can't just disable $templateCache because many libraries use it to store the templates that are shipped with them (e.g. ui-bootstrap and similar).

angular set dirty form within directive controller

I have an angular driven form using $dirty to spot for changes to enable/disable submit button.
Part of the form uses a Directive for uploading a logo but the form is noticing this as a changed element so upon setting a logo that validates in size I need to manually trigger that the form has had a change so should be a case of formName.$setDirty(); however console is saying that $setDirty() is not defined and I think this is because I am within a directive.
Within my directives controller upon file selection I call the function below and it is here when the file is valid that I would want to call the setdirty method.
function isFileValid(file) {
vm.fileValid = true;
vm.errorMessage = "";
if (file.size > 16777216) {
vm.errorMessage = "The File is too big!";
vm.fileValid = false;
} else if (file.size == 0) {
vm.errorMessage = "The File is empty!";
vm.fileValid = false;
}
if (vm.fileValid) {
// make form dirty
$setDirty();
}
return vm.fileValid;
}
Here is the directive JS
(function () {
'use strict';
.module("tpBusinessProfile")
.directive("tpLogoUploader", tpLogoUploader);
function tpLogoUploader() {
return {
templateUrl: "tpLogoUploader.directive.html",
bindToController: true,
scope: {
changedMethod: "&"
},
controller: "tpLogoUploaderCtrl",
controllerAs: 'logoCtrl',
restrict: "E"
};
}
})();
Any help is appreciated.
You need to use directive require option and require controller of form directive:
{
require: '^form'
and then in link function bind method that you need to your scope (dirty solution):
link(scope, elem, attrs, formController){
scope.makeFormDirty = formController.$setDirty
}
and now you can use it in your controller via makeFormDirty

Controller method not found when button inside template is pressed

I'm creating a project using NodeJS, Express and AngularJS that will have a search form (added via custom directive) and a search results that must be loaded only after the search button is pressed.
The problem is that the method I have created inside the controller can't be found from the search form.
Here is a sample of my code:
app.js
(function() {
var app = angular.module('app', ['app-directives']);
app.controller('AppController', function() {
this.buttonClick = function() {
alert('Test');
};
});
})();
directives.js
(function(){
var app = angular.module('app-directives', []);
app.directive('searchForm', function() {
return {
retrict: 'E',
templateUrl: '/partials/search-form.html'
};
});
app.directive('searchResults', function() {
return {
retrict: 'E',
templateUrl: '/partials/search-results.html'
};
});
})();
search-form.html
<input type="text" id="query" />
<button onclick="buttonClick">Search</button>
page-content.html
<section id="mainContent">
<search-form></search-form>
<search-results></search-results>
</section>
UPDATE
The second question will be posted in another thread.
About your first question:
You are using onclick attribute instead angular's 'ng-click' in the button search. This could be the problem. And do not forget to also add the 'ng-app' and 'ng-controller' tags. If not, your method will never be visible.
I also would recommend you to use $scope service instead of 'this' for attaching models and functions you later will use in your views.
Regards

How do I use an Angular directive to show a dialog?

Using Angular, I'm trying to create a directive that will be placed on a button that will launch a search dialog. There are multiple instances of the search button, but obviously I only want a single instance of the dialog. The dialog should be built from a template URL and have it's own controller, but when the user selects an item, the directive will be used to set the value.
Any ideas on how to create the dialog with it's own controller from the directive?
Here's what I've go so far (basically just the directive)...
http://plnkr.co/edit/W9CHO7pfIo9d7KDe3wH6?p=preview
Here is the html from the above plkr...
Find
Here is the code from the above plkr...
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
var person = {};
person.name = 'World';
$scope.person = person;
$scope.setPerson = function(newPerson) {
person = newPerson;
$scope.person = person;
}
});
app.directive('myFind', function () {
var $dlg; // holds the reference to the dialog. Only 1 per app.
return {
restrict: 'A',
link: function (scope, el, attrs) {
if (!$dlg) {
//todo: create the dialog from a template.
$dlg = true;
}
el.bind('click', function () {
//todo: use the dialog box to search.
// This is just test data to show what I'm trying to accomplish.
alert('Find Person');
var foundPerson = {};
foundPerson.name = 'Brian';
scope.$apply(function () {
scope[attrs.myFind](foundPerson);
});
});
}
}
})
This is as far as I've gotten. I can't quite figure out how to create the dialog using a template inside the directive so it only occurs once and then assign it a controller. I think I can assign the controller inside the template, but first I need to figure out how to load the template and call our custom jQuery plugin to generate the dialog (we have our own look & feel for dialogs).
So I believe the question is, how do I load a template inside of a directive? However, if there is a different way of thinking about this problem, I would be interested in that as well.
I will show you how to do it using bootstrap-ui. (you can modify it easily, if it does not suit your needs).
Here is a skeleton of the template. You can normally bound to any properties and functions that are on directive's scope:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
... // e.g. <div class="button" ng-click=cancel()></div>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
...
</div>
</div>
</div>
Here is how to create/declare directive in your module:
.directive("searchDialog", function ($modal) {
return {
controller: SearchDialogCtrl,
scope : {
searchDialog: '=' // here you will set two-way data bind with a property from the parent scope
},
link: function (scope, element, attrs) {
element.on("click", function (event) { // when button is clicked we show the dialog
scope.modalInstance = $modal.open({
templateUrl: 'views/search.dialog.tpl.html',
scope: scope // this will pass the isoleted scope of search-dialog to the angular-ui modal
});
scope.$apply();
});
}
}
});
Then controller may look something like that:
function SearchDialogCtrl(dep1, dep2) {
$scope.cancel = function() {
$scope.modalInstance.close(); // the same instance that was created in element.on('click',...)
}
// you can call it from the template: search.dialog.tpl.html
$scope.someFunction = function () { ... }
// it can bind to it in the search.dialog.tpl.html
$scope.someProperty;
...
// this will be two-way bound with some property from the parent field (look below)
// if you want to perform some action on it just use $scope.$watch
$scope.searchDialog;
}
Then it your mark-up you can just use it like that:
<div class="buttonClass" search-dialog="myFieldFromScope">search</div>
I recommend this plugin:
https://github.com/trees4/ng-modal
Demo here:
https://trees4.github.io/ngModal/demo.html
Create a dialog declaratively; and it works with existing controllers. The content of the dialog can be styled however you like.

Resources