Access to template scope from isolated scope - angularjs

I'm trying to implement an AngularJS directive which has it own isolated scope in order to make it reusable in the same page. This directive is described by a template which is in another file so I use the templateUrl option.
app.directive('inputSettings', function () {
return {
restrict: 'E',
transclude: true,
templateUrl: 'templates/admin/inputSettingsTemplate.html',
controller: 'InputSettingsCtrl',
scope: {
settingkey: '=',
value: '=',
range: '=',
check: '=',
autosave: '='
}
};
});
In this template, I have a form named form.
<form name="form" class="settingForm" novalidate>
<div class="form-group" ng-class="{ 'has-error' : form.input.$invalid, 'has-success' : form.input.$valid }">
<input class="form-control" name="input" type="text" ng-model="valueInput" />
<p ng-show="form.input.$invalid && !form.input.$pristine" class="help-block" translate="{{ 'inputSettingErrorMessage' | translate:translationData }}"></p>
</div>
</form>
The problem I have is that I want to be able to set the validity of the input inside the template using a code like the following in the controller of my directive :
$scope.form.input.$setValidity("input", true);
But I have an error when I try to execute such a code. It seems that $scope does not know the form so I can't interact with my form within the controller of my directive.
Have you any idea? Is there anything I did wrong?
Thanks in advance !

Actually I manage to make my code works with a minimum amount of changes. I don't know if it is the good answer but it works like a charm for me.
I simply replaced the <form> tag by the <ng-form> and now it works perfectly. It means that the controller of my directive now can access to the form by it name.

Generally in a directive , to set the validity of a element we require ngModel for the directive and then we try to set the $validity of the element using the ngModelController

You have your controller for that directory, you can use that controller in order to manipulate the data.
function InputSettingsCtrl(){
var vm = this;
console.log(vm);
}
Also remember to add the controllerAs to your directive
controllerAs: 'ctrl',
Like this you can bind the variable from the controller to the view

