How to have access to directive scope value from my main controller - angularjs

Web API file Upload with AngularJS - https://code.msdn.microsoft.com/AngularJS-with-Web-API-22f62a6e#content
This tutorial upload selected files to the server but there is no way to get the selected filename exception from a directive,
Please, i need a way to get the filename of the selected file from the directive scope, so that i can use it in another controller
This is the focus area
(function () {
'use strict';
angular
.module('app.photo')
.directive('egPhotoUploader', egPhotoUploader);
egPhotoUploader.$inject = ['appInfo','photoManager'];
function egPhotoUploader(appInfo, photoManager) {
var directive = {
link: link,
restrict: 'E',
templateUrl: 'app/photo/egPhotoUploader.html',
scope: true
};
return directive;
function link(scope, element, attrs) {
scope.hasFiles = false;
scope.photos = [];
scope.upload = photoManager.upload;
scope.appStatus = appInfo.status;
scope.photoManagerStatus = photoManager.status;
}
}
})();
The Directive Template
<form name="newPhotosForm" role="form" enctype="multipart/form-data" ng-disabled="appStatus.busy || photoManagerStatus.uploading">
<div class="form-group" ng-hide="hasFiles">
<label for="newPhotos">select and upload new photos</label>
<input type="file" id="newPhotos" class="uploadFile" accept="image/*" eg-files="photos" has-files="hasFiles" multiple>
</div>
<div class="form-group" ng-show="hasFiles && !photoManagerStatus.uploading">
<div>
File Name:{{photos[0].name}}
</div>
<input class="btn btn-primary" type="button" eg-upload="upload(photos)" value="upload">
<input class="btn btn-warning" type="reset" value="cancel" />
</div>
From my main controller, i want to have access to the directive scope
{{vm.photos[0].name}}
function link(scope, element, attrs) {
scope.hasFiles = false;
scope.photos = [];
scope.upload = photoManager.upload;
scope.appStatus = appInfo.status;
scope.photoManagerStatus = photoManager.status;
}
// This is scope that i need in the main controller, please how can i get it out.
scope.photos = [];

Related

How to include input fields from custom directives in angular forms?

