What is the *Angular* way to get an elements siblings? - angularjs

What is the idiomatic way to get an elements siblings when it is clicked using AngularJS?
So far I've got this:
<div ng-controller="FooCtrl">
<div ng-click="clicked()">One</div>
<div ng-click="clicked()">Two</div>
<div ng-click="clicked()">Three</div>
</div>
<script>
function FooCtrl($scope){
$scope.clicked = function()
{
console.log("Clicked", this, arguments);
};
}
</script>
here's a jQuery implementation as a concrete example:
<div id="foo">
<div>One</div>
<div>two</div>
<div>three</div>
</div>
<script>
$(function(){
$('#foo div').on('click', function(){
$(this).siblings('div').removeClass('clicked');
$(this).addClass('clicked');
});
});
</script>

Use a directive, since you want to traverse the DOM:
app.directive('sibs', function() {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
element.parent().children().removeClass('clicked');
element.addClass('clicked');
})
},
}
});
<div sibs>One</div>
<div sibs>Two</div>
<div sibs>Three</div>
Note that jQuery is not required.
fiddle

Here is an angular version of the jQuery sample that you provided:
HTML:
<div ng-controller="FooCtrl">
<div ng-click="selected.item='One'"
ng-class="{clicked:selected.item=='One'}">One</div>
<div ng-click="selected.item='Two'"
ng-class="{clicked:selected.item=='Two'}">Two</div>
<div ng-click="selected.item='Three'"
ng-class="{clicked:selected.item=='Three'}">Three</div>
</div>
JS:
function FooCtrl($scope, $rootScope) {
$scope.selected = {
item:""
}
}
NOTE: You dont strictly need to access DOM for this. However if you still want to then you can write a simple directive. Something like below:
HTML:
<div ng-controller="FooCtrl">
<div ng-click="clicked()" get-siblings>One</div>
<div ng-click="clicked()" get-siblings>Two</div>
<div ng-click="clicked()" get-siblings>Three</div>
</div>
JS:
yourApp.directive('getSiblings', function() {
return {
scope: true,
link: function(scope,element,attrs){
scope.clicked = function () {
element.siblings('div').removeClass('clicked');
element.addClass('clicked');
}
}
}
});
fiddle

Following is a directive built exclusively with Angular grammar (borrowing from jqLite):
link: function(scope, iElement, iAttributes, controllers) {
var parentChildren,
mySiblings = [];
// add a marker to this element to distinguish it from its siblings
// this could be a lot more robust
iElement.attr('rsFindMySiblings', 'anchor');
// get my parent's children, it will include me!
parentChildren = iElement.parent().children();
// remove myself
scope.siblings = [];
for (var i=0; i < parentChildren.length; i++) {
var child = angular.element(parentChildren[i]);
var attr = child.attr('rsFindMySiblings');
if (!attr) {
scope.siblings.push({name: child[0].textContent});
}
}
}
Note that it uses a controller to store the results. See this plunker for a detailed example

Related

Access the main controller's scope from inside a directive

