Accessing scope variable in directive - angularjs

At the moment I have this directive that checks if the username entered by the user already exists in the database -
app.directive('usernameAvailable', function ($timeout, $q, userService, authService) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, elm, attr, model) {
model.$asyncValidators.Username = function () {
var User = {
Username: model.$viewValue
}
var defer = $q.defer();
var promiseGet = userService.DoesUsernameExist(User);
promiseGet.then(function (result) {
scope.$emit('updateUsername', model.$viewValue);
if (result.data == true) {
model.$setValidity('Username', false);
} else {
model.$setValidity('Username', true);
}
},
function (errorResult) {
console.log('Unable to check if the username exists', errorResult);
});
return defer.promise;
};
}
}
});
This works, but my issue is that when you try to edit a user it will say that the username is already taken since it already exists in the database. I want to pass a variable ($scope.InitialUsername) so that i can put a condition in my directive that the username is allowed to equal this when editing a user.
This will also mean passing in a variable for editmode to allow this condition to be checked.

Take a look at this example
Any directive does have a scope variable where you can bind specific informations to
.directive('myDirective', function() {
return {
restrict: 'AE',
scope: {
initialUserName: '=name'
},
};
})
And in your html you can access this property like this:
<my-directive name="tom"></my-directive>
And on the template side you can query the name like
{{initialUserName.name}}

Related

How to pass ng-model value in radio button from ng-repeat into link function on directive

I want one of the Radio Button to be selected once the page is loaded, from another question on stackoverflow i found that Radio Button will be check if the value of the input attribute is equal to the value of model applied on the Radio Button. But i am unable to access the model($parent.selectedItem) on Radio Button in link function inside child directive. Api i used in example is a placeholder but in realtime i will have a property selected which will be true/false which I want to bind to the $parent.selectedItem
var mainApp = angular.module('mainApp', []);
mainApp.factory('myFactory', function ($http) {
var myFactory = {
myMethod: function () {
var promise = $http.get('https://jsonplaceholder.typicode.com/users').then(function (response) {
return response.data;
});
return promise;
}
};
return myFactory;
});
Controller:
mainApp.controller('myController', function ($scope, myFactory) {
myFactory.myMethod().then(function (result) {
$scope.data = result
})
});
Directives:
mainApp.directive('parent', function (myFactory) {
return {
restrict: 'E',
replace: true,
scope: true,
templateUrl: 'parent.html',
link: function (scope, element, attrs, ctrl) {
myFactory.myMethod().then(function (result) {
scope.Model = result
})
}
}
});
mainApp.directive('child', function () {
return {
restrict: 'E',
scope: {
Model: '=ngModel'
},
replace: true,
require: 'ngModel',
templateUrl: 'child.html',
link: function (scope, element, attrs, ctrl) {
// unable to access scope.selectedItem
console.log(scope.selectedItem)
}
}
});
HTML:
// mainpage.html
<body ng-app="mainApp"><parent></parent></body>
//parent.html
<div><child ng-model = "Model"></child></div>
//child.html
<div ng-repeat="item in Model"><input type="radio" name="itemSelected"
ng-value="item" ng-model="$parent.selectedItem"/>{{item.name}}</div>
when you require ngModel in the child directive, what you're basically requiring is its controller, this controller is then injected into your link function as the 4th parameter, in your case the ctrl argument.
so right now your ngModel might work, but it is not in your link function because you're expecting it to exist on the scope as selectedItem, but on your scope you have declared it as Model (not selectedItem). However, you also have access to the ngModel controller, so you could ask for its value there through its controller: https://docs.angularjs.org/api/ng/type/ngModel.NgModelController.
ex:
ctrl.$viewValue
// or
ctrl.$modelValue
//whichever serves your purpose

Injecting service variable to Directives

So I am having a bit of trouble. I have looked through all of the previous solutions from Injecting service to Directive, but I really have no idea what I'm doing wrong. I have an authServices shown below.
app.factory('authService', ['$http', function ($http) {
var authServiceFactory = {};
var _authentication = {
isAuth: false,
userName: ""
};
var _login = function (loginData) {
_authentication.isAuth = true;
_authentication.userName = loginData.userName;
}
appFactory.login = _login;
return appFactory;
}]);
I am injecting it via the method they had proposed.
app.directive('headerNotification', ['authService', function (authService) {
return {
templateUrl: 'app/scripts/directives/header/header-notification/header-notification.html',
restrict: 'E',
replace: true,
link: function (scope) {
scope.authService = authService;
}
}
}]);
My html is as
<li data-ng-hide="authentication.isAuth">
I really feel I am just doing this wrong. Any help would be greatly appreciated.
what is authentication.isAuth in your view.
I think you miss spelled your object.
<li data-ng-hide="authService.isAuth">
Your scope object is authService not authentication, right?
Update - Pass veraible to directive
I am assuming that you have your auth variable in your controller.
$scope.myAuthService = authservice;
Noe you can pass this variable to your directive as an attribute.
<header-notification my-auth="myAuthService"> </header-notification>
Here myAuthService is a scope variable.
Change your directive to accept this variable,
app.directive('headerNotification', function () {
return {
templateUrl: 'app/scripts/directives/header/header-notification/header-notification.html',
restrict: 'E',
scope : {
myAuth : '=' // here you specify that you need to convert your attribute variable 'my-auth' to your directive's scope variable 'myAuth'
},
replace: true,
link: function (scope, element, attr, controller) {
// here you will get your auth variable
scope.myAuth; // this contains your auth details
}
}
});

