Two way binding in custom directive in AngularJS - angularjs

I'm trying to implement two way binding for custom directive in angularJs. Somehow its not working.
html file
<div ng-app='myApp'>Outside directive
<input type='text' ng-model='outAttr'>{{outAttr}}</br>
<div my-directive some-attr='outAttr'></div>
</div>
js file
var myApp = angular.module('myApp', []);
myApp.directive('myDirective', function () {
return {
restrict: 'A,
replace: true,
scope: {
inAttr: '=someAttr';
},
template: "<div><input type='text' ng-model='inAttr'>\
{{inAttr}}</div>"
}
})
Somehow its not working. here is JSFiddle link. Can someone help me pointing out my mistake. Thanks.

Few syntax errors. The logic of your code is OK - jsFiddle
var myApp = angular.module('myApp', []);
myApp.directive('myDirective', function() {
return {
restrict: 'A', // missing '
replace: true,
scope: {
inAttr: '=someAttr' // no ;
},
template: '<div><input type="text" ng-model="inAttr">{{inAttr}}</div>' // no break
};
});

Related

Calling a method within a controller that's within a custom directive

Been trying to figure this out for too long now. Maybe someone can shed some light:
Am experimenting with custom directives and as an exercise I'm trying to create a method within the custom directive's controller that can be called from a simple button within the view. But the method isn't being called, even though I can see the method (using console) as a property within isolated scope object. Any ideas please?
HTML:
<my-dir>
<p>My dir content</p>
<p><button ng-click="hideMe()">Hide element with isolated scope</button></p>
</my-dir>
JS:
var app = angular.module('myApp', []);
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
controller: ['$scope', function ($scope) {
$scope.hideMe = function(){
console.log('hideMe called');
};
}]
};
})
You have to declare your template inside the directive using template: property or inside an external .html file using templateUrl:"path/to/template.html"
Example using template :
var app = angular.module('myApp', []);
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
template : '<p>My dir content</p><p><button ng-click="hideMe()">Hide me</button></p>',
controller: ['$scope', function ($scope) {
$scope.hideMe = function(){
console.log('hideMe called');
};
}]
};
})
Example using templateUrl :
var app = angular.module('myApp', []);
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
templateUrl : 'my-dir.tpls.html',
controller: ['$scope', function ($scope) {
$scope.hideMe = function(){
console.log('hideMe called');
};
}]
};
})
Template : my-dir.tpls.html
<p>My dir content</p>
<p><button ng-click="hideMe()">Hide me</button></p>
HTML:
<my-dir></my-dir>
You can try this,
Directive:
app.directive('myDir', function() {
return {
restrict: 'EA',
scope: {},
link: function($scope, element, attrs) {
$scope.hideMe = function() {
alert('hideMe called');
}
}
}
});
HTML:
<div ng-controller="MyCtrl">
<my-dir>
<p>My dir content</p>
<p>
<button ng-click="hideMe()">Hide element with isolated scope</button>
</p>
</my-dir>
</div>
DEMO

Why isn't this field binding in my directive?

I have a directive with 2-way binding on the dataSourceModel scope variable, but for some reason, it is showing as undefined in the directive. Am I doing something wrong here?
Plunker: http://plnkr.co/edit/LxWMbY9qtDSBUPWNqWV7?p=preview
Code:
Html:
<div ng-controller='TestCtrl'>
<test-directive
selected-id='selectedId'
data-source-model='workOrderItems'> <!-- This does not work -->
</test-directive>
{{workOrderItems}} <!-- this works -->
</div>
Script:
var app = angular.module("testApp", []);
app.controller('TestCtrl', ['$scope', function ($scope) {
$scope.workOrderItems = 'abcd';
$scope.selectedId = '123';
}]);
app.directive('testDirective',function () {
return {
restrict: 'E',
scope: {
selectedId: '=',
dataSourceModel: '='
},
replace: true,
template: "<div></div>",
link: function (scope, element, attrs) {
console.log(scope.selectedId, scope.dataSourceModel);
}
}
});
data- is prefix for custom HTML5 attributes, so the data-source-model='workOrderItems' is translated to just sourceModel in your directive.
Try renaming your directive attribute to something that doesn't start with data (or reference it in HTML as data-data-source-model) and it should work.

AngularJS controller inside directive with binding

I have this simple code of my directive:
app.directive('ngModal', function ($parse) {
return {
restrict: 'E',
template: document.getElementById('ng-modal').innerHTML,
replace: true,
controller : "#",
name:"controllerName",
}
})
<ng-modal controller-name="ModalCtrl"></ng-modal>
And this is my controller:
app.controller('ModalCtrl', ['$scope', function ($scope) {
$scope.model = 'default text'
}])
<div ng-controller="ModalCtrl">
<input type="text" ng-model="model">
</div>
I want, that model field inside my Directive will updated automatically. But I see "default text" always inside directive and changed inside controller. How can I bind it?
You have to add a service to keep information between controllers. Controllers are always created per "view" so your ng-modal and div have different controllers in use, this is why model data is not updated between them.
Fast example:
app.service('sharedData', function() {
var sharedData = {
field: 'default text'
};
return sharedData;
});
app.directive('ngModal', function () {
return {
restrict: 'E',
template: '',
replace: true,
controller : "#",
name:"controllerName",
}
});
app.controller('ModalCtrl', ['$scope', 'sharedData', function ($scope, sharedData) {
$scope.model = sharedData;
}]);
<ng-modal controller-name="ModalCtrl">{{model.field}}</ng-modal>
<div ng-controller="ModalCtrl">
<input type="text" ng-model="model.field">
</div>

