how to call parent method from directive? - angularjs

I have directive like this
.directive('myModal', function() {
return {
restrict: 'A',
replace: true,
scope: {
myModalId: '#'
},
template: '<div id="{{myModalId}}" class="modal">' +
'<div ng-click="parentMethod()" class="modal-dialog">' +
'...' +
'</div>' +
'</div>'
}
});
js code
function ParentController($scope) {
$scope.parentMethod = function() {
alert('clicked');
}
}

There are several ways you can achieve this. I would do it using attribute binding which allows you to execute an expression in the context of the parent scope. This is how you can use it:
<div my-modal my-modal-id="12312" on-click="parentMethod()"></div>
Then in directive you define scope like this:
scope: {
myModalId: '#',
onClick: '&'
}
and in directive template:
<div ng-click="onClick()" class="modal-dialog">
Demo: http://plnkr.co/edit/UDnJGRVqXqbCGSFEAMMA?p=preview
Another way (not recommended), you can directly refer the parent scope from isolated directive scope:
<div ng-click="$parent.parentMethod()" class="modal-dialog">

<body ng-app="myApp">
<div ng-controller="myCtrl">
<direct movied="movie" call-home="callFromDirect"></direct>
</div>
<script>
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope, $rootScope) {
$scope.callFromDirect = function (param) {
console.log('got call to direct ' + param);
}
});
myApp.directive("direct", function () {
return {
restrict: 'E',
scope: {
callHome: '='
},
template: "<input type=\"button\" value=\"click\" ng-click=\"callHome('movie')\" />"
}
});
</script>

Related

How do I dynamically change class in Angular direcitves?