Use $rootScope. It`s global. It contains all $scopeS..
Like
$rootScope.data = data; $scope.data = $rootScope.data

Related

pass ngKeyUp function into directive

I’ve got a directive/template that contains an input field.
The input field has an ngKeyup and an ngModel.
I want the ngKeyup function to be passed into the directive. The ngKeyup on the input field within the directive/template should invoke this function.
This plunker shows option 1 and option 2 http://plnkr.co/edit/kN8mitdG6pK5GNqGzYw5?p=preview
Option one is simplest and partially works, the function is simply passed in by '=', the directive references it in the ngKeyUp attribute
Directive
ngApp.directive("searchField", ['$parse',function ($parse) {
return {
restrict: "E",
scope: {
myKeyUp: '=',
Template
<input type="text" ng-model="model" ng-keyup="myKeyUp" />
This partially works, but the $event object is not passed.
In option two the directive receives the function from the controller as an '&', tries to $parse it and invoke it. This simply isn't working for me but I'm not very familiar with $parse.
ngKeyup can receive any parameters e.g. ng-keyup(a,b,c,$index,$event)
A key point/requirement is that this directive should be the same i.e. myKeyUp should be capable of accepting any parameters.
Any help or pointers much appreciated.
Thanks
John
It has to be '&' in scope, as it allows you to pass the function reference.
and what you have to do in the template is:
<input type="text" ng-model="model" ng-keyup="myKeyUp($event)" />
UPDATE:
Here is another approach that you could take: http://goo.gl/a27JrX
It turns out there's a really simple solution.
Quote from question
I want the ngKeyup function to be passed into the directive. The ngKeyup on the input field within the directive/template should invoke this function.
Turns out the div can simply have the ng-keyup attribute, it doesn't have to be on the input field.
This completely solves the problem.
http://plnkr.co/edit/xTigcDaSLpL9pvRtfY5J?p=preview
Now the directive takes only the model as a parameter. The ngKeyup is outside the directive.
ngApp.directive("searchField", ['$parse',function ($parse) {
return {
restrict: "E",
scope: {
model: '='
},
templateUrl: 'searchFieldTemplate',
replace: true,
controller: ['$scope','$attrs', function ($scope, $attrs) {
}]
};
}]);
And the template is now only concerned with the model
<div class="searchField">
<h4>Template</h4>
(observe the console)
<div>
<input type="text" ng-model="model" />
</div>
That actually makes allot of since when you think about it....

Declaring and using angular directives that map id, name, and ng-model to a single field

I'd like to create/use an AngularJS directive to avoid creating some redundant HTML and also to simplify how I use Bootstrap elements. I'd like to use a directive this way:
<myDirective id="person.lname" label="Last Name"></myDirective>
The template I'd like AngularJS to write is:
<label for="person.lname">Last Name</label>
<input id="person.lname" name="person.lname" ng-model="person.lname">
How would I declare a directive to be able to create this template and have the binding with ng-model still work? Are there any reasons why this would not be a good idea?
Thanks.
UPDATED
I added the label tag to reflect how the id/name would be used for the input element. The generated template would allow you to click on the label and have the focus be placed on the input element.
This should do what you want:
.directive('myDirective', function() {
return {
scope: {
model: '=identifier',
id: '#identifier',
label: '#',
},
restrict: 'E',
template: '<label for="{{id}}">{{label}}</label><input id="{{id}}" name="{{id}}" ng-model="model">'
};
})
View:
<my-directive label='Last name' identifier="person.lname"></my-directive>
Fiddle

validation of a form added with a directive

I would like to access the content of the validation variables provided from AngularJS for the forms.
I need to add forms using a directive like in the code, but if I do that I can't access the $dirty,$error,$invalid variables anymore and I need to access them.
JS:
myApp.directive('test', function() {
return {
scope : {
nameform: "=",
nameinput: "=",
type: "="
},
template: '<form name=nameform></form>',
restrict: 'E',
compile: function (elem,attr){
var form = elem.find("form");
if(attr.type == "text")
form.html('<input type="text" name="nameinput" ng-model="data.text" placeholder="type here..." required />');
}
};
});
HTML:
<test nameform="valform" nameinput="valinput" type="text"/>
{{valform.valinput.$invalid}}
I think that you can't. Because you are using isolated scope for building your directive, so you don't have acces to the information. You can try to build you directive using share scope, and I think that you would be able to access this information.
Change your directive to be ng-form instead of form (because ng-form is nestable). Then wrap your directive inside another form element and give that new form element a name. The outer form element will bind to your outer scope and you can access the properties that way.
Directive template:
"<ng-form name="valform"></ng-form>"
HTML:
<body ng-view>
<div>Valid: {{outerform.$valid}}</div>
<div>Errors: {{outerform.$error}}</div>
<form id="outerform" name="outerform">
<test type="text"/>
</form>
</body>
Side note, form names don't play nice with dynamic names. The plunkr below uses static names so I could help you with your current problem. I found a solution to the dynamic name problem on a different SO post...I think this one...
Dynamic validation and name in a form with AngularJS
Here's a plnkr with your working form...
http://plnkr.co/edit/RFrRXp2kWkP1Mefwl3Kn?p=preview
When you build your HTML for your input controls, make sure you append the name attribute correctly based on 'nameinput' passed in as an attribute:
myApp.directive('test', function() {
return {
scope : {
nameform: "=",
nameinput: "=",
type: "="
},
template: '<form name=nameform></form>',
restrict: 'E',
compile: function (elem,attr){
var form = elem.find("form");
if(attr.type == "text")
form.html('<input type="text" name="' + attr.nameinput +'" ng-model="data.text" placeholder="type here..." required />');
}
};
});

Angular directives: mixing attribute data and ngModel

I've had luck building directives that share scope, and ones that isolate scope, but I'm having trouble figuring out the correct way to do both.
<input type="text" ng-model="search" append-me terms="myTerms">
I'm trying to take an input like the one above, and staple a UL that ng-repeats over a list of attributes after it. I have two problems.
1) How do I correctly interface the shared ng-model scope?
2) What am I doing incorrectly with this compile function?
http://jsfiddle.net/vEQ6W/1/
Mixing isolated scope with ngModel is a documented issue, see the Isolated Scope Pitfall section in the documentation:
Isolated Scope Pitfall
Note that if you have a directive with an isolated scope, you cannot require ngModel since the model value will be looked up on the isolated scope rather than the outer scope. When the directive updates the model value, calling ngModel.$setViewValue() the property on the outer scope will not be updated. However you can get around this by using $parent.
Using this knowledge and some freaky scope experiments I've come with two options that does what you want to do:
(1) See this fiddle It makes use of the $parent method as described above.
<div ng-controller="MyCtrl">
<div>
<input ng-form type="text" ng-model="$parent.search" append-me terms="myTerms">
</div>
{{search}}
</div>
myApp.directive('appendMe', ['$compile', function($compile) {
return {
restrict: 'A',
scope: {terms: '='},
link: function(scope, element, attributes) { // linking function
console.log(scope.terms);
var template = '<p>test</p>' +
'<ul><li ng-repeat="term in terms">{{term}}</li></ul>' +
'<p>hm…</p>'
element.after($compile(template)(scope));
}
}
}]);
(2) See this fiddle It does not use $parent, instead it uses the isolated scope to publish the search model as configured via ngModel.
<div ng-controller="MyCtrl">
<div>
<input ng-form type="text" ng-model="search" append-me terms="myTerms">
</div>
{{search}}
</div>
myApp.directive('appendMe', ['$compile', function($compile) {
return {
restrict: 'A',
scope: {terms: '=', search: '=ngModel'},
link: function(scope, element, attributes) { // linking function
console.log(scope.terms);
var template = '<p>test</p>' +
'<ul><li ng-repeat="term in terms">{{term}}</li></ul>' +
'<p>hm…</p>'
element.after($compile(template)(scope));
}
}
}]);
Both options seem to work just fine.
Regarding #2, "What am I doing incorrectly with this compile function?"
If you change your compile's code snippet from...
...
tElement.after(
'<p>test</p>' +
'<ul><li ng-repeat="term in terms">{{term}}</li></ul>' +
'<p>hm…</p>'
);
...
to...
...
tElement.after(
'<p>test</p>' +
'<ul><li ng-repeat="term in myTerms">{{term}}</li></ul>' +
'<p>hm…</p>'
);
...
the ng-repeat will render correctly. I cannot, however, tell you WHY it works.

AngularJS directive with callback in scope prevents other directives from working

I've created a directive with an isolated scope:
directive("myDirective",function() {
return {
restrict: "A",
scope: {aaa: '='},
link: function(scope, elem) { }
};
})
The directive works well, but it prevents all other dierctives on the same element from working...
I've reproduced the problem on this jsfiddle: http://jsfiddle.net/ironshay/k5ndr/
I'm using angular 1.0.7.
Am I doing something wrong? or is it some kind of an angular bug?
The issue is that you've created a child scope by using scope: {} on the directive. So the foo variable isn't on that scope anymore, it's on the parent. By using $parent.foo everything works correctly:
<input type="text" placeholder="with directive" ng-disabled="!$parent.foo || $parent.foo == ''" my-directive func="myFunc()" />
jsfiddle: http://jsfiddle.net/k5ndr/2/
You are creating isolated scope, which i think is causing the other directives also to get the isolated scope.
You can try to pass the foo variable to the isolated scope as you did the myFunc function.
<input type="text" placeholder="with directive" foo='foo' ng-disabled="!foo || foo == ''" my-directive func="myFunc()" />
I tried to create a fiddle for the same
http://jsfiddle.net/njXjj/

Resources