ngOptions conflicts with my directive and does not work - angularjs

I have written a directive for bootstrap popover. The popover directive itself works fine but when i use it with ngOptions, ngOptions does not work and does not bind data to select atribute.
The directive code is here:
app.directive("ngPopover", function () {
return {
restrict: "A",
scope: { popoverVisible: '=', popoverTitle: "=", popoverContent: "=", popoverTrigger: "#", popoverPlacement: "#" },
link: function (scope, iElement, iAttrs) {
if (scope.popoverTrigger) {
$(angular.element(iElement)).popover({
title: scope.popoverTitle,
content: scope.popoverContent,
trigger: (scope.popoverTrigger) ? scope.popoverTrigger : "manual",
placement: (scope.popoverPlacement) ? scope.popoverPlacement : "right"
});
}
scope.$watch(function () { return scope.popoverVisible; }, function () {
$(angular.element(iElement)).popover('destroy');
$(angular.element(iElement)).popover({
title: scope.popoverTitle,
content: scope.popoverContent,
trigger: (scope.popoverTrigger) ? scope.popoverTrigger : "manual",
placement: (scope.popoverPlacement) ? scope.popoverPlacement : "right"
});
if (scope.popoverVisible)
$(angular.element(iElement)).popover('show');
else
$(angular.element(iElement)).popover('hide');
});
}
};
});
Here is my view code:
<div ng-app="app">
<div ng-controller="Ctrl">
<button class="pop btn btn-danger" ng-click="show()">Show</button>
<br />
<select id="select" ng-options="act for act in activities" ng-model="activity" ng-popover
popover-visible="visPopover" popover-content="'Content'">
</select>
<br />
<button ng-click="hide()">Hide</button>
</div>
</div>
And this is my controller function:
function Ctrl($scope) {
$scope.visPopover = false;
$scope.activities = ['a1', 'a2', 'a3'];
$scope.activity = 'a1';
$scope.hide = function () {
$scope.visPopover = false;
};
$scope.show = function () {
$scope.visPopover = true;
};
}
If there are any other problems or bad practices in my code (specially in writing directive) please let me know!
Update:
Fiddle Link : http://jsfiddle.net/alisabzevari/sZbjt/1/

Stewie was right!
AngularJS Developer guide:
If multiple directives on the same element request a new scope, only
one new scope is created.
So I didn't use scope at all. I got all of my directive properties from attributes. The only trick was that I had to watch one of my attributes popover-visible.
here is the directive code:
app.directive("ngPopover", function () {
return {
restrict: "A",
link: function (scope, iElement, iAttrs) {
if (iAttrs.popoverTrigger) {
$(angular.element(iElement)).popover({
title: iAttrs.popoverTitle,
content: iAttrs.popoverContent,
trigger: (iAttrs.popoverTrigger) ? iAttrs.popoverTrigger : "manual",
placement: (iAttrs.popoverPlacement) ? iAttrs.popoverPlacement : "right"
});
}
scope.$watch(function () { return iAttrs.popoverVisible; }, function () {
$(angular.element(iElement)).popover('destroy');
$(angular.element(iElement)).popover({
title: iAttrs.popoverTitle,
content: iAttrs.popoverContent,
trigger: (iAttrs.popoverTrigger) ? iAttrs.popoverTrigger : "manual",
placement: (iAttrs.popoverPlacement) ? iAttrs.popoverPlacement : "right"
});
if (scope.$eval(iAttrs.popoverVisible))
$(angular.element(iElement)).popover('show');
else
$(angular.element(iElement)).popover('hide');
});
}
};
});

