I want to access a function in a controller from an ng-included html, which contains a directive with an html template.
Meaning, given parent html template with controller:
<div ng-controller="profileCtrl">
<div ng-include src="profileContent"></div>
</div>
profileCtrl:
$scope.profileContent = '/html/views/myview.html';
$scope.test = 'This should show';
myview.html:
<my-directive></my-directive>
myDirective:
angular
.module('myModule')
.directive('myDirective', [function () {
return {
restrict: 'E',
templateUrl: '/html/directives/myDirectiveTemplate.html',
...
myDirectiveTemplate.html:
{{test}} //should output "This should show"
How can I access $scope.test from the child myDirective?
myDirectiveTemplate.html:
<form ng-submit="$parent.updateProfile()">
profileCtrl:
$scope.updateProfile = function() {
console.log('updating profile'); //not called
Try like this
{{$parent.test}}
Related
I write a directive that provide a tab functionality, I use latest version of AngularJS (v1).
In my directive I have a controller that expose an api to a children directives, the scope is isolated in all directives:
Parent Directive
scope: {},
controller: function($scope) {
$scope.values = [];
this.addValue = function(value) {
$scope.values.push(value);
}
}
Child Directive on link function
scope: {},
transclude: true,
template: '<div ng-transclude></div>',
replace: true,
link: function(scope, element, attr, parentCtrl)
{
parentCtrl.addValues('test')
}
In child directive I have a custom html with own controller:
TestCtrl
function TestCtrl($scope){
var vm = this;
... other logic
}
Implementation
<parent>
<child>
<div ng-controller="TestCtrl as t">
<button ng-click="addValues('new_test')"></button>
</div>
</child>
</parent>
I need to call "addValues" method (inside directive controller) on click on my button.
How to organise the code to make this?
var module = angular.module('app', []);
module.controller('root', function ($scope) {
$scope.test = function () {
console.log('i am clicked');
}
});
module.component('child', {
template: '<button type="button" data-ng-click="$ctrl.click()">Click me</button>',
controller: myController,
bindings: {
onClick: '&'
}
});
function myController() {
var ctrl = this;
ctrl.click = function () {
this.onClick();
}
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
</head>
<body data-ng-app="app">
<div data-ng-controller="root">
<child data-on-click="test()"></child>
</div>
</body>
</html>
It is just an example
Here you can read more.
Hope this helps.
MAy be it will be usefull to read about angular's best practice here
Suppose i have a directive CARD
.directive('card', [function() {
return {
restrict: 'E', // Element directive,
templateUrl: 'scripts/directives/card.html'
};
and a controller say CARDCONTROLLER
.controller('cardController',function($scope){
$scope.directivename = 'card';
});
and a HTML file say CARD.HTML
<div>
<{{directivename}}></{{directivename}}>
</div>
But the above doesnot work.
Does any one have any idea about how to accomplish this?
EDIT.
I dont want to generate the directive dynamically.Its just that i want to bind it through the controller without/with changing anything in directive
you must add the controller to your directive.
.directive('card', function() {
return {
restrict: 'E', // Element directive,
templateUrl: 'scripts/directives/card.html',
controller: 'cardController'
}
})
this should do the trick.
call your directive like,
<div>
<data-card>
// add code
</data-card>
</div>
You can define controller in your directive also
.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'scripts/directives/card.html',
controller: function($scope) {
//write your controller code here
},
controllerAs: 'myController'
};
});
see the below snippet , you will get idea how you can do this.
var app = angular.module('myapp',[]);
app.directive('card',function() {
return {
restrict: 'E', // Element directive,
template: '<h5>I am directive template</h5>'
};
});
app.controller('cardController',function($scope,$compile,$element){
$scope.directivename = 'card';
var template = $compile("<"+$scope.directivename+"/>")($scope);
$element.append(template);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="cardController">
</div>
I have a directive, form where the rest of the html is given. The directive is given below
THis is the html for directive
<div test
input="{{guage.input}}"
>
</div>
angular.module('test', [])
.directive('test', function () {
"use strict";
return {
restrict: 'A',
scope: {
input: '='
},
templateUrl: 'gauge/gauge.tpl.html',
replace: true
});
The below is the html loaded after the directive compilation.
<div ng-controller="Testing">
<div>
<div id="{{guageid}}" class="gauge ng-isolate-scope" ng-model="gauge.input" data-service="/api/getDataResult/core-mon-status" guageid="fleet_online" description="Fraction of fleet online" unit="%" test="{{gauge.test}}" input="{{gauge.input}}" gauge="">
</div>
</div>
</div>
Now I have a parent controller above this dom element name is Testing.
From my controller if I change the {{guage.input}} its not displaying.
This is my controller
app.controller('Testing',['$scope','newdataLoad','$timeout',function($scope, newdataLoad, $timeout){
$scope.gauge = {};
$scope.gauge.input = 0;
});
What is the problem with my scope here.
As your scope defines the input with = you dont need the expression brackets.
<div test input="guage.input">
Using expression brackets will break the 2-way binding.
Optimization:
You can completely move you controller into the directive and still make use of the dependency injection
"use strict";
angular.module('test', []).directive('test', function () {
return {
restrict: 'A',
scope: {
input: '='
},
templateUrl: 'gauge/gauge.tpl.html',
replace: true,
controller: function($scope, newdataLoad, $timeout){
$scope.gauge = {};
$scope.gauge.input = 0;
}
}
});
The template code then :
<div>
<div id="{{guageid}}" class="gauge ng-isolate-scope" ng-model="gauge.input" data-service="/api/getDataResult/core-mon-status" guageid="fleet_online" description="Fraction of fleet online" unit="%" test="{{gauge.test}}" input="{{gauge.input}}" gauge="">
</div>
</div>
Remove curlies:
<div test input="guage.input">
</div>
Directive in AngularJS: I find out that the elements inside an element with the directive do not inherit its "scope".
For example:
app
.controller('xxx', function($scope) {})
.directive('yyy', function() {
return {
scope: {},
link: function(scope,elem,attrs) {}
};
});
When we use it in the HTML:
<body ng-controller="xxx">
<div id='withD' yyy>
<div id='inside'>Inside the element with a directive</div>
</div>
</body>
"body" will have a scope whose $id may be 003;
then "#withD" will have an isolate scope $id=004;
the "#inside" will have the scope $id=003, which means the "#inside" inherits "body"'s scope.
If I use "transinclude" for the directive "yyy"; then "body" scope.$id=003, "#withD" scope.$id=004, "#inside" scope.$id=005; moreover, 003 has two children 004 and 005. However, I wanna make the element with the directive has an isolate scope and its child elements inherit the scope.
I read over "ui.bootstrap.tabs" source code but I do not like the style, for it is strange and also not make the parent element share its scope with child elements'; it looks like this:
app
.directive('xitem', function() {
scope: {},
controller: function($scope) {
$scope.subitem = [];
return {
add: function(xsubitem) {$scope.subitem.push(xsubitem);}
}
},
link: function(scope,elem,attrs) {}
})
.directive('xsubitem', function() {
require: '^xitem',
link: function(scope,elem,attrs,ctrl) {ctrl.add(elem);}
});
My expectation is that:
<div ng-controller="xxx">
<div yyy>
<button ng-click="sayHi()">Hi</button>
<div>
</div>
when you click the "Hi" button, the alert dialog will pop up with the message "Hello World" not "Error: Scope".
app
.controller('xxx', function($scope) {
$scope.sayHi = function(){alert('Error: Scope');};
})
.directive('yyy', function() {
return {
scope: {},
link: function(scope,elem,attrs) {
scope.sayHi = function(){alert('Hello World');};
}
};
});
Moreover, I tried this:
app
.controller('xxx', function($scope) {
$scope.sayHi = function(){alert('Error: Scope');};
})
.directive('yyy', function() {
return {
scope: {},
controller: function($scope, $compile) {$scope._compile = $compile;}
link: function(scope,elem,attrs) {
elem.children().forEach(function(one) {
scope._compile(one)(scope);
});
scope.sayHi = function(){alert('Hello World');};
}
};
});
Then it will pop up two alert dialogs with the message "Error: Scope" and "Hello World" respectively.
Now I found the solution - load template dynamically and use $compile to specify scope:
.controller('main', function($scope) {
$scope.sayHi = function() {alert('scope error');};}
)
.directive('scopeInherit', ['$http', '$compile', function($http, $compile) {
return {
scope: {},
link: function(scope, elem, attrs) {
scope.sayHi = function() {alert('hello world');};
scope.contents = angular.element('<div>');
$http.get(elem.attr('contentsURL'))
.success(function (contents) {
scope.contents.html(contents);
$compile(scope.contents)(scope);
});
},
};
}]);
Then we write HTML:
<div ng-controller="main">
<div scope-inherit contents="test.html"></div>
</div>
where there is a test.html:
<button ng-click="sayHi()">speak</button>
Then click on the "speak" button, it will pop up the alert dialog with "hello world"
To do what you want, you need to use a template (either as a string or a templateUrl). If angularjs would work how you expect it in this case then a lot of the angular directives wouldn't work right (such as ng-show, ng-click, etc).
So to work how you want it, change your html to this:
<script type="text/ng-template" id="zzz.html">
<button ng-click="sayHi()">Hi 2</button>
</script>
<div ng-controller="xxx">
<button ng-click="sayHi()">Hi 1</button>
<div yyy></div>
</div>
And update your directive definition to use a templateUrl (or you can provide the string as a template property)
app
.controller('xxx', function($scope) {
$scope.sayHi = function() {
console.error('Error: Scope in xxx', new Date());
};
})
.directive('yyy', function() {
return {
scope: {},
templateUrl: 'zzz.html',
link: function(scope, elem, attrs) {
scope.sayHi = function() {
console.log('Hello World in zzz', new Date());
};
}
};
});
Here's a plunker with this code: http://plnkr.co/edit/nDathkanbULyHHzuI2Rf?p=preview
Update to use multiple templates
Your latest comment was a question about what if you wanted to use different templates on the same page. In that case we can use ng-include.
html:
<div yyy contents="template1.html"></div>
<div yyy contents="template2.html"></div>
<div yyy contents="template3.html"></div>
js:
app
.controller('xxx', ...)
.directive('yyy', function() {
return {
scope: {
theTemplateUrl: '#contents'
},
template: '<ng-include src="theTemplateUrl"></ng-include>',
link: function(scope, elem, attrs) {
scope.sayHi = function() {
console.log('Hello World in yyy', new Date());
};
}
};
});
The benefit of using ng-include is that this is already built into angularjs and is well tested. Plus it supports loading template either inline in a script tag or from an actual url or even pre-loaded into the angular module cache.
And again, here is a plunker with a working sample: http://plnkr.co/edit/uaC4Vcs3IgirChSOrfSL?p=preview
I want to configure my directive module through my app level controllers. Plunker
index.html
<div ng-controller="App">
<foodz index="index"></foodz>
</div>
app.js
angular.module('app', ['foodz']).
controller('App', ['$scope',function($scope){
$scope.index = 1;
}]);
foodz.js
angular.module('foodz', []).
controller('foodzController', ['$scope',function($scope){
//Data is coming in through external API
$scope.$on('foodzFetched', function(e,d) {
$scope.foodz = d;
});
//Lets say data to looks like:
//[{"name":"banana"},{"name":"smoothy"}]
}]).
directive('foodz', function() {
return {
restrict: 'E',
scope:{
index: '#'
},
replace: true,
controller: 'foodzController',
templateUrl: 'foodzTemplate.html',
link: function(scope, controller) {}
};
});
foodzTemplate.html
<div ng-controller="foodzController">
<span>
{{foodz[index].name}}
</span>
</div>
So in this example, I am trying to pass the index through my app level controller into an attribute of my directive element which also has its own controller.
What am I doing wrong here?
Try to make scope:{index:'='}
Look at Directive Definition Object
I think you are receiving index='index' (as string) in your controller. Using = it will get the value from parent controller.