Calling a init function when a directive is loaded - javascript

I have a simple directive that loads comments from a service(commentsService):
'use strict';
angular.module('mean.rank')
.directive('commentList', function(CommentsService, Global) {
return {
restrict: 'E',
templateUrl: 'rank/views/comments-list.html',
replace: false,
link: function($scope, element, attrs) {
//accessing the ad info : console.log("ADD " , $scope.ad);
$scope.comments = [];
$scope.loadComments = function () {
console.log("in loadComments");
var adID = {};
adID.ad_ID = $scope.ad.nid;
console.log("calling services.getComments function with " , adID.ad_ID);
CommentsService.getComments(adID.ad_ID)
.then(function (response) {
angular.forEach(comment in response)
$scope.comments.push(comment);
});
};
}
}
})
The loaded comments should be loaded to the list(inside the templateUrl) using ng-init and then the service for loading (I will add the code if needed).
<div ng-init="loadComments()">
<ul class="media-list comment-detail-list" >
<li class="media" ng-repeat="comment in comments" >
<article>
<div class="pull-left comment-info">
{{ comment.author }}<br />
<time>{{ comment.datePublished | date:"MM/dd/yyyy" }}</time>
</div>
<div class="media-body">
<div class="comment-body">
<p>{{ comment.comment }}</p>
</div>
</div>
</article>
</li>
<li>Debugger</li>
</ul>
</div>
The directive has in its scope the loadCommets() function but it is not triggered.
Thank's for your help!

I'd suggest putting the function call inside the link function itself instead of ng-init.
angular.module('mean.rank')
.directive('commentList', function(CommentsService, Global) {
return {
...
link: function($scope, element, attrs) {
//accessing the ad info : console.log("ADD " , $scope.ad);
$scope.comments = [];
$scope.loadComments = function () {
...
};
$scope.loadComments();
}
}
})
Edit: by the way your forEach syntax is wrong. It should be
angular.forEach(response, function(comment){
$scope.comments.push(comment);
});

Related

Set ng-click attribute value from a string variable