Here is the fix. The trick is to separate the ng-popover from select.
<div ng-app="App">
<div ng-controller="Ctrl">
<button class="pop btn btn-danger" ng-click="show()">Show</button>
<br />
<ng-popover popover-visible="visPopover" popover-content="'Content'"></ng-popover>
<select id="select" ng-options="act for act in activities" ng-model="activity"></select>
<br />
<button ng-click="hide()">Hide</button>
</div>
</div>
var app = angular.module('App', []);
app.directive("ngPopover", function () {
return {
restrict: "E",
scope: {
popoverVisible: '=',
popoverTitle: "=",
popoverContent: "=",
popoverTrigger: "#",
popoverPlacement: "#"
},
link: function (scope, iElement, iAttrs) {
scope.$watch(function () {
return scope.popoverVisible;
}, function () {
var s = iElement.parent().find("select");
$(s).popover('destroy');
$(s).popover({
title: scope.popoverTitle,
content: scope.popoverContent,
trigger: (scope.popoverTrigger) ? scope.popoverTrigger : "manual",
placement: (scope.popoverPlacement) ? scope.popoverPlacement : "right"
});
if (scope.popoverVisible) $(s).popover('show');
else $(s).popover('hide');
});
}
};
}).controller("Ctrl", function ($scope) {
$scope.visPopover = false;
$scope.activities = ['a1', 'a2', 'a3'];
$scope.activity = 'a1';
$scope.hide = function () {
$scope.visPopover = false;
};
$scope.show = function () {
$scope.visPopover = true;
};
});
Try it here.

Related

How to make custom directive run function in angularjs 1.x?