Suppose the following blueprint code:
<div ng-controller="myCtrl">
<div ng-repeat="...">
<div ng-repeat="...">
<div ng-repeat="...">
<div ng=if="..." my-directive>
</div>
</div>
</div>
</div>
</div>
myApp.directive('myDirective', function() {
return {
controller: function($scope){
console.log('controller scope');
console.log($scope);
},
link:function(scope,element){
console.log('link scope');
console.log(scope);
}
}
});
Both outputs in console will point to the scope created by ng-if directive. My question is how may I access myCtrl's scope from inside the directive . Of course not by using $parent.$parent....
The easiest way could be by using require in the directive, like:
<div ng-controller="MyCtrl">
<div my-directive></div>
</div>
var myApp = angular.module("app", []);
myApp.controller("MyCtrl", function($scope) {
this.text = "I am in Controller Scope";
this.getValue = function() { return this.text; };
});
myApp.directive("myDirective", function() {
return {
require: "^ngController",
link: function(scope, elem, attrs, ngCtrl) {
elem.text(ngCtrl.getValue());
}
};
});
EDIT
In your case, I think you could use the controller scope variables and methods in the directive by using scope binding with &; snippet below:
<div ng-controller="MyCtrl as vm">
<my-directive on-get-value="vm.getValue()">
</my-directive>
</div>
angular.module('app', [])
.controller('MyCtrl', function($window) {
var vm = this;
vm.getValue = function() { $window.alert("I am in Controller Scope"); };
})
.directive('myDirective', function() {
return {
scope: {
onGetValue:'&'
},
controllerAs:'vm',
controller: function($scope) {
$scope.onGetValue();
}
};
});
Use services to share data between angular components. This question might be a good start: Share data between AngularJS controllers. This approach will work for sharing data between controller and directive as well
When you are creating your directive, the returning function is called DDO (Directive Defining Object). One of its attributes is 'scope'. if you initialize it with scope : true, the directive will prototypically inherit the parent scope. If you set scope: false, the directive will use the parent scope. And finally, if you set scope : {...}, it will created an isolated scope.
var app = angular.module("test",[]);
app.controller("myCntrl",function($scope){
$scope.text = "Im in controller Scope";
});
app.directive("myDirective", function(){
return {
restrict: "EA",
scope: true,
template: "<div>Where are you, directive ? {{text}}</div>"
};
});
h2 {
cursor: pointer;
}
.directive {
border: 5px solid #F5BF6E;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="test">
<div ng-controller="myCntrl">
<h2 ng-click="reverseName()">Where are you ? {{text}}</h2>
<div my-directive class='directive'></div>
</div>
</div>
You can check this link for more details : Directive Scopes

Variable value not passing in a controller using directive with ng-class

I am referencing the value of the variable in a controller in an ng-class template but its not working.
here is the html directive template URl :
<div class="tooltip-anchor">
<div class=" tooltip-content ehub-body" ng-class="{ 'tooltip__content--disabled': tooltipContentValue}" ng-transclude>Tooltip content</div>
</div>
Here is where i am using the directive in the index page
<div style="text-align:center;">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me
</div>
And here is the directive:
in this directive i am creating a variable and setting it to false and also trying to use it in an ng-class attribute
(function (window) {
'use strict';
angular
.module('ehub.component.tooltip', [])
.controller('ehubTooltipCtrl', ['$scope', function ($scope) {
$scope.tooltipContentValue = false;
}])
.directive('ehubTooltip', ehubTooltip);
function ehubTooltip() {
var directive = {
controller: "ehubTooltipCtrl",
link: link,
transclude: true,
templateUrl: 'ehub-tooltip.html',
restrict: 'E'
};
return directive;
function link(scope, element, attrs) {
scope.keyupevt = function () {
if (event.keyCode === 27) {
$scope.tooltipContentValue = true;
}
}
}
}
})();
Try this working jsfiddle.
angular.module('ExampleApp', ['ngMessages'])
.controller('ExampleController', function($scope) {
})
.directive('ehubTooltip', function() {
var directive = {
link: link,
transclude: true,
template: '<div class="tooltip-anchor"><div class=" tooltip-content ehub-body" ng-class="{ \'tooltip__content--disabled\': tooltipContentValue}" ng-transclude>Tooltip content</div></div>',
restrict: 'E'
};
function link(scope, element, attrs) {
scope.tooltipContentValue = false;
scope.keyupevt = function() {
if (event.keyCode === 27) {
scope.tooltipContentValue = true;
}
}
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<div style="text-align:center;">
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here</a>
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me</a>
</div>
</div>
</div>

How to apped a `directive` by click

Say, I have multiple directives, on click how can i add the "directive" what i want to "popup". i have number of scenario, but using only one pop-up for all. whenever the appropiate element is clicked, using the same pop-up, i would like to update the other 'directives'
for sample, i created only one. click on the red bordered title
here is my code :
<div ng-controller="main">
{{name}}
<div class="info" ng-click="popIt()">
<info-board></info-board>
</div>
<div class="popup" ng-if="show" ng-click="popIt()">
//add directive dynamically - how?
</div>
</div>
</body>
<script type="text/ng-template" id="modalPopup.html">
<div class='ng-modal'>
some information here.
</div>
</script>
js:
var myApp = angular.module("myApp", []);
myApp.controller("main", function($scope) {
$scope.name = "Mo!";
$scope.show = false;
$scope.popIt = function (show) {
$scope.show = !$scope.show;
}
})
myApp.directive("infoBoard", function() {
return {
replace : true,
templateUrl: "modalPopup.html",
scope : {},
link : function (scope, element, attrs) {
element.append("Hi there!")
}
}
})
Demo Onlie

ANgularjs: ng-repeat and nested custom directive

I am trying to ng-repeat a custom directive, which has an attribute that should change over the iteration.
This is my html:
<div ng-controller="WalletsController as controller">
<bitcoin-address ng-repeat="bitcoin_label in controller.getWallets()" bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
This is my controller:
(function() {
var app = angular.module('wallets', [ ]);
app.controller(
"WalletsController",
function($scope, $http) {
this.wallets = [];
var controller = this;
this.getWallets = function() {
return controller.wallets;
};
$http.get("wallet_addresses").success(
function(data) {
for (var i = 0; i < data.length; i++) {
var curWallet = data[i];
$scope[curWallet.label] = {
label: curWallet.label,
address: curWallet.address,
balance: curWallet.balance
};
controller.wallets.push(curWallet.label);
}
}
);
});
app.directive(
'bitcoinAddress',
function() {
return {
restrict: 'E',
templateUrl: '../../resources/html/bitcoin-address.html',
scope: {
bitcoinLabel: '=',
}
};
}
);
})();
And this is my template:
<div class="col-md-8 dashboardAddressCell dropdown-toggle" data-toggle="dropdown">{{bitcoinLabel.address}}</div>
What happens is that the template can not resolve the bitcoinLabel variable. I have tried specifying a constant value and the template works. My conclusion is that I am not correctly specifying the bitcoin_label attribute in the html section.I have also tried using {{bitcoin_address}}, but angularjs complains about that.
I have also tried with the following html code:
<div ng-controller="WalletsController as controller">
<!-- <tr><th>Indirizzo</th><th>Saldo</th><th></th>-->
<div ng-repeat="bitcoin_label in controller.getWallets()">
{{bitcoin_label}}
<bitcoin-address bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
<bitcoin-address bitcoin-label="ciccio"></bitcoin-address>
</div>
It does not work either, but at least it shows the {{bitcoin_label}} value.
The problem seems pretty simple. Instead of
controller.wallets.push(curWallet.label);
you should push corresponding $scope[curWallet.label] object:
controller.wallets.push($scope[curWallet.label]);
Because curWallet.label is just a string so in the first case wallets ends up as array of stings. However you need wallets to be an array of objects, each having address, label, balance properties.
You have some problems with your logic. You're putting wallet labels into .wallets, then iterating over the labels, and then in your bitcoinAddress template you're trying to read .address property of the label string (not from the object where you saved it). Why not simplify the whole thing to this script:
.controller("WalletsController", function($scope, $http) {
$scope.wallets = [];
$http.get("wallet_addresses").success(function(data) {
$scope.wallets = data.slice();
});
})
.directive('bitcoinAddress', function() {
return {
restrict: 'E',
templateUrl: '...',
scope: {
wallet: '=',
}
};
})
this directive template:
<div class="..." ...>{{wallet.address}}</div>
and this body template:
<div ng-controller="WalletsController as controller">
<bitcoin-address ng-repeat="wallet in wallets" wallet="wallet"></bitcoin-address>
</div>
Both bitcoinAddress and ng-repeat directives creating scopes on the same element could cause some conflict (isolate scope in the bitcoinAddress case).
Try adjusting your html structure slightly:
<div ng-controller="WalletsController as controller">
<div ng-repeat="bitcoin_label in controller.getWallets()">
<bitcoin-address bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
</div>
Why not use $scope.wallets instead of this.wallets? Also in your getWallets function. After that try
<div ng-controller="WalletsController">
<div ng-repeat="bitcoin_label in wallets">
<bitcoin-address bitcoin-label="bitcoin_label"></bitcoin-address>
</div>
</div>
But if your wallets is an array of non-object like array of string or integer, use
<div ng-controller="WalletsController">
<div ng-repeat="bitcoin_label in wallets track by $index">
<bitcoin-address bitcoin-label="wallets[$index]"></bitcoin-address>
</div>
</div>

Can not reflect changes in model

I have an AngularJS controller like this
var myModule = angular.module('test', []);
myModule.controller('PendataanMainController',
function($scope, $http)
{
$scope.stat1 = "";
$scope.stat2 = "stat2";
$scope.AmbilStat1 = function()
{
$scope.stat1 = "Hallo stat1";
};
$scope.AmbilStat2 = function()
{
$scope.stat2 = "Hallo stat2";
}
}
);
myModule
.directive(
'fonloaded',
function()
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var this_element = angular.element(element);
this_element.bind(
'click',
function()
{
//this_element.html('<h2>Hallo dari directive</h2>');
scope.stat1 = "Hallo dari directive";
}
);
scope.$apply();
}
}
}
);
and a HTML like this
<div ng-app="test" ng-controller="PendataanMainController" class="columns clear bt-space15">
<div class="col1-2 fl-space2">
<div class="content-box">
<div class="box-body">
<div fonloaded class="box-header clear">
<h2>{{stat1}}</h2>
</div>
{{AmbilStat2()}}
<div class="box-wrap clear">
{{stat2}}
</div>
</div>
</div>
</div>
<div class="col1-2 lastcol">
<div class="content-box">
<div class="box-body">
<div class="box-header clear">
<h2>Stat 2</h2>
</div>
<div class="box-wrap clear">
{{stat2}}
</div>
</div>
</div>
</div>
</div>
I made a custom directive 'fonloaded' and put it on this part:
<div fonloaded class="box-header clear">
<h2>{{stat1}}</h2>
</div>
and when it got clicked, the directive link function change the value of stat1. I want that new stat1 reflected in the html. But it don't.
I am using AngularJS extension for Chrome. And I can see that the stat1 value has changed, but that change does not reflected on the html.
What would be the problem?
http://plnkr.co/edit/4kiJwrG8Xo0unrNc9Fw7?p=preview
please see fixed sample:
function()
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var this_element = angular.element(element);
this_element.on('click',
function(e)
{
// alert('1');
//this_element.html('<h2>Hallo dari directive</h2>');
scope.stat1 = "Hallo dari directive";
scope.$apply();
} );
}
}
}
I must add that I am working within a HTML template (like one from theme forest). I have stripped the ng-app part out of the HTML template to make it stand alone. And now it reflect the changes in the model.
Somehow the HTML template using jQuery to modify the tag which has fonloaded directive. As result {{stat1}} lost by HTML template transformation.

Resources