Angularjs avoid $emit and share a variable across directives

I have a situtation where in i want to avoid using $emit to share a scope variable and instead share some property using a underlying service, the problem is that the property value gets set on return of a promise response in directive 1 and by the time that property value is set in service through directive 1, directive 2 is already loaded and hence the property comes as undefined in directive 2.
Any ideas?
With the provided information, thought of writing this code fragment. Hope this will give you some insights to find the best answer.
angular.module('myApp').service('SomeService', function($http) {
this.readData = function(dataUrl) {
// read data;
return $http.get(dataUrl)
.then(function(res) {
return res.data;
}, function(res) {
return res;
}
}
return this;
});
angular.module('myApp').controller('MyController', function($scope, SomeService) {
$scope.readData = function(url) {
SomeService.readData(url)
.then(function(res) {
$scope.data = res;
}, function(res) {
// Display error
}
}
}
angular.module('myApp').directory('myDirectory1', function() {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
scope.data = scope.readData(url);
}
}
});
angular.module('myApp').directory('myDirectory2', function() {
return {
restrict: 'A',
scope: {
data : '#'
},
link: function(scope, elm, attrs) {
scope.$watch('data', function(newVal) {
// Do some stuffs
});
}
}
});
Perhaps extract the functionality that delivers the promise from your directive1 to the service, and in both directives use
.then(function(data){ ... } )

how to make angular directive accept string or model respectively

I am trying to create an angular directive that will be able to get BOTH model object and a string.
if the directive get a string it just output HTML, but if it's a model the the directive will watch the model for changes and will output data respectively.
I had tried to use the next code:
App.directive('iso2symbol', function () {
return {
restrict: 'E',
replace: true,
link: function ($scope, $element, $attrs) {
var curIsoObj = $scope.$eval($attrs.curIso);
//this is object it may change
if (typeof curIsoObj !== 'undefined') {
console.log('not a text');
$scope.$watch('curIso', function (value) {
console.log(value);
});
}
},
template: '<span>{{currencySymbol}}</span>'
}
}]);
This is not working, I had googled it for long time and I don't find the problem....
here is a link to JSfiddle where I had set a DEMO
Becareful with what you're watching.
according to your watch function you're watching $scope.curIso which really isn't a scope object.
you should be watching
$scope.$watch(function(){return $scope.$eval($attrs.curIso);}, function (value) {
$scope.txt = value;
});
Try this:
App.directive('iso2symbol', function () {
return {
restrict: 'E',
replace: true,
require: 'ngModel',
scope: {
curIso: '='
},
link: function ($scope, $element, $attrs) {
$scope.$observe('curIso', function(newValue, oldValue){
var curIsoObj = newValue;
// Do your test now to see if it's undefined,
// a string, or generic object.
// (the first time it will likely be undefined)
}
},
template: '<span>{{currencySymbol}}</span>'
}
}]);

Wait on $asyncValidators to submit form

I have a form that has a username field and other fields, this field has an async validator that checks whether that username is available when you try to submit the form (there are other fields on the form using async validation). I use this directive to validate and submit the form (only if it's valid):
<form validation-submit="submit()"></form>
app.directive('validationSubmit', ['$parse', function($parse) {
return {
restrict: 'A',
require: '?form',
compile: function($element, attr) {
var submitFn = $parse(attr.validationSubmit);
return function link(scope, element, attrs, FormController) {
var submit = function(event) {
scope.$apply(function() {
if (! FormController.$valid) {
return;
}
submitFn(scope, {$event: event}));
});
};
element.bind('submit', submit);
scope.$on('$destroy', function() {
return element.off('submit', submit);
});
}
}
};
}]);
The problem is this directive is not waiting for the pending async validations to finish. How can I change this directive to only submit after all async validations finished and passed?
Recently I've created a couple of directives that were checking whether email or phone number is available. I found solution that helps me. It's $asyncValidators .
app.directive('validationSubmit', ['$parse', function($parse) {
return {
restrict: 'A',
require: '?form',
link: function(scope, element, attrs, FormController) {
/**
*returns promise
**/
FormController.$asyncValidators.validEmail = function (modelValue) {
return $q(function (resolve, reject) {
//Here you should make query to your server and find out wether username valid or not
//For example it could be:
$http('http://your_api_server/Available').then(function (response) {
if (response.data.Available) {
resolve();
} else {
reject();
}
}, function (err) {
reject(err.data);
});
});
};
}
};
}]);
You should add attribute name to the form:
<form validation-submit="submit()" name="myForm"></form>
And now you have opportunity to check system properties of form in your controller:
$scope.submit = function(){
//Here will be your logic
if($scope.myForm.$valid ||
$scope.myForm.$invalid ||
$scope.myForm.$pending ||
$scope.myForm.$submitted){
//do whatever you want
}
}

Resources