AngularJS How to add / remove directive dynamically in page when button click - angularjs

suppose i have two button and when we click on button 1 then show function call in controller and same way when we click on button 2 then hide function call in controller.
from show function how could i dynamically load and add directive in page and as well as how could i hide or remove directive from page when hide function will be called.
hence i am new in angular so not sure the below code will add directive at run time if i call it from show function ?
$('body').append($compile("<my-angular-directive />")(scope));
scope.$apply();
i do not know how to remove directive from page if it exist in page from controller function. give me suggestion how to achieve this. thanks

you can use directive as class and you can load it with ng-class directive
var jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.showMyDir = true;
$scope.buttonClcik = function(){
$scope.showMyDir = !$scope.showMyDir;
};
});
jimApp.directive("customDir1", function() {
return {
restrict:"AEC",
scope:{
value:"="
},
link: function(scope, element, attrs) {
}
}
});
jimApp.directive("customDir2", function() {
return {
restrict:"C",
link: function(scope, element, attrs) {
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
<button ng-click="buttonClcik();">Click</button>
<div ng-class="{'custom-dir1': showMyDir, 'custom-dir2': !showMyDir}" value="showMyDir">Hai</div>
</div>

Use ng-if. So basically if you have a scope variable let's say $scope.showElement you can then use it like this:
In your controller:
$scope.showElement = true;
HTML:
<this-directive ng-if="showElement"></this-directive>
Originally the directive will show up but when you change the value of $scope.showElement it will be removed.
EDIT based on your comment:
Set your $scope.showElement to false and on ng-click set it to true like this:
angular.module('myApp', [])
.controller('testController', function($scope) {
$scope.showElement = false;
$scope.toggleElement = function() {
console.log('toggle element');
$scope.showElement = $scope.showElement ? false : true;
};
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
</head>
<body ng-controller="testController">
<button type="button" ng-click="toggleElement()">Show directive</button>
<div ng-if="showElement" style="width:100%;height:40px;background-color:red;">This element will be shown/removed</div>
</body>
</html>
EDIT 2:
You can also use css declarations for better performance if you don't mind that the element still stays on the page but is hidden. With css it would go like this:
angular.module('myApp', [])
.controller('testController', function($scope) {
$scope.showElement = false;
$scope.toggleElement = function() {
console.log('toggle element');
$scope.showElement = $scope.showElement ? false : true;
};
});
.hidden {
display: none;
}
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
</head>
<body ng-controller="testController">
<button type="button" ng-click="toggleElement()">Show directive</button>
<div ng-class="{'hidden' : showElement === false}" style="width:100%;height:40px;background-color:red;">This element will be shown/removed</div>
</body>
</html>

You could easily use ng-if directive on the wrapper of your custom directive
<div ng-if="showMyDir()">
<my-angular-directive />
</div>
<div ng-if="showMyDirTwo()">
<my-angular-directive-two />
</div>

Related

angularjs - access transclude html scope from hosting directive

I have a simple directive with transcluded html.
I want to be able to inject directive scope params to the transclude.
I wrote a simple example in plunker :
https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
I know in angular 4 it can be done, but I can't find a good way to do it in angularjs.
// Code goes here
var app = angular.module("app", []);
app.controller("mainCtrl", function($scope) {
$scope.users = ["tal", "oren", "orel", "shluki"];
$scope.deleteUser = (user) => {alert("trying to delete", user);}
});
app.directive('myList', function myList() {
return {
restrict: 'E',
transclude: true,
template: "<div><table><tr ng-repeat='item in collection'><td> This is inside myList - user name: {{item}} <ng-transclude></ng-transclude></td></tr></table></div>",
scope: {
collection: "="
},
replace: true
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<my-list collection="users">
<h2>This is transclude</h2>
<button ng-click="deleteUser(user)">Delete user: {{user ? user : "User name should be here"}}</button>
</my-list>
</body>
</html>
Will really appreicate some help.
plunker: https://plnkr.co/edit/jqyiQdgQxbeTrzyidZYF?p=preview
Here's a working plunker with your example.
http://plnkr.co/edit/BjSowyQdLXd0xoCZFqZ6?p=preview
The idea is to pass it as contents and not html as string. $compile is here because the link is done after ng-repeats already has transcluded its own template.
var template = '<h1>I am foo</h1>\
<div ng-repeat="item in users">\
<placeholder></placeholder>\
<hr>\
</div>';
var templateEl = angular.element(template);
transclude(scope, function(clonedContent) {
templateEl.find("placeholder").replaceWith(clonedContent);
$compile(templateEl)(scope, function(clonedTemplate) {
element.append(clonedTemplate);
});
});
If you want a proper explanation of what the problem was you should check the detailed answer here : Pass data to transcluded element
Hope this helped you out

Angular add dynamic html(directive) with object as parameter

Doesn't sound like it would be a big deal but I don't know what to google for to solve this. I want to be able to fetch an object with $http and then render that info with the help of a directive.
JS:
angular.module("test", []);
angular.module("test").directive('myTest', function() {
return {
templateUrl: 'myTest.html'
};
});
angular.module("test").controller("myCtrl", function($http, $compile){
var vm = this;
vm.name = "Viktor";
vm.country = "Sweden";
vm.origin = "controller";
vm.click = function(){
$http.get("data"+Math.floor((Math.random() * 2) + 1)+".json").success(function(data){
$("body").append($compile("<my-test></my-test>")(data));
})
}
})
Template:
<div>
<div>My name is: {{vm.name}}</div>
<div>I live in : {{vm.country}}</div>
<div>Source origin : {{vm.origin}}</div>
</div>
index.html
<!DOCTYPE html>
<html ng-app="test">
<head>
<script data-require="jquery#*" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script data-require="angular.js#*" data-semver="1.4.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller="myCtrl as vm">
<button ng-click="vm.click()">Add panel</button>
<my-test></my-test>
</body>
</html>
Plunker: http://embed.plnkr.co/YcG9ZFxuR3PYBYASjzm2/preview
I just can't edit your plnkr file.
But bindToController should help you. It should be somethind like this, I create a controller for the directive :
angular.module("test").directive('myTest', function() {
return {
restrict: 'E',
scope: {},
templateUrl: 'myTest.html',
controller: function(){},
controllerAs: 'ctrl',
bindToController: {
name: '=',
country: '=',
origin: '='}
}
});
Also I change the alias of the controller in the template :
<div>
<div>My name is: {{ctrl.name}}</div>
<div>I live in : {{ctrl.country}}</div>
<div>Source origin : {{ctrl.origin}}</div>
<input ng-model="ctrl.name"/>
</div>
Here is a working example modified on your plunkr : plunkr.
I am still stuck in the jQuery way of thinking, I guess I found out how to do it the Angular way. Just to add the newly fetched item to a list and then render the list. Not what I was originally aiming for but that is probably the prettiest way.
And if one now HAVE to add new items instead of redrawing the whole list, I guess one could send in the object as a json-string or set scope variables on the directive for each parameter in the object.
Plunker: http://plnkr.co/edit/5ElTIx?p=preview
Html:
<body ng-controller="myCtrl as vm">
<button ng-click="vm.click()">Add panel</button>
<my-test ng-repeat="panel in vm.panels" obj="panel"></my-test>
</body>
Js:
angular.module("test").controller("myCtrl", function($http, $compile){
var vm = this;
vm.name = "Viktor";
vm.country = "Sweden";
vm.origin = "controller";
vm.panels = [{
name:"Viktor2",
origin:"Controller array",
country:"Sweden"
}];
vm.click = function(){
$http.get("data"+Math.floor((Math.random() * 2) + 1)+".json").success(function(data){
vm.panels.push({
name:data.name,
origin:data.origin,
country:data.country
})
})
}
})

ng-model doesn't work when I create new DOM elements

When I create a new DOM element (out of angular), angular doesn't take it under control. Why?
Exemple:
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button onclick="changeDom();">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<div id='placeNewElement'>
</div>
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function(){
this.name = 'diamont';
this.bright = 3;
});
var changeDom = function(){
document.getElementById('placeNewElement').innerHTML = "<input type='text' ng-model='store.name'/>";
};
</script>
</body>
</html>
Here, initial DOM is created with an input that have databinding with scope.bright data model.
When you click on the button, a new element is created, with ng-model to make a databinding with scope.name.
But when new DOM element is created, angular doesn't 'managed' its element (doesn't show scope.name value, doesn't put typical angular classes (ng-valid, etc) to the element).
I searched a lot and I can't solve it :-(
I tryed to make scope.apply() to upload model, but this appears not to be the problem (because angular doesn't know this new element).
Tnaks for help! ;-)
Jordi
Use the idiomatic way of managing DOM tree (that is, don't modify it from under angular)
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button ng-click="store.showNameField = true">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<input type='text' ng-model='store.name' ng-if='store.showNameField' />
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function(){
this.name = 'diamont';
this.bright = 3;
this.showNameField = false;
});
</script>
</body>
</html>
I founded an answer by Kevin Shay at angularJs google group
The solution for the example code was:
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button ng-click="recognizeNewElements();" onclick="changeDom();">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<div id='placeNewElement'>
</div>
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function($scope, $compile){
this.name = 'diamont';
this.bright = 3;
$scope.recognizeNewElements = function(){
var placeNewElement = angular.element(document.getElementById('placeNewElement'));
$compile(placeNewElement.contents())($scope);
};
});
var changeDom = function(){
document.getElementById('placeNewElement').innerHTML = "<input type='text' ng-model='store.name'/>";
};
</script>
</body>
</html>
Thanks a lot for all you that waste time to help me!
Your approach towards handling new element is wrong. You can achieve this using angular directives and sharing the scope of the same angular controller with the directive. That would update the DOM at run time.
Please have a look at this fiddle example. In this I have added a textbox and dynamically added a new text box which shares the same scope as what was there for first text box and its coming under the scope of angular.
<section ng-app="myApp" ng-controller="MainCtrl">
<addbuttonsbutton></addbuttonsbutton>
<input type='text' ng-model='store'/>
<div id="space-for-buttons"></section>
</section>
var myApp = angular.module('myApp', []);
function MainCtrl($scope) {
$scope.count = 0;
$scope.store = "";
}
//Directive that returns an element which adds buttons on click which show an alert on click
myApp.directive("addbuttonsbutton", function(){
return {
restrict: "E",
template: "<button addbuttons>Click to add buttons</button>"
}
});
//Directive for adding buttons on click that show an alert on click
myApp.directive("addbuttons", function($compile){
return{
link: function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-buttons')).append($compile("<input type='text' ng-model='store' ng-init='scope.store'/>")(scope));
});
}
};
});
Hope this helps.
Thanks Lalit.
I paste your code with some changes for working (may be it will be useful for somebody searching this soluction).
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body>
<section ng-app="myApp" ng-controller="MainCtrl">
<addbuttonsbutton></addbuttonsbutton>
<input type='text' ng-model='store'/>
<div id="space-for-buttons"></div>
</section>
<script type="text/javascript">
var myApp = angular.module('myApp', []).controller('MainCtrl', function($scope) {
$scope.store = "something";
});
//Directive that returns an element which adds buttons on click which show an alert on click
myApp.directive("addbuttonsbutton", function(){
return {
restrict: "E",
template: "<button addbuttons>Click to add buttons</button>"
}
});
//Directive for adding buttons on click that show an alert on click
myApp.directive("addbuttons", function($compile){
return{
link: function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-buttons')).append($compile("<input type='text' ng-model='store'/>")(scope));
});
}
};
});
</script>
</body>
</html>
For me it was not :-(, because I'm looking for something to use witout changing how new DOM elements are created (may be it's not possible :-S ).

Angular js directive issue

Below is my code
I created a simple page and include a directive in it. And on ng-click in directive i want to call parent scope's method.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Directive Page</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"> </script>
</head>
<body>
<div ng-app="myapp" ng-controller="myController">
<ul-dir on-item-click="itemClick(obj)"></ul-dir>
</div>
</body>
</html>
<script>
var myapp = angular.module("myapp", []);
myapp.directive('ulDir', function() {
return {
restrict: 'E',
replace: 'true',
template: '<div id="container"><ul><li ng-repeat="content in contents">{{content.name}}</li></ul></div>',
controller: function ($scope) {
$scope.contents = [{'name':'Nishu', 'age':'20'},{'name':'Nidhi', 'age':'21'},{'name':'Kirti', 'age':'24'}];
},
scope: {
onItemClick: '&' // Pass a reference to the method
}
}
});
myapp.controller('myController', function($scope) {
$scope.itemClick = function(content){
console.log("Success : "+content);
};
});
</script>
So my console log print as "success : undefined"
So content object not passing from directive scope to parentscope.
Please help me.
I believe your call inside template should either be:
<a href="#" ng-click="onItemClick({'content':content})">
or
<a href="#" ng-click="onItemClick({'obj':content})">
Can you try. For more details see this SO post Can an angular directive pass arguments to functions in expressions specified in the directive's attributes?

how to call the directive on button click inside custom directive

Hi i am working on custom directives of angularjs. I just want to know that how to call the directive when I click the button. Please suggested me how to achieve this.
Thanks
<div ng-controller="ctrl">
<mydirc></mydirc>
<button ng-click="clickMe()">call clickMe()</button>
</div>
app.directive('mydirc', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function($scope, element, attrs) {
$scope.clickMe= function() {
alert('inside click');
}
}
}
});
The following example shows a sample custom directive which can handle click events; This is a scope-independent directive. And the appRoot module has to be defined earlier.
<div ng-controller="MyController">
<button custom-click="">Click Me</button>
</div>
appRoot.directive('customClick', function() {
return {
link: function(scope, element, attrs) {
element.click(function(){
//Do something useful
});
}
}
});
By "calling" the directive I am going to assume you mean handling the onclick event from the directive.
You can leverage the 'link' property of directives to attach scope initialization and functions like so:
http://jsbin.com/moruyoso/2
HTML
<!DOCTYPE html>
<html ng-app='app'>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div clicker></div>
</body>
</html>
JS
var app = angular.module('app', []);
app.directive('clicker', function(){
var link = function(scope){
scope.showMessage = function(){
alert('you clicked the directive!');
};
};
return{
link: link,
template: "<button ng-click='showMessage()'>Click me!</button>"
};
});
This example shows how to handle a click event for more than one button inside a directive:
Java Script: ( app.js )
angular.module('app', [])
.controller("articles.controller", function(){
var viewModel = this;
viewModel.articles =
[
{
title: "PHP",
content: "content 1",
selected: false
},
{
title: "C#",
content: "content 2",
selected: false
}
];
viewModel.addArticle = function(){
viewModel.articles.push(
{
title: "MySQL",
content: "new content",
selected: false
}
);
};
viewModel.select = function(article){
article.selected = !article.selected;
};
viewModel.getAll = function(){
console.clear();
console.log(viewModel.articles);
};
viewModel.getSelected = function(){
console.clear();
angular.forEach(viewModel.articles, function(article, key){
if(article.selected)
{
console.log(article.title);
}
});
};
})
.directive("artilceTile", function(){
return {
restrict: 'E',
scope: {
article: '='
},
link: function(scope, element, attr)
{
scope.displayTitle = function()
{
alert(scope.article.title);
},
scope.displayContent = function()
{
alert(scope.article.content);
},
scope.inverseArticleStatus = function()
{
scope.article.selected = !scope.article.selected;
}
},
template: `
<h1>{{article.title}}</h1>
<p>{{article.content}}</p>
<p ng-if="article.selected">Selected</p>
<input ng-model=article.title>
<button ng-click="displayTitle()">Dispaly Title</button>
<button ng-click="displayContent()">Display Content</button>
<button ng-click="inverseArticleStatus()" ng-if="!article.selected">Select</button>
<button ng-click="inverseArticleStatus()" ng-if="article.selected">Unselect</button>
<br><br><br><hr>
`
};
});
HTML: ( index.html )
<!DOCTYPE html>
<html ng-app="app">
<head>
<title></title>
</head>
<body>
<div ng-controller="articles.controller as articlesController">
<span ng-repeat="article in articlesController.articles">
<artilce-tile article="article"></artilce-tile>
</span>
<br><br>
<button ng-click="articlesController.addArticle()">Add New Article</button>
<button ng-click="articlesController.getSelected()">Get Selected Articles</button>
<button ng-click="articlesController.getAll()">Get All Articles</button>
</div>
<script type="text/javascript" src="angular.js"></script>
<script src="app.js"></script>
</body>
If you mean to ask how to show a directive template on button click, use a variable in the controller scope to show/hide it and update the variable on button click.
<div ng-controller="ctrl">
<mydirc ng-show="showMydirc"></mydirc>
<button ng-click="clickMe()">call clickMe()</button>
</div>
app.controller("ctrl", function($scope){
$scope.showMydirc=false;
$scope.clickMe = function(){
$scope.showMydirc = true;
}
});

Resources