ng-include on load with an object - angularjs

This works
<div ng-include="template.html" onload="person='Jane'"></div>
^ This sets the local scope variable person in the include to 'Jane' (string)
But I want to pass a person object that is called user: {name: 'Jane' }
<div ng-include="template.html" onload="person=user"></div>
^ This sets the local scope variable person in the include to be 'undefined'
How is it possible to pass an object as a local variable to ng-include?

Maybe what you actually want is a custom directive:
<div person-directive="{name:'Jane'}"></div>
JS:
angular.module('myApp',[])
.directive('personDirective',function(){
return {
restrict: 'A',
scope: {
person: '=personDirective'
},
templateUrl: 'template.html'
};
});
With this, you bind the passed-in value to person in the loaded template.
Working fiddle

ng-include is not that reusable because it doesn't offer a way to set local variables. Using onload is bad because it litters the global scope. If the example gets a little more complicated, it'll fail.
Making a new directive instead of using ng-include is a cleaner solution.
The ideal usage looks like:
<div ng-include-template="template.html" ng-include-variables="{ person: 'Jane' }"></div>
<div ng-include-template="template.html" ng-include-variables="{ person: user }"></div>
The directive is:
.directive(
'ngIncludeTemplate'
() ->
{
templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
restrict: 'A'
scope: {
'ngIncludeVariables': '&'
}
link: (scope, elem, attrs) ->
vars = scope.ngIncludeVariables()
for key, value of vars
scope[key] = value
}
)
You can see that the directive doesn't use the global scope. Instead, the directive reads the object from ng-include-variables and add those members to its own local scope.
This is clean and generic.

Related

Angular js directive and restrict option