Angularjs Directive scope

I'm trying to create a directive and am running into some problems with the scope object. In creating a plunkr to display what is happening I noticed that I'm close, but not quite sure what I'm misunderstanding.
If i have a directive that looks like this:
angular.module('inputExample', [])
.directive('test',
function() {
return{
restrict: 'E',
link: function(scope){
scope.testInput = 1;
scope.$watch(
'testInput',
function(newTestInput){
console.log(newTestInput);
},
false);
}
}
});
And utilize the directive in the view like this:
<test>
<input data-ng-model="testInput" />
<div>
{{testInput}}
</div>
the div seems to be updated correctly and there is a message logged to the console as I would expect.
If I create the directive like this though:
angular.module('inputExample', [])
.directive('example',
function() {
return{
scope: {
'settings': '#'
},
restrict: 'E',
link: function(scope){
scope.exampleInput = 1;
scope.$watch(
'exampleInput',
function(newTestInput){
console.log(newTestInput);
},
false);
}
}
});
</test>
and use it in a view like this:
<example>
<input data-ng-model="exampleInput" />
<div>
{{exampleInput}}
</div>
</example>
then the div seems to get updated, but there is no message logged to the console. It seems as though the $watch isn't working. Can someone please tell me what I'm doing wrong?
Here is the plunkr.
Thanks in advance for any help that can be offered.
The problem with the example is that you're putting your HTML inside your directive tags. They should be in your template.
This code works
angular.module('inputExample', [])
.directive('example',
function() {
return{
scope: {
'settings': '#'
},
restrict: 'E',
template: '<input data-ng-model="exampleInput" /><div>{{exampleInput}}</div>',
link: function(scope){
scope.exampleInput = 1;
scope.$watch(
'exampleInput',
function(newTestInput){
console.log(newTestInput);
},
false);
}
}
});
With the view as just
<example></example>

AngularJS - Different template in directive

I have a form based on twitter bootstrap, each field have it's own configuration
// controller (the template shows this in ng-repeat
$scope.fields = [{name:"f1", label:"Field 1", with_button: false},
{name:"f2", label:"Field 2", with_button: true}]
I'm trying to make a "conditional directive" that customize the template according to "field.with_button"
// Without button
<div class="controls">
<input type="text" id="i_{{field.name}}">
</div>
// With button
<div class="controls">
<div class="input-append">
<input type="text" id="i_{{field.name}}">
<span class="add-on">bt</span>
</div>
</div>
I searched a lot and didn't find any solution, I tried to create only one div and put contents inside with a compiler function but it didn't parse, and if I call $apply it crashes.
How could I make this directive?
wrong My last try:
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls">{{innerContent}}</div>',
controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
$scope.$eval('$scope.innerContent = \'<input type="text" id="input_{{field.name}}" placeholder="{{field.name}}" class="input-xlarge">\'');
}]
};
});
//<ss-field field="{{field}}"></ss-field>
You can use the $http and $compile services to do what you're after.
http://plnkr.co/edit/Xt9khe?p=preview
This plnkr should demostrate what needs to be done, but basically:
Use $http to load the template depending on the condition.
Compile the loaded template against the current scope with $compile.
angular.module('mymodule',[]).directive('ssField', ['$http', '$compile', function($http, $compile) {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template: '<div class="controls"></div>',
link: function(scope, element, attrs) {
var template;
var withButtonTmpl = 'with_button.html';
var withoutButtonTmpl = 'without_button.html';
if (scope.field.with_button) {
$http.get(withButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
} else {
$http.get(withoutButtonTmpl).then(function(tmpl) {
template = $compile(tmpl.data)(scope);
element.append(template);
});
}
}
};
}]);
You can change the directive to be more robust so the URLs aren't directly embedded in the directive for re-usability, etc., but the concept should be similar.
Just to further expand on Cuing Vo's answer here is something similar to what I use(without using external partials and additional $http calls):
http://jsfiddle.net/LvUdQ/
myApp.directive('myDirective',['$compile', function($compile) {
return {
restrict: 'E',
template: '<hr/>',
link: function (scope, element, attrs, ngModelCtrl) {
var template = {
'templ1':'<div>Template 1</div>',
'templ2':'<div>Template 2</div>',
'default':'<div>Template Default</div>'
};
var templateObj;
if(attrs.templateName){
templateObj = $compile(template[attrs.templateName])(scope);
}else{
templateObj = $compile(template['default'])(scope);
}
element.append(templateObj);
}
};
}]);
However Im not quite sure its by the bible from performance perspective.
In AngularJS, directly manipulate the DOM must only be a last resort solution. Here, you can simply use the ngSwitch directive :
angular.module('mymodule',[]).directive('ssField', function() {
return {
transclude:false,
scope: {
field: '='
},
restrict: 'E',
replace:true,
template:
'<div class="controls" data-ng-switch="field.with_button">' +
'<input type="text" id="i_{{field.name}}" data-ng-switch-when="false">' +
'<div class="input-append" data-ng-switch-default>' +
'<input type="text" id="i_{{field.name}}">' +
'<span class="add-on">bt</span>' +
'</div>' +
'</div>',
};
});

Resources