Angularjs: Can I have my directive run after controller API callback - javascript

first time asker. Apologies if my jargon isn't quite right I'm new to angularjs
I have a controller which gets a list of products with a HTTP call
contractManagementControllers.controller('PriceBandsCtrl', ['$scope', '$routeParams', '$http', '$location',
function ($scope, $routeParams, $http, $location)
{
$http.get('products').success(function (products)
{
$scope.productList = products
})
}
And a directive which I would like to have access to that product list.
contractManagementControllers.directive("priceBands",function($http)
{
return {
scope: true,
restrict: 'AE',
replace: 'true',
templateUrl: 'Partials/PriceBand.html',
link: function ($scope, ele, attrs, c)
{
// use $scope.productList
}
});
My issue is with the order in which things happen. The controller function runs first, followed by the directive link function, followed by the callback which sets the product list. As such $scope.productList is undefined in the directive link function and gives an error
Is there a way to force the link function to wait until the callback has completed?

Set default value to productList in order not get error about undefined variable
contractManagementControllers.controller('PriceBandsCtrl', ['$scope', '$routeParams', '$http', '$location',
function ($scope, $routeParams, $http, $location)
{
$scope.productList = [];
$http.get('products').success(function (products)
{
$scope.productList = products
})
}
and then watch for changes of the productList in directive:
contractManagementControllers.directive("priceBands",function($http)
{
return {
scope: true,
restrict: 'AE',
replace: 'true',
templateUrl: 'Partials/PriceBand.html',
link: function ($scope, ele, attrs, c)
{
$scope.watch('productList', function(newValue, oldValue) {
//Perform here if you need
});
}
});

no need of waiting for callback in angularjs. just put the $scope.productList=[]; in your controller as first line. it will not give undefined to directive.
In your directive link function just write the $watch function to watch changes in element.

Related

AngularJs communicating to sibling component not working

I am working on an old project where I use AngularJS 1.x and have trouble communicating to sibling scope
App.js
var myApp = angular.module('myApp', ['ui.router', 'headerModule', 'dashboardModule', 'profileData', 'roiData', 'eventData', 'LocalStorageModule']);
// Use Routes to Header Changes
myApp.run( ['$rootScope', '$state', '$stateParams', function ($rootScope, $state, $stateParams) {
$rootScope.$on('callViewEvent', function(ev, x){
console.log("here"); //getting printed
$rootScope.$broadcast('vv',x);
});
}]);
I have a directive
eventData.js
var eventData = angular.module('eventData', ['angularPayments', 'ngFileUpload']);
eventData.directive('eventData', function () {
return {
restrict: 'EA',
scope: {},
replace: true,
link: function ($scope, element, attributes) {
},
controller: function ($scope, $attrs, $http, $state, $rootScope, $window, $interval, Auth, Upload) {
$scope.$on('vv', function(e, x){
console.log("event called");
$scope.viewEvent(x);
});
},
templateUrl: 'directives/event/eventData.tpl.html'
}
});
A controller module
dashboardController.js
var dashboardModule = angular.module('dashboardModule', []);
dashboardModule.controller('dashboardController', function($scope, $rootScope, $http, Auth, $state ) {
$scope.childmethod = function(x) {
console.log("click"+x);
$scope.$emit("callViewEvent", x);
}
});
I am emitting event from child to parent and then brodcasting into another child. Emit is working but broadcast is not working. That is the second event call is working
Please try:
$rootScope.$on('vv', function(e, x){
console.log("event called");
$scope.viewEvent(x);
});
The component that listens for the event is either not being instantiated or it is instantiated after the event is dispatched.
For debugging purposes, add a console.log to the component:
app.directive('eventData', function () {
return {
restrict: 'EA',
scope: {},
replace: true,
link: function ($scope, element, attributes) {
},
controller: function ($scope, $attrs, $http, $state, $rootScope, $window, $interval, Auth, Upload) {
//DEBUG TIMING
console.log("eventData controller instantiated");
$scope.$on('vv', function(e, x){
console.log("event called");
$scope.viewEvent(x);
});
},
templateUrl: 'directives/event/eventData.tpl.html'
}
});
Be aware that the $scope event bus is deprecated. Using it will make migration to Angular 2+ more difficult.
For more information, see
AngularJS Developer Guide - Component-based Application Architecture

How to get controller scope from multi-level directives in AngularJS(without $parent)

How can I access controller scope from multi-level directives in below structure:
I create a directive that has multi-level scopes inside its.
1. Controller scope
1.2. Directive1 scope(main directive)
1.2.1. Directive2 scope
1.2.1.1 Directive3 scope
I want to get the controller scope from directive 3.
please don't refer to $parent because the parent level it's not certain and a user may use this directive inside another directive.(see below codes)
<div ng-controller="Test">
<custom-dir>
<dir1>
<dir2>
<dir3>
</dir3>
</dir2>
</dir1>
<custom-dir>
</div>
The users create a function in the Test controller and the function will be called in my Directive 3 scope(how to get controller scope?).
<div ng-controller="Test">
<dir1>
<dir2>
<dir3>
</dir3>
</dir2>
</dir1>
</div>
More details(please don't refer to syntax error):
The controller is:
App.controller('ScopeController', function ($scope, $rootScope, $uibModal, $http, $filter, $cookieStore, Common, $cookies) {
$scope.runTest = function () {
return `<input type='button' ng-click='testHtml()' value='Test'/>`;
}
$scope.testHtml = function () {
alert("work");
}
$scope.model=someModel;
$scope.config=someConfig;
$scope.columns={html: $scope.runTest};
});
the dir1 directive:
App.directive("dir1", function ($compile, $filter, $rootScope, $timeout, Common, $window, $http) {
return {
restrict: "E",
priority: 1,
terminal: false,
templateUrl: "Content/html/Table.html?version=2.6",
scope: {
model: "=",
columns: "=",
config: "=",
search: "#",
},
link: function (scope, elem, attrs) {
scope.CallFunc = function (html) {
if (typeof (html) =="function" )
return html();
else {
return scope.$parent.$eval(html + "()", {});
}
}
}
}
});
the dynamic directive compile the runTest output
App.directive('dynamic', function ($compile) {
return {
restrict: 'A',
replace:true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function (html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
});
If I change the line $compile(ele.contents())(scope); to $compile(ele.contents())(scope.$parent.$parent); it's work.
In this directive, I need get the controller scope without $parent because
some users may use this directive inside another directive same below:
<custom-dir>
<dir1 model="model" columns="columns" config="config">
<div dynamic="CallFunc(columns.html)"></div>
</dir1>
</custom-dir>
The using HTML tag
<dir1 model="model" columns="columns" config="config">
<div dynamic="CallFunc(columns.html)"></div>
</dir1>
This issue handle with following codes:
A service for storing the controller scope:
App.service('TableService', function () {
return {
MyScope: null
};
});
Inject the TableService to dynamic directive(this directive compiles dynamic content):
App.directive('dynamic', function ($compile,TableService) {
return {
restrict: 'A',
replace:true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function (html) {
ele.html(html);
$compile(ele.contents())(TableService.MyScope);
});
}
};
});
And finally in the controller:
App.controller('ScopeController', function ($scope, $rootScope, $uibModal,
$http, $filter, $cookieStore, Common, $cookies,TableService) {
TableService.myScope = $scope;
$scope.runTest = function () {
return `<input type='button' ng-click='testHtml()' value='Test'/>`;
}
$scope.testHtml = function () {
alert("work");
}
$scope.model=someModel;
$scope.config=someConfig;
$scope.columns={html: $scope.runTest};
});
After that, the dynamic directive can access controller scope and all dynamic events(like testHtml) will be called even if the directive put in another directive(without using the $parent).
thank you #shaunhusain, huan feng for giving me an idea.
In child controller do something like:
$scope.$broadcast('yourEvent');
In parent controller do the listener:
$scope.$on('yourEvent' , function(){
//Handle your logic
});
A special case service
.service('DirectDispatcher', function(){
return {
fireMe: angular.noop
}
});
First directive registers a function callback
.directive(
...
link:function(DirectDispatcher){
function myHandler() {window.alert('just testing')}
DirectDispatcher.fireMe = myHandler;
}
...
);
Second directive fires the function callback
.directive(
...
link:function(DirectDispatcher){
DirectDispatcher.fireMe();
}
...
);

how to sync $scope between a directive and controller in angular

I have a directive as the following:
app.directive('fileInput', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.bind('change', function () {
$parse(attrs.fileInput)
.assign(scope, element[0].files)
scope.$apply();
});
scope.$watch('files', function () {
console.log(scope.files);
});
},
controller: function ($scope, $element, $attrs) {
$element.bind('change', function () {
$parse($attrs.fileInput)
.assign($scope, $element[0].files)
$scope.$apply();
});
$scope.$watch('files', function () {
console.log($scope.files);
});
}
}
EDIT
and this is controller:
controllers.controller('RegisterCtrl', ['$scope', '$routeParams', '$location', '$http', 'Restangular', 'ServiceRepository',
function($scope, $routeParams, $location, $http, Restangular, ServiceRepository)
{
$scope.regService = function () {
$scope.error = {};
$scope.submitted = true;
var fd = new FormData();
fd.append("model", angular.toJson($scope.services));
console.log($scope.files);
}
}
And this is view file
<input type="file" id="boarding-picture_where_sleeping" class="form-control" file-input="files" multiple>
Additional info, regService() method is called when submitting the form
and when I debug $scope.files, it's available in console tab. but in my controller, it's undefined
so how to sync it between the directive and controller
Thanks
it's working now. the problem was caused I used 2 nested directives :)
Thanks guys
I would use scope and bindToController attributes in the directive definition, like in the following snippet from this blog:
app.directive('someDirective', function () {
return {
scope: {
name: '='
},
controller: function () {
this.name = 'Pascal';
},
controllerAs: 'ctrl',
bindToController: true,
template: '<div>{{ctrl.name}}</div>'
};
});
It requires use of the controllerAs syntax too, but you should be using that anyway as it is much more clear than passing $scope around everywhere and dealing with prototypical inheritance. This is recommended in John Papa's AngularJS Style Guide

Assigning data in link function in Angular

I am currently building an app. My view has been minified to this for presentation purposes:
<div class='reviews' ng-repeat="review in reviews">
<div><ratestar></ratestar></div>
</div>
I also have a directive that I built:
angular.module('LiveAPP.artist',[])
.controller('artistCtrl', ['$scope', '$http', '$location', 'dataFactory', '$routeParams', artistCtrl])
.directive("ratestar", function() {
return {
restrict: "E",
scope: {},
template: "<div id='rateYo'></div>",
link: function( scope, ele, attrs ) {
var $rateYo = $(ele).rateYo({
rating:***VARIES ACCORDING TO REVIEW***
});
}
};
});
function artistCtrl($scope, $http, $location, dataFactory, $routeParams){
$scope.reviews = [{num_of_stars:1},...,{num_of_stars:3}]
}
Essentialy I want the rating in rateYo({}) to be the num_of_stars from $scope.reviews. So when ng-repeat is run it takes each object from from $scope.reviews and assigns num_of_stars to rating. Anyone have any idea on how to tackle this? I had a feeling that adding class=$index to <ratestar></ratestar> might work, but it didn't.

Accessing parent controller in an Angular directive

I have directive that dynamically sets the header bar content for a given application state.
I would like to be able to access the functions and variables in the Controller of the current view, but I am only ever able to access my RootCtrl.
The directive looks like this.
return {
restrict: 'EA',
template: "<div ng-include='getState()'></div>",
transclude: true,
scope: false,
controller: ['$scope', '$state', function($scope, $state) {
//some logic to retrieve and return the correct header template html
}],
link: function(scope, element, attrs){
console.log(scope.test);
console.log(scope.test2);
}
}
And the controllers.
.controller('RootCtrl', function($scope, $state, $location, $rootScope) {
$scope.test = 'hello';
//...
})
.controller('ContactsCtrl', function($scope, $state, CustomerService) {
console.log('Contacts init');
$scope.test2 = 'hello 2';
//...
})
And when I navigate to the contacts state, the output looks like this.
hello
undefined
Contacts init
What should I do if I want to be able to access the test2 variable?
You will need to use the require property inside your directive.
This will make the scope of the defined controllers available inside the link function as 4th argument. You can access the scopes as an array inside the link function then.
Your code may look like:
return {
restrict: 'EA',
template: "<div ng-include='getState()'></div>",
transclude: true,
scope: false,
require:['^RootCtrl', '^ContactsCtrl'],
controller: ['$scope', '$state', function($scope, $state) {
//some logic to retrieve and return the correct header template html
}],
link: function(scope, element, attrs, requiredControllers){
console.log(requiredControllers[0].test);
console.log(requiredControllers[1].test2);
}
}
See the Angular documentation for Directives for some more examples (under the title Creating Directives that Communicate) and the explanation of the ^controller syntax.

Categories

Resources