I'm trying to create programmatically popover, but faced with following problem. I can't access parent scope inside popover template. Expected result is:
Hello my name is Roman
but I get
Hello my name is undefined
Here is plunker
If I use bs-popover as attribute on any element, then I get expected result.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<title>Popover issue</title>
</head>
<body>
<div data-ng-app="myApp" data-ng-controller="defaultCtrl" style="margin: 100px 100px">
<button custom-popover ng-click="showPopover()">Popover</button>
<script type="text/ng-template" id="example.html">
<p>My name is {{user.name || 'undefined' }}</p>
</script>
</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
<script src="//code.angularjs.org/1.3.8/angular-sanitize.min.js" data-semver="1.3.8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-strap/2.1.5/angular-strap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-strap/2.1.5/angular-strap.tpl.min.js"></script>
<script type="text/javascript">
var app = angular.module("myApp", ['mgcrea.ngStrap', 'ngSanitize']);
app.controller("defaultCtrl", ["$scope", function($scope) {
$scope.user = {
name: "Roman"
};
}]);
app.directive("customPopover", ["$popover", "$compile", function($popover, $compile) {
return {
restrict: "A",
scope: true,
link: function(scope, element, attrs) {
var myPopover = $popover(element, {
title: 'My Title',
contentTemplate: 'example.html',
html: true,
trigger: 'manual',
autoClose: true
});
scope.showPopover = function() {
myPopover.show();
}
}
}
}]);
</script>
</body>
</html>
Thanks in advice
Checkout http://plnkr.co/edit/62BDv7JwluOl3eqtXPCZ?p=preview
Prototype inheritance is default on scope in angular.
So if you are not creating isolated scope then you can access parent scope objects from your scope directly until and unless you are not overriding them.
var myPopover = $popover(element, {
title: 'My Title',
contentTemplate: 'example.html',
html: true,
trigger: 'manual',
autoClose: true,
scope: scope
});
scope.showPopover = function() {
myPopover.show();
}
Related
If I have an element which has ng-class attribute with some binding and I call addClass on that element will these two work fine together?
I mean that will class attribute of that element contain both classes (one added via addClass call and another from ng-class)
For example:
Here is the element:
<div class="ui-grid-cell-contents"
ng-class="[col.colIndex(), { 'position-relative': col.grouping && row.groupHeader }]"
ng-bind="COL_FIELD CUSTOM_FILTERS"
data-directive-that-calls-add-class>
Here is the directive that calls addClass (its link function):
link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes) => {
element.addClass("disable-safari-tooltip");
Here is an example where both the classes will be added one through ng-class and other through a directive.
The ng-class makes background yellow and directive makes text green
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<style>
.yellow{
background-color: yellow
}
.red{
color: green
}
</style>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<div red ng-class=" { 'yellow': yellow}" >Test</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.yellow = true;
})
app.directive('red', function () {
return {
restrict: 'A',
link: function ($scope, $element, $attr, $timeout) {
$element.addClass("red");
}
};
});
</script>
</body>
</html>
But, if the same css property needs to be changed, then the last added will take more priority, for example, check the class is changed after 2 seconds through a directive
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<style>
.yellow{
background-color: yellow
}
.red{
color: green
}
.blue{
background-color: blue
}
</style>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<div red ng-class=" { 'yellow': yellow}" >Test</div>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.yellow = true;
})
app.directive('red', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $element, $attr) {
$element.addClass("red");
$timeout(function () {
$element.addClass("blue");
}, 2000);
}
};
});
</script>
</body>
</html>
Here is a demo URL
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
})
})
}
})
I'm trying to migrate from angular 1.2.28 to angular 1.3.16, however my code broke.
Angular 1.2.28 working: http://plnkr.co/edit/XfVakwA3Upm7Z2wosHCQ?p=preview
Angular 1.3.16 not working: http://plnkr.co/edit/4VxcHL0MHddobkmu9DMG?p=preview
JS
var app = angular.module('app', []);
app.run(function($rootScope, $timeout){
$rootScope.loading = true;
$timeout(function(){
$rootScope.items = ['Angular', '1.3.16', 'doesnt work'];
$rootScope.loading = false;
}, 3000);
});
app.directive('refresh', function(){
return {
restrict: 'A',
require: '^myDirective',
link: function(scope, element, attrs, ctrl){
if(scope.$last)
ctrl.init();
}
};
});
app.directive('myDirective', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p>Height: {{myHeight}}</p> <div ng-transclude></div></div>',
controller: function($scope, $element){
this.init = init;
function init(){
$scope.myHeight = $('.my-directive').height();
}
}
};
});
HTML
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.3.16" data-semver="1.3.16" src="https://code.angularjs.org/1.3.16/angular.js"></script>
<script data-require="jquery#1.11.0" data-semver="1.11.0" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular 1.3.16</h1>
<div ng-show="loading">Loading...</div>
<my-directive ng-hide="loading">
<div ng-repeat="item in items" refresh>
<p>{{item}}</p>
</div>
</my-directive>
</body>
</html>
The idea is to only run certain code when the inner html is outputted.
Height is 0 in angular 1.3.16.
However, if I remove ng-hide="loading" from <my-directive ng-hide="loading"> in angular 1.3.16, height gets the appropriated value.
Any ideas how can I solve this?
Inject $timeout into your directive and put the init code block in $timeout(function(){ ... }) like this:
app.directive('myDirective', function($timeout){
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p><b>Height: {{myHeight}}</b></p> <div ng-transclude></div></div>',
controller: function($scope, $element){
this.init = init;
function init(){
$timeout(function(){
$scope.myHeight = $('.my-directive').height();
});
}
}
};
});
var app = angular.module('app', []);
app.run(function($rootScope, $timeout) {
$rootScope.loading = true;
$timeout(function() {
$rootScope.items = ['Angular', '1.3.16', ' work'];
$rootScope.loading = false;
}, 1000);
});
app.directive('myDirective', function($timeout) {
return {
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="my-directive"><p>Height: {{myHeight}}</p> <div ng-transclude></div></div>',
link: function($scope, $element) {
$element.on('DOMAttrModified DOMSubtreeModified', init);
function init() {
$scope.$apply(function() {
$scope.myHeight = $element.height();
});
}
}
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="jquery#1.11.0" data-semver="1.11.0" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script data-require="angular.js#1.3.16" data-semver="1.3.16" src="https://code.angularjs.org/1.3.16/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Angular 1.3.16</h1>
<div ng-show="loading">Loading...</div>
<my-directive ng-hide="loading">
<div ng-repeat="item in items" refresh>
<p>{{item}}</p>
</div>
</my-directive>
</body>
</html>
You have to set the height in the correct angular directive phase/lifecycle. You should set the hight in the link or even postlink phase. Usually the two phases are the same if you don't use prelink This is when all the content has already been rendered. See angular $compile or google for angular post link
The controller is for the logic and the link is for html/dom manipulations.
EDIT:
You can bind 'DOMAttrModified DOMSubtreeModified` events to trigger changes.
Hi I am trying to bind a value to a directive using '#' which is defined in the ng-init event. but this doesn't comes into my directive and it returns undefined. When does this property actually resolves is it in Compile, link? Also, why is it returning undefined always? What am I doing wrong here?
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="demoController" ng-init="init()">
<h1>Confused directives!!!</h1>
<my-user userName="{{user.name}}"></my-user>
<br>
{{user.name}}
</body>
</html>
(function(){
'use strict';
var app = angular.module('app', []);
app.controller('demoController', ['$scope', demoController])
function demoController($scope){
console.log('Im in the controller');
$scope.init = function (){
$scope.user = {
name: 'Argo'
}
console.log('Im in init method of controller')
}
}
app.directive('myUser', myUser);
function myUser(){
return {
restrict: 'AE',
replace: true,
scope: {
userName: '#'
},
template: '<div><b>{{userName}} is defined from init method </b></div>',
link: function(scope, ele, attr){
console.log('Im in link method of directive');
console.log('this is loaded' + scope.userName);
attr.$observe('userName', function(newValue){
console.log(newValue);
})
}
}
}
})();
http://plnkr.co/edit/vAKMNub8HRcphiaMMauR?p=preview
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;
}
});