I have an external JSON file which is a collection of buttons/actions like this
{
"foo1":{
"name":"productDetails",
"displayAs":"button",
"action":"viewDetails()"
},
"foo2":{
"name":"sell",
"displayAs":"button",
"action":"sellProduct()"
}
}
Later, in my view, I create a <div> using ng-repeat for each object contained in that JSON.
My question is, can I set the ng-click property as it it in the JSON file like this?
<li ng-repeat = "field in fields">
<div ng-click = field.action > {{field.name}} </div>
</li>
you can do this using ng-bind-html.
<li ng-repeat = "field in fields">
<div bind-html-compile ng-bind-html="trust(field)" > </div>
</li>
Add bind-html-compile directive to compile the DOM so that changes will effect to DOM elements
Directive
.directive('bindHtmlCompile', ['$compile', function($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$watch(function() {
return scope.$eval(attrs.bindHtmlCompile);
}, function(value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
In trust function pass the object as an argument and return the html trust element
$scope.trust = function(html) {
return $sce.trustAsHtml('<div ng-click = "' + html.action + '" > {{field.name}} </div>');
}
Demo
angular.module("app",[])
.controller("ctrl",function($scope,$sce){
$scope.viewDetails = function(){console.log("viewDetails")}
$scope.sellProduct = function(){console.log("sellProduct")}
$scope.fields = {
"foo1":{
"name":"productDetails",
"displayAs":"button",
"action":"viewDetails()"
},
"foo2":{
"name":"sell",
"displayAs":"button",
"action":"sellProduct()"
}
}
$scope.trust = function(html){
return $sce.trustAsHtml('<div ng-click = "'+html.action+'" > {{field.name}} </div>');
}
}).directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value);
$compile(element.contents())(scope);
});
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<li ng-repeat = "field in fields">
<div bind-html-compile ng-bind-html="trust(field)" > </div>
</li>
</div>

In Angular 1.x how can I access controller methods from a custom directive?

I am currently trying to figure out the best way to access two controller methods from within my custom directive. My current code looks like so:
Parent Component Template (navMenus.html):
<menu-toggle section="navItem" ng-if="navItem.type === 'toggle'"></menu-toggle>
Parent Component Controller (navMenus.controller.js):
...
isOpen(section) {
return this.NavMenusFactory.isSectionSelected(section);
}
toggleOpen(section) {
this.NavMenusFactory.toggleSelectSection(section);
}
...
Directive Template (menuToggle.html):
<md-button class="md-button-toggle" ng-click="vm.toggle()">
{{ section.text | translate }}
</md-button>
<ul ng-show="vm.isOpen()" class="menu-toggle-list">
<li ng-repeat="subItem in section.subItems">
{{ subItem.text | translate }}
<menu-link section="subItem"></menu-link>
</li>
</ul>
Directive (menuToggle.directive.js):
...
return {
restrict: 'AE',
template,
replace: true,
scope: {
section: '=',
},
link(scope, element) {
$timeout(() => {
const $element = element;
scope.vm.toggle = function () {
console.log(scope.$parent.isOpen());
};
scope.isOpen = function () {
return $element.isOpen(scope.section);
};
scope.toggle = function () {
$element.toggleOpen(scope.section);
};
});
}
}
How can I access these methods?
Since there is scope in your directive, you have created an isolated scope.
So in order order to access the controller's function you can make use of events
Inside directive, create an $emit event, on the click event:
scope.toggle = function toggle (){
scope.$emit('EVENT_NAME', { data: true }) // here data is optional
}
In controller, you can perform action as:
$scope.$on('EVENT_NAME', function(event, data){
// here you can call the controller's method
})
This is the best way i know, to call a controller function from directive.
/* Directive template */
<md-button class="md-button-toggle" ng-click="vm.toggle()">
{{ section.text | translate }}
</md-button>
<ul ng-show="isOpen()" class="menu-toggle-list">
<li ng-repeat="subItem in section.subItems">
{{ subItem.text | translate }}
<menu-link section="subItem"></menu-link>
</li>
</ul>
/* Directive Code */
...
return {
restrict: 'AE',
template,
replace: true,
scope: {
section: '=',
isOpen: '&'
},
link(scope, element) {
$timeout(() => {
const $element = element;
scope.vm.toggle = function () {
console.log(scope.$parent.isOpen());
};
scope.isOpen = function () {
return $element.isOpen(scope.section);
};
scope.toggle = function () {
$element.toggleOpen(scope.section);
};
});
}
}
/* in your view, you can call controller fn */
<menu-toggle section="navItem" is-open="isOpen()" ng-if="navItem.type === 'toggle'"></menu-toggle>

Why am I getting an angular unknown provider injector error when passing an array to a modal?

I keep getting this error when trying to open a modal that takes an array as a parameter: https://docs.angularjs.org/error/$injector/unpr?p0=listItemsProvider%20%3C-%20listItems%20%3C-%20SingleActionListModalCtrl
Here's the controller:
controller('SingleActionListModalCtrl', ['$scope', '$uibModalInstance', 'modalTitle', 'modalText', 'listItems', 'cancelBtnCaption', 'cancelBtnFunction',
function ($scope, $uibModalInstance, modalTitle, modalText, listItems, cancelBtnCaption, cancelBtnFunction) {
$scope.modalTitle = modalTitle;
$scope.modalText = modalText;
$scope.list = listItems;
$scope.cancelBtnCaption = cancelBtnCaption;
$scope.cancel = function () {
cancelBtnFunction();
$uibModalInstance.close();
};
}]).
Here's the view for the modal:
<div>
<script type="text/ng-template" id="EventFooterSingleActionListModal">
<div class="modal-header">
<h3 class="modal-title">{{ modalTitle }}</h3>
</div>
<div class="modal-body">
{{ modalText }}
<ul>
<li ng-repeat="listItem in list track by $index">{{ listItem }}</li>
</ul>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="cancel()">{{ cancelBtnCaption }}</button>
</div>
</script>
</div>
Here's the function definition:
ModalFactory.OpenSingleActionListModal = function (modalName, title, msg, listItems, cancelCaption, cancelFunction) {
var modalInstance = $uibModal.open({
animation: true,
templateUrl: modalName,
controller: 'SingleActionListModalCtrl',
size: null,
resolve: {
modalTitle: function () {
return title;
},
modalText: function () {
return msg;
},
list: function() {
return listItems;
},
cancelBtnCaption: function () {
return cancelCaption;
},
cancelBtnFunction: function () {
return cancelFunction;
}
}
});
}
And here's the function call:
ModalFactory.OpenSingleActionListModal('EventFooterSingleActionListModal',
'Modal title', 'Modal text',
['list item 1', 'list item 2'],
'Close', function () { });
In your controller, you named it listItems, in your resolve it's list

ng-click works only once inside ng-repeat

I am new to Angular and struggling with ng-click inside ng-repeat. For some reason, it only fires once, and the following clicks throw the following error:
Controller:
(function ( window, angular ) {
'use strict'
var LeadsController = function ( $scope, $http, $cookies, breadcrumbFactory ) {
$scope.leads = []
$scope.activeLead = undefined
$scope.breadcrumb = breadcrumbFactory
$scope.getLeads = function () {
$http({
method: 'GET',
url: '/admin/leads'
})
.success( function getLeadsSuccess ( data ) {
$scope.leads = data.data
$scope.activeLead = $scope.leads[0]
})
.error( function getLeadsError ( err ) {
console.error( err )
})
}
$scope.activateLead = function () {
$scope.activeLead = this.lead
}
$scope.getLeads()
}
angular
.module( 'app' )
.controller( 'LeadsController', [
'$scope',
'$http',
'$cookies',
'breadcrumbFactory',
LeadsController
])
})( window, angular );
HTML:
<ul class="list list--dual-line" id="leads-list">
<li ng-repeat="lead in leads">
<a ng-click="activateLead()">
<p>{{ lead.firstName }} {{ lead.lastName }}</p>
<small>{{ lead.email }}</small>
</a>
</li>
</ul>
In this code you're replacing the function activateLead with the lead that's being clicked:
$scope.activateLead = function ( lead ) {
debugger
$scope.activateLead = lead
}
Not sure what this line is supposed to do but this is the problem:
$scope.activateLead = lead
Controller:
$scope.activateLead = function(lead) {
$scope.activeLead = lead;
};
HTML:
<ul class="list list--dual-line" id="leads-list">
<li ng-repeat="lead in leads">
<a ng-click="activateLead(lead)">
<p>{{ lead.firstName }} {{ lead.lastName }}</p>
<small>{{ lead.email }}</small>
</a>
</li>
</ul>
Verify function:
$scope.activateLead = function ( lead ) {
debugger
$scope.activeLead = lead
}
the variable name is wrong!

Angular uibModal, Resolve, Unknown Provider

I am trying to expose a "generic" modal - using Angular's $uibModal - through a service. Here is the definition of that service:
angular.module('app').service('CustomModalService', ['$uibModal', function ($uibModal) {
var openCustomModal = function (size, title, message) {
var actionToPerformOnConfirm = action;
var modalInstance = $uibModal.open({
templateUrl : 'templates/CustomModal.html',
size: size,
resolve: {
title: title,
message: message
}
});
};
return {
openCustomModal: openCustomModal
};
}]);
Nothing too complicated, above. However, it is not working. If I remove the resolve property from the object, the service works; however, if I include the resolve property, I get the Unknown Provider error originating from that property.
The documentation for the resolve property reads:
(Type: Object) - Members that will be resolved and passed to the
controller as locals; it is equivalent of the resolve property in the
router.
The objective is to be able to provide a template for the modal that utilizes these properties in its DOM, e.g. :
<div ng-controller="CustomModalController">
<div class="modal-header">
<h3 class="modal-title">{{title}}</h3>
</div>
<div class="modal-body">
{{message}}
</div>
<div class="modal-footer">
<button class="ad-button ad-blue" type="button" ng-click="confirmAction()"></button>
<button class="ad-button ad-blue" type="button" ng-click="cancelAction()"></button>
</div>
</div>
What am I missing that is causing this error to be thrown?
You have two problems:
You need to define the controller in your modal config
Your resolve object needs to be a map of string: function, where string is the name of the dependency that will be injected into your modal's controller, and function is a factory function that will be used to provide that dependency when the controller is instantiated.
Working example: JSFiddle
JavaScript
angular.module('myApp', ['ui.bootstrap'])
.controller('MyModalController', MyModalController)
.directive('modalTrigger', modalTriggerDirective)
.factory('$myModal', myModalFactory)
;
function MyModalController($uibModalInstance, items) {
var vm = this;
vm.content = items;
vm.confirm = $uibModalInstance.close;
vm.cancel = $uibModalInstance.dismiss;
};
function modalTriggerDirective($myModal) {
function postLink(scope, iElement, iAttrs) {
function onClick() {
var size = scope.$eval(iAttrs.size) || 'lg'; // default to large size
var title = scope.$eval(iAttrs.title) || 'Default Title';
var message = scope.$eval(iAttrs.message) || 'Default Message';
$myModal.open(size, title, message);
}
iElement.on('click', onClick);
scope.$on('$destroy', function() {
iElement.off('click', onClick);
});
}
return {
link: postLink
};
}
function myModalFactory($uibModal) {
var open = function (size, title, message) {
return $uibModal.open({
controller: 'MyModalController',
controllerAs: 'vm',
templateUrl : 'templates/CustomModal.html',
size: size,
resolve: {
items: function() {
return {
title: title,
message: message
};
}
}
});
};
return {
open: open
};
}
HTML
<script type="text/ng-template" id="templates/CustomModal.html">
<div class="modal-header">
<h3 class="modal-title">{{vm.content.title}}</h3>
</div>
<div class="modal-body">
{{vm.content.message}}
</div>
<div class="modal-footer">
<button class="ad-button ad-blue" type="button" ng-click="vm.confirm()">
confirm
</button>
<button class="ad-button ad-blue" type="button" ng-click="vm.cancel()">
cancel
</button>
</div>
</script>
<button modal-trigger size="'sm'" title="'Hello World!'" message="'This is a test'">
Click Me
</button>

Categories