I want to get a value straight from an attribute directive:
<form cronos-dataset="People as p">
Form Content
</form>
In my JS I tried:
app.directive('cronosDataset',[function() {
return {
restrict: 'A',
controller: 'CronosGenericDatasetController',
scope: {
"cronos-dataset" : '#'
}
};
}])
.controller("CronosGenericDatasetController",['$scope', function($scope) {
alert($scope["cronos-dataset"]);
}]);
I want to alert "People as p" string but I get undefined. Is that right path or should I go thorough a different approach?
You are supposed to have camelCase in the scope declaration
app.directive('cronosDataset',[function() {
return {
restrict: 'A',
controller: 'CronosGenericDatasetController',
scope: {
cronosDataset : '#'
}
};
}])
Here is a demo to see different variations
http://plnkr.co/edit/G6BiGgs4pzNqLW2sSMt7?p=preview
Make a link function instead:
app.directive('cronosDataset',[function() {
return {
scope: {},
restrict: 'A',
link: function (scope, elem, attrs) {
alert(attrs.cronosDataset);
}
Related
I am trying to dynamically load some HTML stored in a JSON file using Angular.
I am doing this by reading the JSON data into a scope and passing it to a directive that I wrote for loading HTML into the template.
Controller
.controller('testCtrl', function($scope, $http, $state){
$http.get('views/foo.json').then(function(res){
$scope.somehtml = res.data;
});
})
Directive
.directive('loadHtml', function($compile){
return {
restrict: 'AE',
scope: {
content: "#",
},
link: function(scope, element, attrs) {
scope.content = attrs.content;
element.html(scope.content).show();
$compile(element.contents())(scope);
},
template: '{{content}}'
};
})
This works!
<load-html content="hello success"></load-html>
This doesn't : (
<load-html content="{{somehtml}}"></load-html>
What am I missing here??
Found the solution myself, perhaps this helps someone:
I needed to "observe" the attribute value in the directive.
New Directive:
.directive('loadHtml', function($compile){
return {
restrict: 'AE',
scope: {},
link: function(scope, element, attrs) {
attrs.$observe('content', function(val) { /* $observing the attribute scope */
scope.content = val;
element.html(scope.content).show();
$compile(element.contents())(scope);
})
},
template: '{{content}}'
};
})
I'm having trouble with an angular directive. It doesn't seem to run the link function.
I feel like it's something obvious, but I just can't figure it out.
The directive is required as seen from below
angular.module('test').requires // ["injectedModule"]
Code below. Fiddle.
Any help would be amazing.
angular
.module('test', ['injectedModule'])
.controller('tester', [
function() {
this.test = function(data) {
alert(data);
}
}
]);
angular
.module('injectedModule', [])
.directive('testing', [
function() {
return {
restrict: 'E',
scope: true,
link: function(scope, element, attrs) {
alert(scope, element, attrs);
}
};
}
]);
<div ng-app="test">
<div ng-controller="tester as t">
<video id="test" ng-src="https://scontent.cdninstagram.com/hphotos-xfa1/t50.2886-16/11726387_1613973172221601_1804343601_n.mp4" testing="t.test(el)" />
</div>
</div>
Looks to me like
restrict: 'E',
should be
restrict: 'A',
Your directive isn't being called at all as it is.
I think the error is in the restriction you are giving to your directive.
You are restricting your directive to match only element (in other words tag). You should restrict to match attribute 'A'. Here's angular official documentation https://docs.angularjs.org/guide/directive
and here's your fiddle working
Code sample:
angular
.module('injectedModule', [])
.directive('testing', [
function() {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attrs) {
alert(scope, element, attrs);
}
};
}
]);
I have the following code:
<div id='parent'>
<div id='child1'>
<my-select></my-select>
</div>
<div id='child2'>
<my-input></my-input>
</div>
</div>
I also have two directives which get some data from the data factory. I need the two directives to talk to each other such that when a value in select box is changed the input in changes accordingly.
Here's my two directives:
.directive("mySelect", function ($compile) {
return {
restrict: 'E',
scope:'=',
template: " <select id='mapselectdropdown'>\
<option value=map1>map1</option> \
<option value=map2>map2</option> \
</select>'",
link: function (scope, element, attrs) {
scope.selectValue = //dont konw how to get the value of the select
}
};
})
.directive("myInput", function($compile) {
return {
restrict: 'E',
controller: ['$scope', 'dataService', function ($scope, dataService) {
dataService.getLocalData().then(function (data) {
$scope.masterData = data.input;
});
}],
template: "<input id='someInput'></input>",
link: function (scope, element, attrs) {
//here I need to get the select value and assign it to the input
}
};
})
This would essentially do the onchange() function that you can add on selects. any ideas?
You could use $rootScope to broadcast a message that the other controller listens for:
// Broadcast with
$rootScope.$broadcast('inputChange', 'new value');
// Subscribe with
$rootScope.$on('inputChange', function(newValue) { /* do something */ });
Read Angular docs here
Maybe transclude the directives to get access to properties of outer scope where you define the shared variable ?
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
-> https://docs.angularjs.org/guide/directive
After much research this is what worked...
I added the following:
.directive('onChange', function() {
return {
restrict: 'A',
scope:{'onChange':'=' },
link: function(scope, elm, attrs) {
scope.$watch('onChange', function(nVal) { elm.val(nVal); });
elm.bind('blur', function() {
var currentValue = elm.val();
if( scope.onChange !== currentValue ) {
scope.$apply(function() {
scope.onChange = currentValue;
});
}
});
}
};
})
Then on the element's link function I added:
link: function (scope, elm, attrs) {
scope.$watch('onChange', function (nVal) {
elm.val(nVal);
});
}
Last added the attribute that the values would get set to in the scope:
<select name="map-select2" on-change="mapId" >
I understand that I can dynamically set a templateUrl base on an option DOM attribute template-url="foo.html" given the following code:
angular.module('foo').directive('parent', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// code
},
templateUrl: function(elem,attrs) {
return attrs.templateUrl || 'some/path/default.html'
}
}
});
However, I need to take this a step further and pass this string one level deeper, to a child directive.
Given this HTML:
Usage in Main project
<parent parent-template="bar.html" child-template="foo.html"></parent>
The child will not be exposed in most cases, so if child-template is set, it needs to implicitly replace templateUrl for all child <child></child> elements that are located in the parent foo.html.
The require: '^parent' attribute passes data from scope to scope, but I'm not seeing this available in templateUrl when it's declared.
foo.html
<h1>Title</h1>
<child ng-repeat="item in array"></child>
Directives
angular.module('foo').directive('parent', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// code
},
templateUrl: function(elem,attrs) {
return attrs.parentTemplate || 'some/path/default.html'
},
scope: {
childTemplate: '=childTemplate'
}
}
})
.directive('child', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// code
},
templateUrl: function(elem,attrs) {
return ??? // parent.attribute.childTemplate? || 'some/path/default.html'
},
require: '^parent',
scope: {
childTemplate: '=childTemplate'
}
}
});
Update
The old answer (see bellow) won't work because it's only possible to access the controller of the required directives inside the link functions, and the templateUrl function gets executed before the link functions.
Therefore the only way to solve this is to handle everything in the templateUrl function of the child directive. However this function only takes 2 arguments: tElement and tArgs.
So, we will have to find the element of the parent directive and access the attribute child-template. Like this:
angular.module('testApp', [])
.directive('parent', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
},
transclude:true,
templateUrl: function(elem,attrs) {
return attrs.parentTemplate || 'default.html'
}
}
})
.directive('child', function() {
return {
restrict: 'E',
require:'^parent',
templateUrl: function(elem,attrs) {
//if jQuery is loaded the elem will be a jQuery element, so we can use the function "closest"
if(elem.closest)
return elem.closest("parent").attr("child-template") || 'default.html';
//if jQuery isn't loaded we will have to do it manually
var parentDirectiveElem=elem;
do{
parentDirectiveElem=parentDirectiveElem.parent();
}while(parentDirectiveElem.length>0 && parentDirectiveElem[0].tagName.toUpperCase()!="PARENT");
return parentDirectiveElem.attr("child-template") || 'default.html';
}
}
});
Example
Old Answer
Since you are isolating the scope, you could try this, it's a bit hacky but I guess that it should work:
angular.module('foo').directive('parent', function() {
return {
restrict: 'E',
controller: function($scope) {
this.childTemplate=$scope.childTemplate;
},
link: function(scope, element, attrs) {
},
templateUrl: function(elem,attrs) {
return attrs.parentTemplate || 'some/path/default.html'
},
scope: {
childTemplate: '#'
}
}
})
.directive('child', function() {
return {
restrict: 'E',
require: '^parent',
link: function(scope, element, attrs, parentController) {
if(parentController.childTemplate)
element.data("childTemplate", parentController.childTemplate);
},
templateUrl: function(elem,attrs) {
return elem.data("childTemplate") || 'some/path/default.html'
}
}
});
In my question, I was attempting to provide an override for the templateUrl of an off-the-shelf directive that didn't have one. My original question doesn't mention this, however, I wanted to add this as a reference to others who may have forgotten, as I did. Angular allows you to decorate directives and override their properties.
app.config(function($provide) {
$provide.decorator('child', function($delegate) {
var directive = $delegate[0];
directive.templateUrl = 'path/to/custom.html';
return $delegate;
});
});
Have the following problem. I want to make two directives. One of them will be an attribute for another.
Something like this.
<html>
<title>Directives</title>
<head lang="en">
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js" type="text/javascript"></script>
<script src="main.js"></script>
</head>
<body ng-app="app">
<outer inner></outer>
</body>
</html>
The directive source code is here:
var app = angular.module('app', []);
app.directive('inner', function() {
return {
require: "^ngModel",
restrict: "AC",
transclude: true,
replace: false,
templateUrl: /* here is a path to template it's not interesting*/,
controller: function($scope) {
console.log('controller...');
},
link: function(scope, element, attrs) {
console.log('link...');
}
};
});
app.directive('outer', function($q, $rootScope) {
return {
require: "^ngModel",
restrict: "E",
replace: true,
scope: { /* isolated scope */ },
controller: function($scope) {},
templateUrl: /* path to template */,
link: function (scope, elem, attrs, ctrl) {}
}
});
The problem is that controller of outer works, but inner doesn't... Neither link nor controller function works... Can't understand what is wrong...
Any ideas?
The reason its not working is because both directives have been asked to render a template on the same element, and its ambiguous as to which one should be given priority.
You can fix this by giving the inner directive priority over the outer directive (higher numbers indicate higher priority).
Inner:
app.directive('inner', function() {
return {
priority:2,
restrict: "AC",
transclude: true,
replace: false,
template: "<div>{{say()}}<span ng-transclude/></div>",
controller: function($scope) {
$scope.message = "";
$scope.say = function() {
return "this is message";
};
// $scope.say(); // this doesn't work as well
console.log('controller...');
},
link: function(scope, element, attrs) {
// alert('hey');
// console.log('link...');
}
};
});
Also, both directives cannot transclude their contents. One must be 'transclude:false' and the other must be transclude:true.
app.directive('outer', function($q, $rootScope) {
return {
priority:1,
restrict: "E",
transclude:false,
scope: { /* isolated scope */ },
controller: function($scope) {
$scope.message = "";
$scope.sayAgain = function() {
return "one more message";
};
$scope.sayAgain(); // this doesn't work as well
},
template: "<div>{{sayAgain()}}</div>",
link: function (scope, elem, attrs, ctrl) {}
}
});
Here is a working fiddle:
JSFiddle