I'm trying to write a custom directive to replace similar buttons on my page. But when I move ng-class into directive's template, it's not working anymore. Is it wrong to include ng-class within custom directive? Should I use addClass and removeClass in link function instead?
html:
<dt-button ngclass="{'active-button': selectedRows.length >=1}" text="tablebuttons.delete" icon="v-delete" ng-click="deleteDialog()"></dt-button>
directive
.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
ngclass: '='
},
link: function(scope, ielem, iattrs) {
},
template:
'<button ng-class="{{ngclass}}">' +
'<span class="{{icon}}"></span>' +
'<p translate="{{text}}">' +
'</p>' +
'</button>'
}
})
try use this. change class to ng-class in your template.
you pass a model to directive for text in view while it is not 2 way data binding.
template:
'<button class="active-button" ng-class="{{ngclass}}">' +
'<span class="{{icon}}"></span>' +
'<p translate="{{text}}">' +
'</p>' +
'</button>'
// Code goes here
var app = angular
.module('MyApp', [])
.controller('Main', ['$scope',
function($scope) {
var vm = this;
vm.selectedRows = 4;
vm.deleteDialog = function() {
console.log(vm.selectedRows);
vm.selectedRows = 0;
}
}
])
.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
ngclass: '='
},
controller: "Main as ctrl",
link: function(scope, ielem, iattrs) {
},
template: '<button ng-class="ngclass" >' +
'<p>{{text}}</p>' +
'</button>'
}
});
.active-button {
background-color: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="main-content" ng-app="MyApp" ng-controller="Main as ctrl">
<div>
<dt-button ngclass="{'active-button':ctrl.selectedRows >=1}" ng-click="ctrl.deleteDialog()" text="delete"></dt-button>
</div>
</div>
I think nothing wrong with your approach to put ng-class at template of directive. I have tried to reproduce your code snippet at this plunk it is give the correct class name active-button which i defined at style.css with background color blue. But because i don't know much about expression selectedRows.length >=1 on your ngclass attribute, i make it just to true value which will always give active-button class to the element. When you change it to false, it will remove the active-button class.
My guess is seem something wrong with your expression selectedRows.length >=1. At following element declaration :
<dt-button ngclass="{'active-button': selectedRows.length >=1}" text="tablebuttons.delete" icon="v-delete" ng-click="deleteDialog()"></dt-button>
Maybe you can check by bind those expression return value to the element with double curly brace or any other way.
Small correction for your code, you may need to put semicolon ( ; ) at the end of return keyword inside .directive().
Try This
jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.selectedRows = [0];
$scope.tablebuttons = {delete:"Delete"};
$scope.deleteDialog = function(){
$scope.selectedRows = [];
}
});
jimApp.directive('dtButton', function() {
return {
restrict: 'E',
scope: {
icon: '#',
text: '#',
myClass: '#'
},
link: function(scope, ielem, iattrs) {
console.log(scope.myClass);
},
template:
'<button class="{{myClass}}">' +
'<span class="{{icon}}"></span>' +
'{{text}}' +
'</button>'
}
})
.active-button{
background:red;
}
.inactive-button{
background:#ccc;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
<dt-button my-class="{{selectedRows.length?'active-button':'inactive-button'}}" text="{{tablebuttons.delete}}" icon="v-delete" ng-click="deleteDialog()"></dt-button>
</div>

AngularJS: ng-click not working in custom input directive

I'm new in AngularJS, currently I'm working on custom input directive with a label.
I refer to one article in stack overflow but encountered problem.
The running code is on fiddle:
http://jsfiddle.net/luneyq/bpN9b/18/
Core code is as below:
<!doctype html>
<html>
<head>
<script src="../common/angular.js"></script>
</head>
<body>
<div ng-app="myApp">
<div ng-controller="MainController">
<my-input type="number" name="valueNumber1" ng-model="valueNumber1" label="Age" ng-change="change()" ng-click="click()"></my-input>
<div id="result">a</div>
</div>
</div>
<script>
var app = angular.module("myApp", []);
app.controller('MainController', function($scope, $window){
$scope.valueNumber1 = 10;
$scope.change = function() {
document.getElementById("result").innerHTML = 'change:' + $scope.valueNumber1;
};
$scope.click = function() {
document.getElementById("result").innerHTML = 'click:' + $scope.valueNumber1;
};
});
app.directive('myInput', function() {
return {
require: '^ngModel',
restrict: 'EA',
scope: {
ngModel: '=',
name: '#name',
label: '#label'
},
replace: true,
transclude: true,
//priority: 10,
template: '<div>' +
'<label for="{{ name }}">{{label}}</label>' +
'<input id="{{ name }}" ng-model="ngModel" />' +
'</div>',
compile: function(tElement, tAttrs, transclude) {
var tInput = tElement.find('input');
// Move the attributed given to 'custom-input' to the real input field
angular.forEach(tAttrs, function(value, key) {
if (key.charAt(0) == '$' || key == "class")
return;
tInput.attr(key, value);
tInput.parent().removeAttr(key);
});
tElement.removeAttr('ng-model');
return;
}
};
})
</script>
</body>
</html>
My problems are:
1. ng-click and ng-change not working on the input
2. ng-model="ngModel" on input element, why ngModel is used here? if I change ngModel to aaa, the initial value for the input disappeared
3. attributes copy in compile function removed the dash(-) signal, ng-click is copied as ngClick.
I'm not sure is this the cause of this problem.
Can anyone help on this problem?
Your problem was click and change methods not in your isolate scope - BUT you are not making use of Angular's two way binding here - instead simply use ng-bind on your result div - here's a fiddle http://jsfiddle.net/bpN9b/20/
var app = angular.module("myApp", []);
app.controller('MainController', function($scope, $window){
$scope.valueNumber1 = 10;
});
app.directive('myInput', function() {
return {
require: '^ngModel',
restrict: 'EA',
scope: {
ngModel: '=',
name: '#name',
label: '#label'
},
replace: true,
priority: 10,
template: '<div class="cbay-input-div">' +
'<label for="{{ name }}">{{label}}</label>' +
'<input id="{{ name }}" ng-model="ngModel" />' +
'</div>',
compile: function(tElement, tAttrs, transclude) {
console.log(tAttrs);
var tInput = tElement.find('input');
// Move the attributed given to 'custom-input' to the real input field
angular.forEach(tAttrs, function(value, key) {
//console.log(key + " = " + value);
if (key.charAt(0) == '$' || key == "class")
return;
tInput.attr(key, value);
tInput.parent().removeAttr(key);
});
tElement.removeAttr('ng-model');
return;
}
};
});
here's the template
<div ng-app="myApp">
<div ng-controller="MainController">
<my-input type="number" name="valueNumber1" ng-model="valueNumber1" label="Age" ng-change="change(this)" ng-click="click(this)"></my-input>
<div id="result" ng-bind="valueNumber1">a</div>
</div>
</div>

ng-switch and manually $compile

I tried to make recursive directive like here
Angularjs: understanding a recursive directive
ng-switch options are all commented, ng-if works fine.
I don't know what is a problem.
Just inspect output of ng-switch
var module = angular.module('myapp', []);
module.controller("TreeCtrl", function($scope) {
$scope.controls = [
{
"label":"Checkbox",
"type":"checkbox"
},
{
"label":"input",
"type":"input"
},
{
"label":"Some group",
"type":"Group",
"controls":[
{
"label":"subcontrol",
"type":"input"
}
]
}
];
});
module.directive("tree", function($compile) {
return {
restrict: "E",
scope: {controls: '='},
template:
'<div ng-switch="control.type">' +
'<label>{{control.label}}</label>'+
'<div ng-switch-when="input"><input type="text" name="ctrl1"></input></div>'+
'<div ng-switch-when="checkbox"><input type="checkbox" "name="ctr2"></input></div>'+
'<div ng-repeat="subcontrol in control.controls">' +
'<tree control="subcontrol"></tree>' +
'</div>' +
'</div>',
compile: function(tElement, tAttr, transclude) {
//We are removing the contents/innerHTML from the element we are going to be applying the
//directive to and saving it to adding it below to the $compile call as the template
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents, transclude);
}
compiledContents(scope, function(clone, scope) {
iElement.append(clone);
});
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp">
<div ng-controller="TreeCtrl">
<div ng-repeat='control in controls'>
<tree control="control"></tree>
</div>
</div>
</div>

AngularJS How to parse html into directive template

How i can parse html in directive template???
app.directive('user', function(){
return {
restrict: 'E',
scope: false,
template: '<div class="clearfix buttons-container" ng-bind-html-unsafe="current_text"></div>'
}
});
//in controller
$scope.current_text = 'Hello <strong>'+current_username+'</strong> !!!'
use $sce.trustAsHtml. this method will create trusted HTML object for ng-bind-html directive.
$sce.trustAsHtml Docs
See below example
var app = angular.module('app', []);
app.controller('ctrl', function($scope, $sce) {
var current_username = "User";
var html = 'Hello <strong>' + current_username + '</strong> !!!';
$scope.current_text = $sce.trustAsHtml(html);
});
app.directive('user', function() {
return {
restrict: 'E',
scope: false,
template: '<div class="clearfix buttons-container" ng-bind-html="current_text"></div>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl">
<user></user>
</div>
</div>
you can do this using isolate-scope
in this way.
app.directive('user', function(){
return {
restrict: 'E',
scope: {
current_text : '#current_text'
},
template: '<div class="clearfix buttons-container" ng-bind-html-unsafe="current_text"></div>'
}
});
//in controller
$scope.currenText = 'Hello <strong>'+current_username+'</strong> !!!'
<div current_text="currenText" > </div> // your directive included here

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