Angular directive scope. How to isolate single variable - javascript

I'm trying to make input wrapper with angular directives and it should have different labels. Now I even can't access model outside the input (even with scope: false).
HTML:
<input-block data-label="my label">
<input class="input-field" type="text" name="test" ng-model="test"/>
</input-block>
Test: {{test}} <!--not working-->
Angular:
profileApp.directive('inputBlock', function() {
return {
replace: true,
restrict: 'E',
transclude: true,
template: '' +
'<div class="input-block">' +
'<span class="input-text">{{label}}</span>' +
'<ng-transclude></ng-transclude>' +
'</div>',
link: function(scope, element, attrs) {
scope.label = attrs.label;
}
};
});
The only idea for now is to find a way to isolate single variable or something similar

as was already said here, just use object for your ng-model:
<body ng-init="model = {}">
<input-block data-label="my label2">
<input class="input-field" type="text" name="test" ng-model="model.test"/>
</input-block>
Test: {{model.test}}
</body>
plunker: http://plnkr.co/edit/XxeMlVv6I6qOwjPoCUtQ?p=preview

Related

Finding all ng-model elements of a particular child elements

I have written on directive I just want all the ng-model elements inside the element where the directive is placed
This is my html element with loggerhelp directive
<input type="text" loggerhelp />
This is my directive
angular
.module('app').directive('loggerhelp', loggerhelp);
loggerhelp($mdDialog) {
var Popupdirective = {
restrict: 'A',
scope: false,
link: popupController
}
return Popupdirective
function popupController(scope, element, attr) {
// Here i want all the child ng-model elements.
console.log(element)
alert();
}
}
Have you tried searching by attribute like element.find("[ng-model]")? element is the jqLite-wrapped element that this directive matches so find and the selectors should work for you.
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
}])
.directive('loggerhelp', function loggerhelp() {
var loggerhelpDirective = {
restrict: 'A',
scope: false,
link: loggerhelpController
}
return loggerhelpDirective
function loggerhelpController(scope, element, attr) {
var innerElementsWithNgModel = element.find("[ng-model]");
console.log(innerElementsWithNgModel.length);
console.log(angular.element(innerElementsWithNgModel[2]).attr('ng-model'));
//so now you have an array with all the elements that have ng-model attr
//you can attach event handler function for blur
innerElementsWithNgModel.on('blur', function(){
var ngModelAttr = angular.element(this).attr('ng-model');
//we can evaluate this now using $eval
if (ngModelAttr){
console.log(scope.$eval(ngModelAttr));
}
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<div loggerhelp>
<input type="text" ng-model="$ctrl.model1" />
<input type="text" ng-model="$ctrl.model2" />
<input type="text" ng-model="$ctrl.model3" />
<input type="text" ng-model="$ctrl.model4" />
</div>
</div>
</div>
UPDATE 1: Working snippet to illustrate how it works.
UPDATE 2: Now we can attach event handler function for blur event using .on() and evaluate the attribute using $eval().
You can achieve this by fourth parameter of directive , i.e NgModelController, Read doc.
Code should be like:
link: function(scope, element, attr, ngModel) {
$timeout(function() {
console.log(element);
console.log(ngModel.$viewValue);
}, 0)
}
See this example fiddle

Two way binding Directive + Controller

I am dynamically adding a text field in DOM (from directive link function) & want to grab entered value and push it to controller scope object, but its always giving me undefined, below is my code:
<div class="input-group">
<span class="fieldIcon input-group-addon"><i class="fa fa-sticky-note" aria-hidden="true"></i></span>
<select name="addlist[]" multiple="multiple">
<option ng-repeat="options in optionList">{{options.label}}</option>
</select>
</div>
<script type="text/javascript">
angular.module('myapp')
.controller('AddContactController',[ '$scope', function ($scope) {
$scope.optionList = [{label: 'NewList'}];
$scope.addOption = function(optionList) {
console.log('List:', optionList); // its giving undefined
scope.optionList.push(optionList);
}
}])
.directive('optionList', ['$compile', function ($compile) {
return {
restrict: 'E',
templateUrl: '/templates/int_optionList.html',
controller: 'AddContactController',
link: function(scope, element, attrs) {
// Adding input field and on click of a button controllers addOption function should be called with the text field value
var addListField = '<input class="form-control" type="text" ng-modal="addList" name="addList" placeholder="Add new list...">'+
+'<button class="btn btn-default" type="button" ng-click="addOption()">';
addListField = $compile(addListField)(scope);
$(element).find('.multiselect-container').prepend(addListField);
}
}
}]);
</script>
Now here in addOption function I am getting optionList value as undefined.
Error is human :).
Replace ng-modal by ng-model in your directive
AngularJS expects models to be in object form, setting ng-modal="addList" to ng-modal="data.addList" might help.

Using angular form in directive template

I am using require: '^form' in my simple directive.
Then I trying to use this form in an ng-show but it doesn't seem to work.
Note: I don't want to pass the form name in as an attribute.
Can anyone see where i am going wrong? I only want the message to show when the form is invalid.
angular.module('xxx').directive('errorWall', errorWall);
function errorWall() {
return {
restrict: 'E',
require: '^form',
scope: {},
link: (scope, elm, attrs, frm) => {
scope.formCtrl = frm;
},
template: '<div ng-show="formCtrl.$invalid">You have error messages.</div>'
};
}
Make sure you've placed the directive inside the form with at least one input with a ng-model directive on it.
<form>
<input type="text" ng-model="name" required />
<error-wall></error-wall>
</form>
Here's a working fiddle https://jsfiddle.net/3gv8nvL3/3/ with one form required input.

Angular: directive access to form element

I have a form label containing an input:
<label data-live-email-check="http://www.example-service-uri.com/">
<span class="embedded-label">Email</span>
<input ng-model="formData.email"
type="email"
name="email"
placeholder="Email"
required/>
<span class="message" ng-show="emailValidationMessage">{{emailValidationMessage}}</span>
</label>
I want to create a directive that takes the URL provided by the data-live-email-check attribute and sends the email to that URL, validating whether or not it already exists.
angular.module("App").directive("liveEmailCheck", [function () {
return {
restrict: "A",
scope: {
ngModel: "="
},
link: function (scope, element, attrs) {
scope.$watch(
function(){
return scope.ngModel
},
function(newVal){
console.log(newVal);
}
);
}
}
}]);
I just want to watch the model on the input, and fire a request when it updates.
Since the directive is defined on the label element ngModel is not properly bound. What am I doing wrong? My watch expression is not logging anything because it never fires.
I know I could manually grab the input, but that just seems like I'd be breaking the "angular pattern" that I feel so "bound" by. The frustrating thing is that there seems to be so many ways to do everything in Angular that I can never tell if I'm approaching a problem correctly or not.
--Edit--
To provide the solution that I personally would take (out of ignorance of a "better" way), would be the following:
angular.module("App").directive("liveEmailCheck", [function () {
return {
restrict: "A",
require: ["^form"],
link: function (scope, element, attrs, ctrl) {
var formCtrl = ctrl[0];
scope.formEl = formCtrl[element.find("input").attr("name")];
scope.$watch(function(){return scope.formEl.$valid},
function(newVal){
console.log(newVal);
});
}
}
}]);
This WORKS, but I feel like it "breaks the angular rules".
A custom validation is written like this:
'use strict';
angular.module('App')
.directive('liveEmailCheck', function (){
return {
require: 'ngModel',
link: function (scope, elem, attr, ngModel){
ngModel.$validators.liveEmailCheck= function (value){
//Your logic here or in a factory
};
}
};
});
and then on your HTML it goes like
<input type="email" live-email-check="liveEmailCheck>
Basically you add your own validation to the set of build-in validations of angular.
What you need here is an ng-model asyncValidator. Here is a simple implementation of such a directive.
angular.module("App").directive('asyncEmailValidator', function ($http) {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
var emailValidationUrl = attrs.asyncEmailValidator;
ngModel.$asyncValidators.emailValidator = function (modelValue, viewValue) {
var value = modelValue || viewValue;
// NOTE: don't forget to correctly add the value to the url
return $http(emailValidationUrl + value).then(function (validationResponse) {
// NOTE: return rejected promise if validation failed else true
});
};
}
};
});
How you can use it in your case:
<label>
<span class="embedded-label">Email</span>
<input ng-model="formData.email"
async-email-validator="http://www.example-service-uri.com/"
type="email"
name="email"
placeholder="Email"
required/>
<span class="message" ng-show="<FormNameHere>.email.$error.emailValidator">
{{emailValidationMessage}}
</span>
</label>
This will be the right solution because it is implemented with angular ng-model validation which considers the validity of the model too.

Getting element id from Controller in Angularjs

Is it possible to add the element id in Angular,
<div class="col-sm-8">
<select class="form-control input-sm"
id="ruleResetType"
name="ruleResetType"
ng-model="rule.resetType"
ng-options="data as data for data in Type"
ng-required="true"
ng-disabled="isEditable(id)">
</select>
</div>
I wonder if is it possible to add id at isEditable(id)", I mean the id shall be the element.id?
This should work, but like Greg says, you probably don't want to use the element id.
app.directive("disableIfIn", function(){
return {
restrict: 'A',
transclude: true,
scope: {
disabledElements: '#disableIfIn'
},
template: '<div ng-transclude></div>',
link: function (scope, element, attrs) {
if(scope.disabledElements.indexOf(attrs.id) === -1){
element.prop('disabled', false);
}
}
};
});
Then (assuming the existence of disabledElements on your scope and elements are disabled by default) add this attribute to your HTML elements:
disable-if-in="{{disabledElements}}"

Categories