How do I make a directive run a function like the other built in directives in angular?
In example:
<div ng-repeat="someId in myList" my-directive="runFunctionInScope(someId, divHtmlElement)" />
myApp.directive('myDirective', function ()
{
return function (scope, element, attrs)
{
//??
}
}
You can try something like the below code snippet. Also please check this plunker for working example of your given scenario.
Template:
<div ng-repeat="someId in myList" my-method='theMethodToBeCalled' my-id='someId.id' />
Controller:
app.controller('MainCtrl', function($scope) {
$scope.myList=[{
id: 1,
value: 'One'
}, {
id: 2,
value: 'Two'
}];
$scope.theMethodToBeCalled = function(id) {
alert(id);
};
});
Directive:
app.directive("myMethod",function($parse) {
var directiveDefinitionObject = {
restrict: 'A',
scope: {
method:'&myMethod',
id: '=myId'
},
link: function(scope,element,attrs) {
var expressionHandler = scope.method();
expressionHandler(scope.id);
}
};
return directiveDefinitionObject;
});

Update child directive on click

is there a way to update a child directive on click? In my plnkr, column 1 contains a list of names. If you click on the name it will populate the info into the contact directive in column 2. If I make a change in the textbox in column 2, the data in the info directive in column 3 will also change as well. Here is my plnkr:
http://plnkr.co/edit/gcZbd9letYhA4ViBQJ0Q?p=preview
Here is my JS:
var app = angular.module('myApp', []);
app.controller('contactController', function() {
this.contacts = [
{
id: 1,
name: 'Bob'
},
{
id: 2,
name: 'Sally'
},
{
id: 3,
name: 'Joe'
}
]
this.selectedContact;
this.PublishData = function(data) {
this.selectedContact = data;
}
this.UpdateData = function(data) {
for (var i = 0; i < this.contacts.length; i++) {
if (this.contacts[i].id === data.id) {
this.contacts[i] = angular.copy(data);
}
}
}
});
app.directive('contactDirective', function () {
return {
restrict: 'E',
templateUrl: 'contact.html',
scope: {
myModel: '=',
updateData: '&'
},
link: function (scope, element, attrs) {
scope.$watch('myModel', function (newValue, oldValue) {
scope.contact = angular.copy(newValue);
});
}
}
});
app.directive('infoDirective', function () {
return {
restrict: 'E',
templateUrl: 'info.html',
scope: {
contactObject: '='
},
link: function (scope, element, attrs) {
}
}
});
you can simply use $broadcast and $on services
with $broadcat you create an event and you pass a parameter
with $on you listen that event and take that value
I edited your code in this way:
<!--Contact template-->
<div class="col-sm-6">
<b>Column 2</b>
<input type="text" ng-model="newName" />
<button ng-click="updateData({data:contact,newName:newName})">Update</button>
</div>
<div class="col-sm-6">
<b>Column 3</b>
<info-directive contact-object="contact"></info-directive>
</div>
<!-- Your Index file -->
<body ng-app="myApp">
<div ng-controller="contactController as $ctrl">
<div class="row col-md-12">
<div class="col-sm-2">
<b>Column 1</b>
<div ng-repeat="contact in $ctrl.contacts">
</div>
</div>
<div class="col-sm-6">
<contact-directive my-model="$ctrl.selectedContact" update-data="$ctrl.UpdateData(data,newName)"></contact-directive>
</div>
</div>
</div>
</body>
//and your controller
var app = angular.module('myApp', []);
app.controller('contactController', function() {
this.contacts = [
{
id: 1,
name: 'Bob'
},
{
id: 2,
name: 'Sally'
},
{
id: 3,
name: 'Joe'
}
]
this.selectedContact;
this.PublishData = function(data) {
this.selectedContact = data;
}
this.UpdateData = function(data,newName) {
for (var i = 0; i < this.contacts.length; i++) {
if (this.contacts[i].id === data.id) {
this.contacts[i].name = newName;
}
}
}
});
app.directive('contactDirective', function () {
return {
restrict: 'E',
templateUrl: 'contact.html',
scope: {
myModel: '=',
updateData: '&'
},
link: function (scope, element, attrs) {
scope.$watch('myModel', function (newValue, oldValue) {
scope.newName = newValue.name;
scope.contact = angular.copy(newValue);
});
}
}
});
app.directive('infoDirective', function () {
return {
restrict: 'E',
templateUrl: 'info.html',
scope: {
contactObject: '='
},
link: function (scope, element, attrs) {
}
}
});

What is the scope of templates bindings in a directive without a controller

Here is a simple angular application. It shows/hides a text based on user clicking a button.
<div ng-controller="exampleController as ctrl">
<example></example>
</div>
app.controller('exampleController', function () {});
app.directive('example', function () {
return {
restrict: 'E',
template: '<p ng-show=\"showMe\">Text to show</p><button ng-click=\"clickMe()\">Click me</button>',
scope: {},
link: function (scope) {
scope.clickMe = function () {
scope.showMe = !scope.showMe;
};
}
};
});
When I remove the controller it doesn't work. Note that the directive creates an isolate scope, so my understanding is that it doesn't depend on the controllers scope.
<div>
<example></example>
</div>
app.directive('example', function () {
return {
restrict: 'E',
template: '<p ng-show=\"showMe\">Text to show</p><button ng-click=\"clickMe()\">Click me</button>',
scope: {},
link: function (scope) {
scope.clickMe = function () {
scope.showMe = !scope.showMe;
};
}
};
});
What is wrong here?
But anyways you should have ng-app
<body ng-app="demo">
<div>
<example></example>
</div>
</body>
var app = angular.module("demo", [])
app.directive('example', function () {
return {
restrict: 'E',
template: '<p ng-show=\"showMe\">Text to show</p><button ng-click=\"clickMe()\">Click me</button>',
scope: {},
link: function (scope) {
scope.clickMe = function () {
scope.showMe = !scope.showMe;
};
}
};
});
Fiddle

AngularJs, when directive compile new attributes, the events are not triggered if there is a scope

I'm a bit stuck on an directive which add attributes and recompile the element.
If I had a scope on the directive ng-change is not triggered anymore (without it it works). I based my test on this answer
The HTML
<div ng-app="myApp">
<div ng-controller='testController'>
<div ng-repeat="field in fields">
<input type="text" ng-model="ngModel[field.fieldName]" property="{{formParams.getProperties(field.fieldName)}}" update-attr ng-change="test()" />
</div>
</div>
</div>
The directive:
angular.module('myApp', [])
.controller('testController', function ($scope) {
$scope.properties = {
"something": {
"style": "float:left;"
},
"something2": {
"style": "float:right;"
}
};
$scope.ngModel = {};
$scope.fields = [{
fieldName: 'something'
}, {
fieldName: 'something2'
}];
$scope.test = function () {
alert('i dont get triggered');
};
$scope.formParams = {
getProperties: function (fieldName) {
return $scope.properties[fieldName];
}
};
})
.directive('updateAttr', function ($compile) {
return {
restrict: 'A',
replace: true,
terminate: true,
scope: {
ngModel : '='
},
link: function (scope, elem, attrs) {
if (angular.isDefined(attrs['property']) && attrs['property'].lenght != 0) {
var json = JSON.parse(attrs['property']);
angular.forEach(json, function (value, key) {
elem.attr(key, value);
});
elem.removeAttr('property');
var $e = $compile(elem[0].outerHTML)(scope);
elem.replaceWith($e);
}
}
};
});
Here a fork of the fiddle to test with a scope on the directive: fiddle
Do you have any suggestion ?
I found why ng-change was not trigger so I share the answer:
When we add scope attribute on the directive, a new scope is created. So we have to use $scope.$parent for the compilation. I have updated the fiddle with the correction.

angularJS: watch async data in directive

I'm trying to watch a async data in my directive, here is my JS code:
(function(angular) {
var myApp = angular.module('testTree', []);
myApp.config(function($httpProvider) {
$httpProvider.defaults.headers.get = {};
$httpProvider.defaults.headers.get["Content-Type"] = "application/json";
});
myApp.factory('DataService', function($http) {
return { getData: function(prmParentId, prmParentSrc) {
var data = $.param({ 'parentId': prmParentId, 'parentSrc': prmParentSrc });
return $http.get("Temp.aspx/GetData", { params: { parentId: prmParentId, parentSrc: prmParentSrc }, data: '' }).
success(function(result) {
return result.d;
});
}
}
});
myApp.controller('myController', myController);
function myController($scope, DataService) {
$scope.treeNodes = [];
$scope.init = function() {
DataService.getData(0, 0).then(function(promise) {
$scope.treeNodes = promise.data.d;
});
}
$scope.focusNode = function() {
console.log("kuku2");
}
}
myApp.directive('nodes', function() {
return {
restrict: "E",
replace: true,
scope: {
nodes: '=',
clickFn: '&'
},
template: "<ul><node ng-repeat='node in nodes' node='node' click-fn='clickFn()'></node></ul>",
link: function(scope, element, attrs) {
scope.$watch('treeNodes', function(newVal, oldVal) {
console.log(scope.treeNodes);
}, true);
}
}
});
myApp.directive('node', function($compile) {
return {
restrict: "E",
replace: true,
scope: {
node: '=',
clickFn: '&'
},
template: "<li><span ng-click='clickFn()(node)'>{{node.ObjectName}}</span></li>",
link: function(scope, element, attrs) {
if (angular.isArray(scope.node.Children)) {
element.append("<nodes nodes='node.Children' click-fn='clickFn()'></nodes>");
$compile('<nodes nodes="node.Children" click-fn="clickFn()"></nodes>')(scope, function(cloned, scope) {
element.append(cloned);
});
}
}
}
});
})(angular);
And this is my HTML:
<div ng-app="testTree">
<div ng-controller="myController">
<div ng-init="init()">
<nodes node="treeNodes" click-fn="focusNode"></nodes>
</div>
</div>
</div>
The console in the directive's watch is always "undefined". What am I doing wrong here?
thanks.
Pass treeNodes as nodes in your directive. So you need to watch nodes.
scope.$watch('nodes', function(newVal, oldVal) {
console.log($scope.nodes);
}, true);
<div ng-app="testTree">
<div ng-controller="myController">
<div ng-init="init()">
<nodes nodes="treeNodes" click-fn="focusNode"></nodes>
</div>
</div>
</div>

Resources