I know it is possible to put Controller inside the Directive but I can't make it work of how it should be.
Added plunker link below.
My Working Directive and Controller in One Javascript File
This code works perfectly but I want to modify the app.controller and move it inside my app.directive
app.controller('NameCtrl', function () {
var vm = this;
vm.namePattern = '/^[a-zA-Z ]{1,25}$/';
});
app.directive('nameDirective', function () {
return {
restrict: 'AE',
templateUrl: '/name.html'
}
});
some codes in my Index HTML file
the first <div> is for the above code, and the second <div> is for my directive that I am working on(below).
<div ng-controller="NameCtrl" name-directive></div>
<!--<div name-directive></div> -->
codes in templateURl name.html file
it seems that ng-pattern="ctrl.namePattern" is not working at all, but it is completely working when I use the first JS code above and just modify it to ng-pattern="namePattern".
<input type="text" name="username" class="form-control" placeholder="Enter your first name"
ng-model="username"
ng-minlength="2"
ng-maxlength="20"
required
ng-pattern="ctrl.namePattern"/>
The codes I am working on my JS file (from the codes above)
app.directive('nameDirective', function () {
return {
restrict: 'AE',
scope: {
},
controller: function($scope) {
$scope.namePattern = '/^[a-zA-Z ]{1,25}$/';
},
controllerAs: 'ctrl',
bindToController: true,
templateUrl: '/name.html'
}
});
I hope, I explain clearly my problem.
This is the link of my sample code that I am working on.
If aren't using isolated scope, just remove it from your directive or use scope: true...
app.directive('nameDirective', function () {
return {
restrict: 'AE',
//scope: {}, //Or you can use scope: true (It hasn't much sense in your case)
controllerAs: 'ctrl',
bindToController: true,
controller: function() {
var vm = this;
vm.namePattern = '/^[a-zA-Z ]{1,25}$/';
},
templateUrl: '/name.html'
}
});
See your directive working, I've updated your PLUNKER.
Related
I am developing an angular framework where user can configure header, menu, footer and selected pages using custom directives. To complete this requirement, at one point I need the following. I have seen example on the net, but does not really explain it well.
The requirement is that the templateUrl of the first custom directive shall be replaced with a template attribute that should call another custom directive.
The following code with templateUrl works fine.
angular.module("app",[]);
angular.module("app").controller("productController", ['$scope', function ($scope) {
}]);
angular.module("app").directive("tmHtml", function () {
return {
transclude: false,
scope: {
},
controller: "productController",
templateUrl: "/templates/HideShow.html"
};
});
However, when I change the above code as follows. I am making the change so that my custom directive tmHtml calls another custom directive.
angular.module("app").directive("tmHtml", function () {
return {
transclude: false,
scope: {
},
controller: "productController",
template: ``<hideShow></hideShow>``
};
});
New Directive for hideShow is written as follows
angular.module("app").directive("hideShow", function () {
return {
tempateUrl: "/templates/HideShow.html"
};
});
It's not working. I understand I am missing something here. I could not find out. Appreciate help
Working code:
var app = angular.module('plunker', []);
app.controller('productController', function($scope) {
});
app.directive("hideShow", function() {
return {
templateUrl: "hideshow.html"
};
});
app.directive("tmHtml", function() {
return {
transclude: false,
scope: {},
controller: "productController",
template: "<hide-show></hide-show>"
};
});
the problem is with the spelling of templateUrl in your hideShow directive.
Demo : http://plnkr.co/edit/TaznOeNQ7dM9lyFgqwCL?p=preview
Try define your controller with controllerAs:
angular.module("app").directive("tmHtml", function () {
return {
transclude: false,
scope: {
},
controllerAs: "productController",
templateUrl: "/templates/HideShow.html"
};
});
angular.module("app").directive("tmHtml", function () {
return {
transclude: false,
scope: {
},
controller: "productController",
template: ``<hideShow></hideShow>``
};
});
must be replaced by
angular.module("app").directive("tmHtml", function () {
return {
transclude: false,
scope: {
},
controller: "productController",
template: "<hide-show></hide-show>"
};
});
under the attribute template, you add Html. So, you still have to use snake-case there, like in your Html files
Your first directive may have an eventually scoped attribute that you observe.
Then it may wrap the second directive. If needed, your directives may communicates as parents and children.
I started to learn Angular not so long time ago and I'm trying to understand scope, binding and etc.
I have an order details controller:
orderApp.controller('OrderDetailsController', ['$http','$routeParams','$scope','config', function($http, $routeParams, $scope, config){
var orderCtrl = this;
orderCtrl.orderId = $routeParams.orderId;
orderCtrl.order = {};
orderCtrl.editingView = false;
...
}]);
On order details page I want to output all information about selected order. Also we need to give user ability to edit order. Information about editing mode is stored in orderCtrl.editingView.
I decided to create custom directive. If edit mode is off - display text, otherwise display input.
orderApp.directive('editableText', function(){
return {
restrict: 'E',
scope: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
This is template:
<div class="col-xs-8" ng-if="!editMode">{{property}}</div>
<div class="col-xs-8" ng-if="editMode"><input type="text" class="form-control" ng-model="property"></div>
And this is how I use directive in html files:
<editable-text property="orderCtrl.order.coid" edit-mode="orderCtrl.editingView"></editable-text>
Text and input are switching when edit mode is on/off. Problem is that orderCtrl.order.coid property is not updated when I change it in input.
Before edit property looks like:
Turn on edit mode and change value:
Turn off edit mode and we see old value:
Do I need to synchronise controller values and directive scope? I thought that with 2-ways binding it should happen automatically. Probably there is any other way to write this functionality? Will appreciate any help.
UPD
Directive code:
orderApp.directive('editableText', function(){
return {
restrict: 'E',
bindToController: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
Directive template:
<div class="col-xs-8" ng-if="!orderCtrl.editMode">{{orderCtrl.property}}</div>
<div class="col-xs-8" ng-if="orderCtrl.editMode"><input type="text" class="form-control" ng-model="orderCtrl.property"/></div>
Directive usage:
<editable-text property="orderCtrl.order.coid" edit-mode="orderCtrl.editingView"></editable-text>
I'm not sure that we really need to pass edit-mode attribute.
You should use bindToController: { ..scope properties.. } option here inside your directive to make sure that isolated scope properties should get bounded to controller this context.
Directive
orderApp.directive('editableText', function(){
return {
restrict: 'E',
bindToController: {
property: '=property',
editMode: '=editMode'
},
controller: 'OrderDetailsController',
controllerAs: 'orderCtrl',
templateUrl: '/pages/editable-text.html'
}
});
Template
<div class="col-xs-8" ng-if="!orderCtrl.editMode">
{{orderCtrl.property}}
</div>
<div class="col-xs-8" ng-if="orderCtrl.editMode">
<input type="text" class="form-control" ng-model="orderCtrl.property"/>
</div>
Note:- this above bindToController: { ..scope properties.. } option available for angular 1.4+ versions.
For Angular 1.3 > version & 1.4 > version you should use former way of doing it by having bindingToController: true to bind scope variable to controller context & do keep the varaibles inside scope: { ...props... }
scope: {
property: '=property',
editMode: '=editMode'
},
bindToController: true
It seems like I get confused by isolated scopes in directives and hope you can help me out.
I tried to wrap a piece of code (which contains some custom directives) into a new directive to reduce code duplication. Obviously I needed to give some attributes like ng-model into my new directive as a parameter to make the directive reusable. ng-model does not like expressions though (I tried ng-model="{{myVariableWhichContainsDesiredNgModelString}}" at first) and thus I ended up at this article: AngularJS - Create a directive that uses ng-model.
While the accepted answer seems to work for a simple setup, I edited the plunker from the accepted answer to test out if it would work with nested directives as well: (in my app I need to wrap directives from a third party-library which I can not edit) Plunker. In my code each directive seems to generate its own scope and two-way-databinding by using = in the scope definition does not seem to work out as desired.
EDIT: Since it was not clear what i am asking I edited the Plunker above and will rephrase the question: In the Plunker I have three input-fields which are supposed to bind to the same model-value. This works initially, but as soon as I edit the third input-field it generates its own variable in its isolated scope instead of updating the initial value. Obviously the third input field refers to the new variable from that point on. How can I avoid that behaviour and keep the input linked to $scope.model.name?
Observation: removing the isolated-scope-directive from the template makes everything work as expected...
template: '<div><my-input ng-model="myDirectiveVar"></my-input></div>',
instead of
template: '<div><my-isolated-scope-directive><my-input ng-model="myDirectiveVar"></my-input></my-isolated-scope-directive></div>',
Plunker
HTML:
<!-- this binds to the model which i would like all my inputs to bind to.-->
<input ng-model="name">
<!-- Example 1: This seems to generate a new modelvalue in the isolated-scope directive. Can I avoid this without modifying that directive?-->
<my-isolated-scope-directive><my-input ng-model="name"></my-input></my-isolated-scope-directive>
<!-- Example 2: This is what i would like my code to look like in the end: One directive which uses the code-snippet of Example 1 as template and passes some parameters into that template.-->
<my-wrapper-directive my-directive-var="name"></my-wrapper-directive>
Directives:
my-input contains a modified input-field:
app.directive('myInput', function() {
return {
restrict: 'E',
replace: true,
require: 'ngModel',
template: '<input class="some">',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
})
my-isolated-scope-directive is a placeholder-directive with its own isolated scope to simulate the behaviour for nested directives:
.directive('myIsolatedScopeDirective', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {
something: '='
},
template: '<div ng-transclude></div>',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
})
my-wrapper-directive encapsulates both previous directives and accepts a parameter which should be used as ng-model value of the input field:
.directive('myWrapperDirective', function() {
return {
restrict: 'E',
transclude: false,
replace: true,
scope: {
myDirectiveVar: '='
},
template: '<div><my-isolated-scope-directive><my-input ng-model="myDirectiveVar"></my-input></my-isolated-scope-directive></div>',
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
}
};
});
Any suggestions and hints on what I am missing are appreciated. Can I maybe somehow link ng-model to a service-instance without making my directive dependant on that service?
I wouldn't do it like this as it is old notation using scopes... I would use controllerAs and bindToController
script:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function() {
this.model = { name: 'World' };
this.name = "Felipe";
});
app.directive('myInput', function() {
return {
restrict: 'E',
replace: true,
// controllerAs: 'app',
require: 'ngModel',
template: '<input class="some">',
controller: function(){
}
};
})
.directive('myIsolatedScopeDirective', function() {
return {
restrict: 'E',
transclude: true,
controllerAs: 'app1',
bindToController: {
something: '='
},
template: '<div ng-transclude></div>',
controller: function(){
}
};
})
.directive('myWrapperDirective', function() {
return {
restrict: 'E',
transclude: false,
controllerAs: 'app2',
bindToController: {
myDirectiveVar: '='
},
template: '<div><my-isolated-scope-directive>'+
'<my-input ng-model="app2.myDirectiveVar"></my-input>'+
'</my-isolated-scope-directive></div>',
controller: function(){
}
};
});
index:
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl as main">
This scope value
<input ng-model="main.model.name">
<my-isolated-scope-directive>
<my-input ng-model="main.model.name"></my-input>
</my-isolated-scope-directive>
<my-wrapper-directive my-directive-var="main.model.name">
</my-wrapper-directive>
</body>
</html>
See the plunker:
http://plnkr.co/edit/VD0wXO1jivQc3JvfQFTh?p=preview
UPDATE
yes, good point, so if you want to use controllerAs, you need angular 1.2 as minimum, for bindToController you need angular 1.3
I got a problem by passing a function to a directive ( familiar to this post: AngularJS - pass function to directive but i can´t get it working)
Here is my Code:
Directive:
.directive('testdirective', function(){
return{
restrict: 'E',
scope: {
onClick: '&'
},
controller: 'TestController',
controllerAs: 'tc',
bindToController: true,
template: '<div><button ng-click="onClick()">What UP</button></div>',
replace: true
}
})
Controller:
TestController.$inject = ["$scope"];
function TestController($scope) {
$scope.testFunction = function(){
alert("I´m the alert of the TestContoller");
};
$scope.test = 'test';
}
HTML:
<div>
<testdirective on-click="testFunction()"></testdirective>
</div>
What I want sounds very simple, I just want to pass the function to the directive and execute it with the ng-click on the button.
For me my code looks exactly like this fiddle
but mine is not working :/
Would be awesome if someone got some hints for me.
EDIT
My directive will need his own controller !
Later the function to be passed in will come from another controller !!!
The fiddle is not the same as your code.
You have set the controller of your directive to be "TestController". I assume what you wanted to do was:
.directive('testdirective', function(){
return{
restrict: 'E',
scope: {
onClick: '&'
},
template: '<div><button ng-click="onClick()">What UP</button></div>',
replace: true
}
});
and in your HTML,
<div ng-controller="TestController">
<testdirective on-click="testFunction()"></testdirective>
</div>
EDIT: Based on OP's comment
app.directive('testdirective', function(){
return{
restrict: 'E',
scope: {
onClick: '&'
},
template: '<div><button ng-click="tc.onClick()">What UP</button></div>',
replace: true,
controller: 'TestController',
controllerAs: 'tc',
bindToController: true
}
});
app.controller('TestController', function ($scope) {
console.log($scope);
}) ;
app.controller('AnotherController', function ($scope) {
$scope.testFunction = function(){
alert("I´m the alert of the TestContoller");
};
$scope.test = 'test';
});
And, your HTML
<div ng-controller="AnotherController">
<testdirective on-click="testFunction()"></testdirective>
</div>
You are telling the directive to bindToController. So within the directive's template, onClick is bound to the controller and not the scope. So, you access the onclick via the controller as tc.onClick() in the directive's template.
You may want to pass a method as a reference:
1.Pass the function as a reference and not a call:
<div>
<testdirective on-click="testFunction"></testdirective>
</div>
2.Update the directive:
.directive('testdirective', function(){
return{
restrict: 'E',
scope: {
onClick: '='
},
template: '<div><button ng-click="onClick()">What UP</button></div>',
replace: true
}
});
JSFIDDLE.
Well, in your testdirective,you defined controller TestController.
The testFunction() that you try to calling via onClick directive scope parameter is defined in controller TestController which is directive controller.
So, rather than calling via onClick you can call directly like
template: '<div><button ng-click="testFunction()">What UP</button></div>'.
Its very confusing ,you defining controller in directive and again referring it's one function via same directive's scope parameter which look like recursive.
If you want to call via directive scope parameter then you should do belowe changes.
for e.g.
JS :
<div ng-controller="TestController" ng-app="dr">
<testdirective on-click="testFunction()"></testdirective>
</div>
app.directive('testdirective', function() {
return{
restrict: 'E',
scope: {
onClick: '&'
},
template: '<div><button ng-click="onClick()">What UP</button></div>',
replace: true
}
});
Directivie:
.directive('testdirective', function(){
return{
restrict: 'E',
scope: {
onClick: '=onClick'
},
controller: 'TestController',
controllerAs: 'tc',
bindToController: true,
template: '<div><button ng-click="onClick()">What UP</button></div>',
replace: true
}
})
use '=' instead of '&' so you can fetch the html function in your directive. and you can simply pass onClick parameter through HTML
HTML:
<div>
<testdirective on-click="testFunction()"></testdirective>
</div>
So, I'm not sure what it is I'm asking, but I want to achieve this:
Index.html:
<div ng-view>
</div>
<script>
angular.module('myApp', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { controller: "HomeController", templateUrl: '/Partials/Home/Dashboard.html' });
$routeProvider.otherwise({ redirectTo: '/' });
}]);
</script>
Home/Dashboard.html:
<h2 class="page-header">{{welcome}}</h2>
<!-- Insert my reusable component here -->
My reusable component would reside in MyComponent/Component.html and have the controller myApp.component.controller associated with it.
So effectively I want to drop in the resuable component into the page and bind it to my controller. So I got as far as having this:
.directive('MyComponent', function() {
return {
restrict: 'E',
scope: {
},
templateUrl: '/MyComponent/Component.html'
};
});
So how do I now bind my controller to it? Do I do this:
.directive('MyComponent', function() {
return {
restrict: 'E',
resolve: function () {
return /* resolve myApp.component.controller */;
},
templateUrl: '/MyComponent/Component.html'
};
});
When a directive requires a controller, it receives that controller as the fourth argument of its
link function.
.directive('MyComponent', function() {
return {
restrict: 'E',
controller: 'MyController', // attach it.
require: ['MyController','^ngModel'], // required in link function
templateUrl: '/MyComponent/Component.html',
link: function(scope, element, attrs, controllers) {
var MyController = controllers[0];
var ngModelCtlr = controllers[1];
///...
}
};
});
The ^ prefix means that this directive searches for the controller on its parents (without the ^ prefix, the directive would look for the controller on just its own element).
Best Practice: use controller when you want to expose an API to other directives. Otherwise use link.
You can assign controllers to directives:
.directive('MyComponent', function() {
return {
restrict: 'E',
controller : 'HomeController',
templateUrl: '/MyComponent/Component.html'
};
});
So I just want to clarify a few things up here.
/MyComponent/Component.html:
<h2>{{welcome}}</h2>
/mycomponent.directive.js:
.directive('MyComponent', function() {
return {
restrict: 'E',
controller : 'ComponentController',
templateUrl: '/MyComponent/Component.html'
};
});
the above bound like this in index.html:
<h2>{{welcome}}</h2> <!-- this is from ng-controller = HomeController -->
<my-component></my-component> <!-- this is scoping from ng-controller = ComponentController -->
This generates the result
<h2>Hello from MyComponent</h2>
<h2>Hello from MyComponent</h2>
It appears that the ComponentController has taken over the entire scope. I then changed the directive to this:
.directive('MyComponent', function() {
return {
restrict: 'E',
// controller : 'ComponentController',
templateUrl: '/MyComponent/Component.html'
};
});
And changed the index.html to this:
<h2>{{welcome}}</h2> <!-- this is from ng-controller = HomeController -->
<div ng-controller="ComponentController">
<my-component></my-component> <!-- this is scoping from ng-controller = ComponentController -->
</div>
This gave the correct output:
<h2>Welcome from HomeController</h2>
<h2>Welcome from ComponentController</h2>
Then I changed the directive again to this:
.directive('MyComponent', function() {
return {
restrict: 'A', // <----- note this small change, restrict to attributes
// controller : 'ComponentController',
templateUrl: '/MyComponent/Component.html'
};
});
I changed index.html to this:
<h2>{{welcome}}</h2>
<div ng-controller="ComponentController" my-component></div>
And this also produced the desired output, just in less lines of code.
So I just hope this clarifies directives to people a bit better. I put this on for completeness and the steps that I took to achieve this. Obviously I had some help from the other answers which pointed me in the right direction.