i was reading a write up on directive from this url https://docs.angularjs.org/guide/directive
The restrict option is typically set to:
'A' - only matches attribute name
'E' - only matches element name
'C' - only matches class name
'M' - only matches comment
<div ng-controller="Controller">
<my-customer></my-customer>
</div>
angular.module('docsRestrictDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
templateUrl: 'my-customer.html'
};
});
template html file
Name: {{customer.name}} Address: {{customer.address}}
please help me to understand what is the meaning of restrict: 'E', ?
i am looking for a example where restrict will be A or C
please show me the usage of restrict: 'A' and C
also tell me how could i pass multiple argument to directives ?
thanks
let say you have a directive 'myDirective'
if in restrict you have only C you can only use it as classes like this :
<div class="my-directive"></div>
If it's A it's as attribute :
<div my-directive></div>
If it's E it's as element
<my-directive></my-directive>
TO pass argument you generaly se attributes :
<div my-directive my-first-argument="toto" my-second-argument="titi"></div>
To get the value you have to way :
use the attr provided in link function
use the scope with one way or two way binding.
Personnaly i prefer the attribute approach when it comme to directive, element after and class in last. I have already bootstrap based on classes i don't want to clash with it.
please help me to understand what is the meaning of restrict: 'E', ?
The 'E' restriction will match by element name:
<my-customer></my-customer>
i am looking for a example where restrict will be A or C
please show me the usage of restrict: 'A' and C
The 'A' restriction will match by an element attribute:
<div my-customer=""></div>
The 'C' restriction will match by a class:
<div class="my-customer"></div>
also tell me how could i pass multiple argument to directives ?
It depends on your requirements, but one simple way is using an isolated scope:
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
arg1: "#",
arg2: "#"
},
templateUrl: 'my-customer.html'
};
});
<my-customer arg1="first" arg2="second"></my-customer>
This SO answer gives a pretty good explanation of this process.
restrict is key to be used in DDO (Directive Definition Object) to inform what it Would be in view
for Example in DDO (Directive Definition Object)
Directive with restrict:E (Element)
Directive
app.directive('my-directive',function(){
return {
restrict: 'E', //means HTML Element
...
};
});
so directive in View for restrict:'E' as Element in HTML element as below
view
<myDirective></myDirective>
Directive with restrict:A (Attributes)
Directive
app.directive('my-directive',function(){
return {
restrict: 'A', //means HTML attribute
...
};
})
so directive in View for restrict:'A' as attribute in HTML element as below
view
<div myDirective></div>
Directive with restrict:'C' (class)
Directive
app.directive('my-directive',function(){
return {
restrict: 'C', //means HTML attribute
...
};
})
so directive in View for restrict:'C' as class in HTML element as below
view
<div class="myDirective"></div>
you can use Isolate scope (doesn't inherit from its parents scope) or you can pass variables to attributes which depends on your implementation both can used in directive's link and controller
for isolate scope DDO will be as
<my-directive variable1="hello" variable2="world" variable3="call()"></my-directive>
app.directive('myDirective',function(){
restrict:E,
scope:{
variable1:'#variable1',
variable2:'=varialbe2',
variable3:'&variable3'
}
})
Or you can also pass data through attributes and access them using attrs in Directive's link Function and $attrs DI in Directives Controller

$scope.$apply() is not updating the parent scope. Is this a binding issue?

Perhaps this is a poor way of doing this, so I'm open to changing this.
I am implementing a drag and drop function through a <drag-item> directive and a <drop-target> directive. The <drop-target> directive successfully calls this function in the parent scope:
$scope.testAddSet = function(){
console.log($scope.workoutItem); // gives me the last value
$scope.thisMonth[0].sets.push($scope.workoutItem);
$scope.$apply(); // needed to update the view, but uses the old value for workoutItem "test"
};
$scope.workoutItem = "test"; // value declared here
but my <drag-item> directive doesn't seem to update the value of $scope.workoutItem,
View:
<div ng-repeat="exercise in exercises">
<drag-item workout-item="workoutItem" exercise="exercise"></drag-item>
</div>
<drag-item> directive:
angular.module('app.directives.dragItem', [])
.directive('dragItem', function(){
return {
restrict: 'E',
scope: {
exercise: '=',
workoutItem: '='
},
templateUrl: "templates/dragTile.html",
link: function(scope, element, attrs){
element.on('dragstart', function (event) {
scope.workoutItem = scope.exercise.name; //correctly updates the local scope, but not the parent scope
console.log(scope.workoutItem);
});
}
};
});
How do I fix this?
Looks like the problem is that you are defining $scope.workoutItem in your parent scope and then directive is creating another workoutItem property at directive's scope level. To avoid this, it is recomended that you use dots(.) to define your models. Please try something like:
$scope.workout = {item: undefined}
and then in your view:
<div ng-repeat="exercise in exercises">
<drag-item workout-item="workout.item" exercise="exercise"></drag-item>
</div>
I think this should solve your problem without modifying the directive itself.

Acessing form from directive's link function

In my custom directive I'm declaring new form <div ng-form="inputForm"></div> with input in it.
How can I access to this form within link function? scope.inputForm is undefined:/
Edit: code sample
.directive('ifInput', ['$system', function ($system) {
return {
restrict: "E",
replace: true,
scope: {},
link: function (scope, element, attrs) {
scope.temp = function () {
console.log(scope.inputForm);
}
},
templateUrl: "template/if-input-pola/index.html"
};
}])
.run(["$templateCache", function ($templateCache) {
$templateCache.put("template/if-input-pola/index.html",
"<div ng-form=\"inputForm\" class=\"form-group ng-fadeInLeft\" ng-class=\"{'has-error': inputForm[kolumna.id].$invalid && inputForm.$dirty}\">" +
"{{inputForm|json}}"+ //here is correct object (with $error and so on)
"<a ng-click=\"temp()\">a</a>" + //here is undefined
"</div>"
);
}])
EDIT 2:
Problem was that I wan's using object like this ng-form="model.inputForm" - now everything is ok
You can just define ng-model e.g.
<input ng-model="myInput"> and
link: function (scope, element) {
console.log(scope.myInput);
}
According to the documentation, using ng-form="inputForm" should post the controller on the local scope. If you cannot see it, you're either looking at a nested scope with that property overwritten, or at an ancestor scope. Keep in mind, that some directives, such as ng-repeat or ng-if, create local scopes, so in the following code:
<div ng-controller="myController">
<div ng-if="someCondition">
<div ng-form="myInput">
...
</div>
</div>
</div>
myInput would NOT be visible on the scope managed by myController.
When accesing the scope property within a link function, it also depends where your linked directive is, as the property may not be assigned to the scope yet. Try $watching the property and you just might find out it's being assigned later.

Angularjs - Pass argument to directive

Im wondering if there is a way to pass an argument to a directive?
What I want to do is append a directive from the controller like this:
$scope.title = "title";
$scope.title2 = "title2";
angular.element(document.getElementById('wrapper')).append('<directive_name></directive_name>');
Is it possible to pass an argument at the same time so the content of my directive template could be linked to one scope or another?
here is the directive:
app.directive("directive_name", function(){
return {
restrict:'E',
transclude:true,
template:'<div class="title"><h2>{{title}}</h3></div>',
replace:true
};
})
What if I want to use the same directive but with $scope.title2?
You can pass arguments to your custom directive as you do with the builtin Angular-directives - by specifying an attribute on the directive-element:
angular.element(document.getElementById('wrapper'))
.append('<directive-name title="title2"></directive-name>');
What you need to do is define the scope (including the argument(s)/parameter(s)) in the factory function of your directive. In below example the directive takes a title-parameter. You can then use it, for example in the template, using the regular Angular-way: {{title}}
app.directive('directiveName', function(){
return {
restrict:'E',
scope: {
title: '#'
},
template:'<div class="title"><h2>{{title}}</h2></div>'
};
});
Depending on how/what you want to bind, you have different options:
= is two-way binding
# simply reads the value (one-way binding)
& is used to bind functions
In some cases you may want use an "external" name which differs from the "internal" name. With external I mean the attribute name on the directive-element and with internal I mean the name of the variable which is used within the directive's scope.
For example if we look at above directive, you might not want to specify another, additional attribute for the title, even though you internally want to work with a title-property. Instead you want to use your directive as follows:
<directive-name="title2"></directive-name>
This can be achieved by specifying a name behind the above mentioned option in the scope definition:
scope: {
title: '#directiveName'
}
Please also note following things:
The HTML5-specification says that custom attributes (this is basically what is all over the place in Angular applications) should be prefixed with data-. Angular supports this by stripping the data--prefix from any attributes. So in above example you could specify the attribute on the element (data-title="title2") and internally everything would be the same.
Attributes on elements are always in the form of <div data-my-attribute="..." /> while in code (e.g. properties on scope object) they are in the form of myAttribute. I lost lots of time before I realized this.
For another approach to exchanging/sharing data between different Angular components (controllers, directives), you might want to have a look at services or directive controllers.
You can find more information on the Angular homepage (directives)
Here is how I solved my problem:
Directive
app.directive("directive_name", function(){
return {
restrict: 'E',
transclude: true,
template: function(elem, attr){
return '<div><h2>{{'+attr.scope+'}}</h2></div>';
},
replace: true
};
})
Controller
$scope.building = function(data){
var chart = angular.element(document.createElement('directive_name'));
chart.attr('scope', data);
$compile(chart)($scope);
angular.element(document.getElementById('wrapper')).append(chart);
}
I now can use different scopes through the same directive and append them dynamically.
You can try like below:
app.directive("directive_name", function(){
return {
restrict:'E',
transclude:true,
template:'<div class="title"><h2>{{title}}</h3></div>',
scope:{
accept:"="
},
replace:true
};
})
it sets up a two-way binding between the value of the 'accept' attribute and the parent scope.
And also you can set two way data binding with property: '='
For example, if you want both key and value bound to the local scope you would do:
scope:{
key:'=',
value:'='
},
For more info,
https://docs.angularjs.org/guide/directive
So, if you want to pass an argument from controller to directive, then refer this below fiddle
http://jsfiddle.net/jaimem/y85Ft/7/
Hope it helps..
Controller code
myApp.controller('mainController', ['$scope', '$log', function($scope, $log) {
$scope.person = {
name:"sangeetha PH",
address:"first Block"
}
}]);
Directive Code
myApp.directive('searchResult',function(){
return{
restrict:'AECM',
templateUrl:'directives/search.html',
replace: true,
scope:{
personName:"#",
personAddress:"#"
}
}
});
USAGE
File :directives/search.html
content:
<h1>{{personName}} </h1>
<h2>{{personAddress}}</h2>
the File where we use directive
<search-result person-name="{{person.name}}" person-address="{{person.address}}"></search-result>
<button my-directive="push">Push to Go</button>
app.directive("myDirective", function() {
return {
restrict : "A",
link: function(scope, elm, attrs) {
elm.bind('click', function(event) {
alert("You pressed button: " + event.target.getAttribute('my-directive'));
});
}
};
});
here is what I did
I'm using directive as html attribute and I passed parameter as following in my HTML file. my-directive="push" And from the directive I retrieved it from the Mouse-click event object. event.target.getAttribute('my-directive').
Insert the var msg in the click event with scope.$apply to make the changes to the confirm, based on your controller changes to the variables shown in ng-confirm-click therein.
<button type="button" class="btn" ng-confirm-click="You are about to send {{quantity}} of {{thing}} selected? Confirm with OK" confirmed-click="youraction(id)" aria-describedby="passwordHelpBlock">Send</button>
app.directive('ngConfirmClick', [
function() {
return {
link: function(scope, element, attr) {
var clickAction = attr.confirmedClick;
element.on('click', function(event) {
var msg = attr.ngConfirmClick || "Are you sure? Click OK to confirm.";
if (window.confirm(msg)) {
scope.$apply(clickAction)
}
});
}
};
}
])

Why is what I set in rootScope not available in a directive?

I am setting the following:
function appRun(
$rootScope
) {
$rootScope.abc = 99;
}
I am calling a directive like this:
<admin-retrieve-button ctrl="exam" home="home" Network="Network"></admin-retrieve-button>
Here's my directive:
app.directive('adminRetrieveButton', ['stateService', function (stateService) {
return {
scope: {
ctrl: '=',
home: '=',
Network: '=',
abc: '='
},
restrict: 'E',
template: "xxxx {{ abc }} dddd",
link: function (scope, element, attrs) {
scope.stateService = stateService;
scope.entity = attrs["entity"];
}
};
}]);
However when my HTML page comes up it shows:
xxxx dddd
Can someone tell me why the rootScope value of 99 does not show up?
Because you're declaring an isolate scope on your directive, thus creating a new parent less scope. One way to fix this would be to obviously explicitly pass in abc in your HTML:
<admin-retrieve-button ctrl="exam" abc="abc" home="home" Network="Network"></admin-retrieve-button>
Or you could change your directive to not create a scope of its own:
scope:false
or just leave the scope property alone (don't declare it), however passing in a directives dependencies through a scope of its own is good practice IMO, it's more explicit and gets rid of hidden dependencies.

Resources