This is my code right now:
var app = angular.module("MyApp", []);
app.controller("ctrl", function($scope) {
$scope.slides = [{
title: "Slide 1"
},{
title: "Slide 2"
}];
$scope.clicked = {title: "undefined slide"};
});
app.directive("scroller", function() {
return {
template: "<div slide ng-click='click()' ng-repeat='slide in slides'></div>"
};
});
app.directive("slide", function() {
return {
template: "{{slide.title}}",
controller: function($scope) {
// controller
$scope.click = function() {
// Tell parent I was clicked
}
},
link: function(scope, element, attrs) {
// link
}
};
});
I want the clicked in the main controller to be the slide which was clicked.
$scope.clicked = $scope.slide
obviously does not work because of the repeat scope.
Two way databinding does not work, because this would create a seconde scope for the element and hence the template of the slide directive would stop working.
I know that
$scope.$parent.clicked = $scope.slide;
would work, but I wanted to ask for a better solution, because I don't like accessing the parent scope like this.
Can someone please help me with this? Maybe broadcast the clicked item?
Thanks in advance.
You can use 'require' option of the directive. This option is used to specify that your controller requires another one to work properly.
Your code should look like this:
var app = angular.module("MyApp", []);
app.controller("ctrl", function($scope) {
$scope.slides = [{
title: "Slide 1"
},{
title: "Slide 2"
}];
$scope.clicked = {title: "undefined slide"};
this.slideClicked = function(slide){
$scope.clicked = slide;
};
});
app.directive("scroller", function() {
return {
template: "<div>{{clicked.title}}</div><div slide ng-click='click(slide)' ng-repeat='slide in slides'></div>",
controller: 'ctrl'
};
});
app.directive("slide", function() {
return {
template: "{{slide.title}}",
require: '^scroller',
controller: function($scope) {
// controller
$scope.click = function(slide) {
$scope.containerController.slideClicked(slide);
}
},
link: function(scope, element, attrs, controllers) {
scope.containerController = controllers;
// link
}
};
});
Here is https://jsfiddle.net/60zbpvau/1/
You can also store the information in a property of a property of $scope. When the $scope is created for the child controls, its properties are shallow copied.
Related
I'm working on a fiddle that I found from this answer on SO
I've modified the fiddle for my situation in the following way:
angular.module('sampleApp', [])
.controller('myCtrl', function($scope) {
$scope.func = function() {
$scope.name = "Test Name";
}
})
.directive("myDirective", function($compile) {
return {
template: "<div>{{name}}</div>",
scope: {
name: '='
},
link: function(scope, element, attrs) {
alert(scope.name);
}
}
});
Basically, I am trying to pass a variable value from a scope function to the directive - but the alert message shows up an undefined value.
Any idea as to what is going wrong here? How can I pass the value stored within $scope.name within the $scope.func function and pass it to the directive?
The updated fiddle can be found here.
The problem is that you are defining name inside a function in your controller and that function is never called. Change to this.
angular.module('sampleApp', [])
.controller('myCtrl', function($scope) {
$scope.name = "Test Name";
})
.directive("myDirective", function($compile) {
return {
template: "<div>{{name}}</div>",
scope: {
name: '='
},
link: function(scope, element, attrs) {
alert(scope.name);
}
}
});
<div ng-app="sampleApp" ng-controller="myCtrl">
<div my-directive name="name">
</div>
</div>
The alert function is executing before the data is set by the controller.
To see value changes, add a controller and use $onChanges Life-Cycle Hook:
app.directive("myDirective", function() {
return {
template: "<div>{{name}}</div>",
scope: {
̶n̶a̶m̶e̶:̶ ̶'̶=̶'̶
name: '<'
},
controller: function() {
this.onChanges = function(changesObj) {
if (changesObj.name) {
alert(changesObj.name.currentValue);
};
};
}
}
});
Also note that one-time < binding is used instead of two-way = binding.
I am new to AngularJS and I am trying to create directive which contains another directive inside.
Here how first directive look like
(
function () {
app.directive("cmEventBar", function () {
var controller = function () {
var vm = this;
vm.events = [
{name: "Truckdriver1", eventId: 1 },
{name: "Truckdriver2", eventId: 1 },
{name: "Truckdriver3", eventId: 1 }
];
}
return {
templateUrl: "app/shared/eventsBar/eventBar.html",
restrict: "E",
controller: controller,
priority: 1000,
terminal: true,
controllerAs: "vm",
scope: {}
};
});
})();
Inside html of this directive I am using ng-repeat to show all events. Insinde ng-repeat I have another directive:
<div ng-repeat="item in vm.events">
<cm-single-event name="{{item.name}}" eventId="{{item.eventId}}"></cm-single-event>
</div>
Here how second directive look like:
(function () {
app.directive("cmSingleEvent", function () {
var controller = function () {
var vm = this;
vm.info = switchEvent(eventId);
}
return {
template: "<li class={{vm.info.itemClass}}> {{vm.name}} {{vm.info.messege}} </li>",
restrict: "E",
controller: controller,
controllerAs: "vm",
scope: {
name: "=",
eventId: "="
}
};
});
})();
The output is a little weird because event has 3 elements and in output I see only one element + there are no vm.info from directive inside.
Output
{{vm.name}} {{vm.info.messege}}
What am I doing wrong here?
Thanks in advance!
As you are using = for name & eventId, you should not use {{}} while assigning there value in attribute & cmSingleEvent directive should have bindToController: true to retrieve value from isolated scope to controller this(context)
<cm-single-event name="item.name" event-id="item.eventId"></cm-single-event>
I have a dynamically loaded directive that listens for an event from its parent. What happens is this event is getting handled twice inside the directive even though the parent only broadcasts the event once. This event is getting listened to multiple times and I'm not sure where to clean this up.
Here is the Plunker example. You can trigger the parent event by clicking the "Parent Click" button and the handler prints text using console.log(). You will see that the console.log is getting executed twice.
http://plnkr.co/edit/aPiMO2PKDybsZ4cS7Fnh?p=info
Here is my script:
(function(angular) {
'use strict';
var myAppModule = angular.module('myApp', []);
myAppModule.controller('myController', function($scope, $rootScope) {
$scope.directiveId = 'my-directive1';
$scope.directiveData = 'Directive 1 data.';
$scope.parentClick = function() {
$rootScope.$broadcast("parentEvent", "hello from parent");
}
});
myAppModule.directive('loaddirective', function($compile) {
return {
restrict: 'A',
scope: {
loaddirective: "=",
directivedata: "="
},
link: function($scope, $element, $attr) {
console.log('loaddirective link');
var value = $scope.loaddirective;
function update(value) {
$element.html("<div " + value + " directivedata='directivedata'></div>");
$compile($element.contents())($scope);
}
if (value) {
update(value);
}
$scope.$watch('loaddirective', update);
},
controller: ['$scope',
function($scope) {
console.log('loaddirective controller');
}
]
};
});
myAppModule.directive('myDirective1', function() {
return {
templateUrl: 'my-directive1.html',
scope: {
directivedata: "="
},
controller: ['$scope',
function($scope) {
var offParentEvent = $scope.$on('parentEvent', function(event, arg) {
// This event is getting handled twice here.
// This is due to my $watch in the 'loaddirective'
// directive. I need this watch to be able to
// dynamically change this directive later.
// My guess is I need to deregister this by
// calling offParentEvent() somewhere but I don't
// know where to do that.
console.log('myDirective1 parentEvent data: ' + arg);
});
}
]
};
});
})(window.angular);
I have these nested directives the very inner of which has an 'X' sign, which is, when clicked, is supposed to delete an item (classic problem). Basically, the whole thing is a menu.
I have added an ng-click to the 'X' sign/button of the item, but i am very confused on how to link this whole thing back to the controller, so that i can call a deleteItem() function and actually remove the item. I also want to have scope for the sidebar-item separated (the original version of this code is slightly more elaborated and has conditional statements)
Here's what i have so far
The full working - i.e. not really working - version can be found in this jsfiddle
Here's the relevant part of HTML:
<div ng-app="demoApp">
<div ng-controller="sidebarController">
<div sidebar>
<div sidebar-item ng-repeat="item in items" item="item"></div>
</div>
<button ng-click="addItem();">Add Item</button>
</div>
</div>
And here's the JavaScript:
var demoApp = angular.module('demoApp', []);
demoApp.controller("sidebarController", ["$scope", function($scope) {
$scope.items = [
];
$scope.itemId = 1;
$scope.addItem = function() {
var inx = $scope.itemId++;
$scope.items.push( { id: inx, title: "Item " + inx, subTitle: "Extra content " + inx } );
};
$scope.deleteItem = function(item) {
console.log("Delete this!");
console.log(item);
};
}]);
demoApp.directive("sidebar", function() {
return {
restrict: "A",
transclude: true,
template: '<div><div ng-transclude></div></div>',
controller: ["$scope", function($scope) {
this.deleteItem = function(item) {
$scope.deleteItem(item);
$scope.$apply();
};
}]
};
});
demoApp.directive("sidebarItem", function() {
return {
restrict: "A",
scope: {
item: "="
},
require: "^sidebar",
template: '<div><span>{{ item.title }}</span><br /><span>{{ item.subTitle }}</span><br /><span ng-click="deleteItem(item)">X</span></div>',
};
});
Im sure the answer is simple, but I just cant find it.
If you want to use a required controller function you need to inject it into the link function
in sidebar-item add
link : function (scope, element, attrs, sidebar) {
scope.deleteItem = function (item) {
sidebar.deleteItem(item);
}
}
Please forgive my lack of understanding.
I pass the name of a collection to my directive:
<ul tag-it tag-src="preview_data.preview.extract.keywords"><li>Tag 1</li><li>Tag 2</li></ul>
The directive is defined:
app.directive('tagIt', function (){
return {
restrict: 'A',
link: function(scope,elem, attr) {
elem.tagit();
console.log(attr.tagSrc); //the name of my collection, but how do I access it?
}
}
});
How do I access my collection from the directive and make sure my directive is called when the collection is populated? Here is how preview_data.preview.extract.keywords gets populated.
app.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
console.log('config');
$routeProvider.when("/", {
templateUrl: "/templates/addItem.html",
controller: "AddItemController",
resolve: {
loadData: addItemCtrl.loadData
}
});
});
var addItemCtrl=app.controller("AddItemController", function ($scope, $route, $sce, Preview) {
var title = decodeURIComponent($route.current.params.title);
var ua = decodeURIComponent($route.current.params.ua);
var uri = decodeURIComponent($route.current.params.uri);
$scope.preview_data = {
uri: uri,
title: title,
ua: ua
}
//pass parameters to web preview API
Preview.get(uri, ua, title).then(function (data) {
$scope.preview_data.preview = data;
if (data.embed.html) {
$scope.preview_data.preview.embed.html = $sce.trustAsHtml(data.embed.html);
}
}, function (data) {
alert('Error: no data returned')
});
});
You need to set the variable in the directive scope and set the template to iterate between the tags:
template : '<li data-ng-repeat="tag in tagSrc">{{tag.name}}</li>',
scope : {
tagSrc : '='
},
And will became this:
app.directive('tagIt', function (){
return {
restrict: 'A',
template : '<li data-ng-repeat="tag in tagSrc">{{tag.name}}</li>',
scope : {
tagSrc : '='
},
link: function(scope,elem, attr) {
console.log(attr.tagSrc);
}
}
});
the '=' attribute will tells to angular to use a tw way binding with the array passed in the directive declaration in the HTML.
Here is a plunker with a working example.
And here is a good arcticle explaning the directive's attributes and life cycle.
I hope it helps.
[EDIT]
If you want just iterate the array, without creating some different behavior in the list items, you can just simply use the ng-repeat directive:
<ul>
<li data-ng-repeat="tag in tags">{{tag.name}}</li>
<ul>