I need to create a simple form with validations like this - https://jsfiddle.net/rohansh20/k7omkz7p/2/
<div ng-app="module1" ng-controller="ctrl1 as vm">
<form novalidate name="vm.form1" class="css-form">
<label>Name:
<input type="text" name="Name" ng-model="vm.user.name" required />
</label>
<br />
<label>E-mail:
<input type="email" name="Email" ng-model="vm.user.email" required />
</label>
<br />
<input type="submit" ng-click="vm.save(vm.form1, vm.user)" value="Save" />
</form>
<div>
{{vm.result}}
</div>
<pre>form = {{vm.form1 | json}}</pre>
</div>
angular.module('module1', []);
angular.module('module1').controller('ctrl1', function() {
this.save = function(form, user) {
if(form.$invalid) {
this.result = 'Please correct the data entered';
return;
}
this.result = 'User ' + user.name + ' with email ' + user.email + ' saved successfully';
};
});
But I need to dynamically generate the input fields. So I have made a directive that transforms into any type of input field - https://jsfiddle.net/rohansh20/hdxj0np6/3/
<div ng-app="module1" ng-controller="ctrl1 as vm">
<form novalidate name="vm.form1" class="css-form">
<custom-input name="Name" type="text" model="vm.user.name" required="true">
</custom-input>
<br />
<custom-input name="Email" type="email" model="vm.user.email" required="true">
</custom-input>
<br />
<input type="submit" ng-click="vm.save(vm.form1, vm.user)" value="Save" />
</form>
<div>
{{vm.result}}
</div>
<pre>form = {{vm.form1 | json}}</pre>
</div>
var app = angular.module('module1', []);
app.controller('ctrl1', function() {
this.save = function(form, user) {
if(form.$invalid) {
this.result = 'Please correct the data entered';
return;
}
this.result = 'User ' + user.name + ' with email ' + user.email + ' saved successfully';
};
});
app.directive('customInput', function($compile) {
return {
restrict: 'E',
link: function(scope, element, attributes) {
var labelElement = angular.element('<label/>'),
name = attributes.name,
type = attributes.type,
ngModelString = attributes.model,
required = attributes.required,
inputElement = angular.element('<input/>');
inputElement.attr('ng-model', ngModelString);
inputElement.attr('name', name);
inputElement.attr('type', type);
if (required) {
inputElement.attr('required', 'required');
}
labelElement.append(name + ': ');
labelElement.append(inputElement);
$compile(labelElement)(scope);
element.replaceWith(labelElement);
}
}
});
The fiddles are simplified versions of what I'm trying to make.
The problem is that these fields, even though compiled and rendered perfectly(which can be seen by inspecting the HTML), are not getting included as part of the parent form control. This can be seen in the displayed form control object in both the fiddles. Because of this, the form validity cannot be determined and both forms behave differently on submitting invalid input.
I need the form control in the second fiddle to have correct values for its properties and to have the child controls and their properties like in the first fiddle. Is this even possible using a custom directive? What do I need to change to make this work?
Note - The directive would involve complex operations to dynamically create HTML, so it has to be done in the link function of a directive. A directive template with multiple ngIfs would just not work.
In order not to lose bindings from your parent form, include it in your custom form directive
In your directive
(function () {
'use strict';
angular
.module('myApp')
.directive('customInput', customInput)
customInput.$inject = ['$compile'];
/* #ngInject */
function customInput ($compile) {
var directive = {
templateUrl: 'app/custom-input.html',
restrict: 'E',
transclude: true,
require: "?^form",
compile: compile,
controller:CustomInputController,
controllerAs:'vm',
scope:{
inputType:'=',
inputValue:'='
}
};
return directive;
function CustomInputController($scope){
var vm = this;
}
function compile(element, attributes){
return {
pre: preLink,
post: postLink
}
}
function preLink (scope,element,attrs, form, transclude){
}
function postLink (scope,element,attrs, form, transclude){
scope.currentForm = form;
$compile(element.contents())(scope);
}
}
})();
In your directive html template
<input type="inputType" ng-model="inputValue">
When you call your directive
<br-input type="text"
input-value="vm.user.email"
inputname="name"
input-required ="true">
</br-input>

File name doesn't get cleared after form.$setPristine for a custom angular directive for file upload

I am using angular 1.5.8. After following some resources, I got the file upload working. I had to create a custom directive for that.
Directive
//file-upload-model-directive.js
'use strict';
export default function (app) {
app.directive('fileUploadModel', fileUploadModelDirective);
function fileUploadModelDirective () {
'ngInject';
return {
restrict: 'A',
link: linkFn,
require: 'ngModel'
};
function linkFn (scope, element, attrs, ngModel) {
element.bind('change', function(event){
var files = event.target.files;
var file = files[0];
ngModel.$setViewValue(file);
scope.$apply();
});
}
}
}
I am also using angular's form. And I have a "reset" button on this form. I want to clear all the form fields when clicked. And it happens with all form fields except file.
View
<form ng-submit="dataCtrl.upload(form)" name="form">
<div class="form-group" ng-class="{'has-error' : form.file.$invalid && !form.file.$pristine}">
<label>Select file</label>
<input type="file" name="file" ng-model="dataCtrl.newUpload.csvFile" file-upload-model required/>
</div>
<div class="form-group" ng-class="{'has-error' : form.comment.$invalid && !form.comment.$pristine}">
<label>Comment</label>
<textarea class="form-control" name="comment"
ng-model="dataCtrl.newUpload.comment" required></textarea>
</div>
<div class="form-group pull-right">
<button type="submit" class="btn btn-success" ng-disabled="form.$invalid">Upload</button>
<button class="btn btn-default" ng-click="dataCtrl.reset(form)" ng-disabled="!form.$dirty">Reset</button>
</div>
</form>
And the Controller
'use strict';
function DataController($log, catalogCnst, requestSV, $http) {
'ngInject';
this.reset = function(form) {
this.newUpload = {};
// form.file.$setViewValue(null); // this didn't work either
form.$setPristine()
};
this.upload = function(form) {
// some code
};
}
When "reset" is clicked, I see that
form.file.$pristine is false
form.file.$invalid is false
But I still see filename near the file upload element.
I also tried adding watch and handling event on the element in the directive
scope.$watch(attrs.fileUploadModel, function(value) {
console.log('attrs.file');
});
element.on('$pristine', function() {
console.log('destroy');
});
But they didn't get invoked.
How do I do this? Please guide me.
When you clear newUpload, file input does not get cleared. You need to do this separately.
See JSFiddle:
Basically, I added to the directive scope:
scope: {
model: '=ngModel'
},
... and watch:
scope.$watch('model', function(file) {
if (!file) {
element.val('');
}
});
Instead of using button tags, why not use input tags. This, in theory, might work.
<input type="submit" class="btn btn-success" ng-disabled="form.$invalid" value="Upload">
<input type="reset" class="btn btn-default" ng-click="dataCtrl.reset(form)" ng-disabled="!form.$dirty" value="Reset">

Scope not updating in link directive on ng-submit

I can't get the scope object to update on submit. The submit function is called, but the console shows the object does not update. I have tried most similar problem-solutions I have found.
The directive (inside index.html):
<div d-header-login class="header-login"></div>
The directive HTML:
<div class="container_header-login">
<div data-ng-if="!login.opLogin">
<form ng-submit="submit()">
<input type="text" ng-model="username" placeholder="username/email" />
<input type="text" ng-model="scope.password" placeholder="password" />
<br />
<input type="submit" class="btn btn-primary" value="Login"/>
</form>
Register
Forgot password
</div>
<div data-ng-if="login.opLogin">
<!--{{login.opLogin}}-->
Logged in as
</div>
</div>
The directive js
var mHeaderLogin = angular.module('app.directives.mHeaderLogin', ['mMain'])// mMain-dependent due to factory call
.directive('dHeaderLogin', fHeaderLogin);
function fHeaderLogin() {
return {
restrict: 'A',
//replace: true,
scope: {
username: '='
}, //isolate scope
templateUrl: 'app/directives/header-Login/header-Login.html',
controller: function ($scope, simpleFactory) {
$scope.users = simpleFactory.getUsers();
$scope.username = "test";
},
link: function (scope, element, attrs) {
scope.login = { opLogin: false };
scope.submit = function () {
console.log(scope.username);
console.log("fSubmit");
}
}
}
}
The log will show "test"; not what I wrote in the input box

Get access to form controller validation errors in a custom directive

I have a directive that wraps a form element with some inputs. One of the options is passing in a formName. Usually, with a form with the example name of myForm, to show an error you would do something like myForm.firstName.$error.required.
But, how do I get access to the errors when the form name is dynamically being passed in to the directive?
example usage
<my-custom-form formName='myForm' formSubmit='parentCtrl.foo()'></my-custom-form>
directive
angular.module('example')
.directive('myCustomForm', [
function() {
return {
restrict: 'E',
replace: true,
templateUrl: 'myCustomForm.directive.html',
scope: {
fornName: '#',
formSubmit: '&'
},
require: ['myCustomForm', 'form'],
link: function(scope, element, attrs, ctrls) {
var directiveCtrl = ctrls[0];
var formCtrl = ctrls[1];
scope.data = {};
scope.hasError = function(field) {
// how do i show the errors here?
};
scope.onSubmit = function() {
scope.formSubmit();
};
}
};
}]);
template
<form name="{{ formName }}" ng-submit="onSubmit()" novalidate>
<div class="form-group" ng-class="{'is-invalid': hasError('fullName') }">
<input type="text" name="fullName" ng-model="data.full_name" required />
<div ng-show="hasError('fullName')">
<p>How do I show this error?</p>
</div>
</div>
<div class="form-group" ng-class="{'is-invalid': hasError('email') }">
<input type="text" name="email" ng-model="data.email" ng-minlength="4" required />
<div ng-show="hasError('email')">
<p>How do I show this error?</p>
</div>
</div>
<button type="submit">Submit</button>
</form>
I think the only problem with your code is that the directive requires itself, I don't think that will work. Just removing the myCustomForm from the require works fine.
To check if the field has errors, you just need to check if the $error object in the form controller is empty.
require: ['form'],
link: function(scope, element, attrs, ctrls) {
var formCtrl = ctrls[0];
scope.data = {};
scope.hasError = function(field) {
// Field has errors if $error is not an empty object
return !angular.equals({}, formCtrl[field].$error);
};
Plunker

ng-model is not bind with controller's $scope when I am creating a form dynamically using directive.

view code:- mydir is my custom directive
<div ng-model="vdmodel" mydir="dataValue">
</div>
my directive :-
app.directive('mydir',['$translate',function($translate){
return {
restrict: 'A',
transclude: true,
scope: {dir:'=mydir'},
compile: function(element, attrs) {
return function(scope, element, attrs, controller){
var setTemplate = '';
var setOpt = '';
if(scope.dir.itemtype== 'NUMBER'){
setTemplate = '<input type="number" class="form-control form-font ng-animate ng-dirty"';
setTemplate +='" ng-model="dir[somevalue]" value="'+scope.sizing.somevalue+'" >';
element.html(setTemplate);
}
}
}
}
});
There are many more form element in directive, but when I am trying to submit and collect value in my controller function I get nothing.
What I am doing wrong and what is the best way to collect form values ?
there are quiet a few changes that you will need to do
1.as you are using isolate scope, pass ngModel as well to the directive
scope: {dir:'=mydir', ngModel: '='},
2.as per the best practise ngModel must always have a dot
ng-model="params.vdmodel"
3.make sure to initialize the params object in controller
$scope.params = {}
Usually, a directive would share the same scope as the parent controller but since you are defining a scope in your directive, it sets up it's own isolate scope. Now since the controller and directive have their seperate scope, you need a way to share the data between them which is now done by using data: "=" in scope.
The app code
var myApp = angular.module('myApp', []);
myApp.controller('myController', function ($scope, $http) {
$scope.vdmodel = {};
})
.directive("mydir", function () {
return {
restrict: "A",
scope:{
data:"=model",
dir:'=mydir'
},
templateUrl: 'test/form.html'
};
});
The form.html
<form>
Name : <input type="text" ng-model="data.modelName" /><br><br>
Age : <input type="number" ng-model="data.modelAge" /><br><br>
Place : <input type="text" ng-model="data.modelPlace" /><br><br>
Gender:
<input type="radio" ng-model="data.modelGender" value="male"/>Male<br>
<input type="radio" ng-model="data.modelGender" value="female"/>Female<br><br><br>
</form>
The page.html
<div ng-app="myApp" >
<div ng-controller="myController" >
<div model="vdmodel" mydir="dataValue"></div>
<h3>Display:</h3>
<div>
<div>Name : {{myData.modelName}} </div><br>
<div>Age : {{myData.modelAge}}</div><br>
<div>Place : {{myData.modelPlace}}</div><br>
<div>Gender : {{myData.modelGender}}</div><br>
</div>
</div>
</div>
You have to use $compile service to compile a template and link with the current scope before put it into the element.
.directive('mydir', function($compile) {
return {
restrict: 'A',
transclude: true,
scope: {
dir: '=mydir'
},
link: function(scope, element, attrs, controller) {
var setTemplate = '';
var setOpt = '';
if (scope.dir.itemtype == 'NUMBER') {
setTemplate = '<input type="number" class="form-control form-font ng-animate ng-dirty"';
setTemplate += '" ng-model="dir.somevalue" value="' + scope.dir.somevalue + '" >';
element.html($compile(setTemplate)(scope));
}
}
}
});
See the plunker below for the full working example.
Plunker: http://plnkr.co/edit/7i9bYmd8blPNHch5jze4?p=preview

Resources