Dynamically change custom directive attribute value which should change directive content - angularjs

Basically I have made custom directive to which i pass Url as string then inside the directive controller i'm calling http.get() method which creates the content inside this directive. What i want is to be able to change the value of annotation-url attribute which in return will change the content inside the directive because the new Url will return different JSON object. But it seems the directive is not getting refreshed when i change the annotationData from the controller.
HTML
<span ng-click="annotatonData = 'data/annotations1.json'">John</span>
<span ng-click="annotatonData = 'data/annotations2.json'">Marry</span>
<ng-annotate user-options="user" annotation-url="{{annotatonData}}"></ng-annotate>
Controller:
app.controller('ngAnnotationsController', ['$scope', '$http', function ($scope, $http) {
//InIt default http get
$scope.annotatonData = 'data/annotations1.json';
}]);
Directive:
app.directive('ngAnnotate', function () {
return {
templateUrl: 'templates/...',
restrict: 'E',
scope: {
annotationUrl: "#"
},
controller: ['$scope', '$http', function ($scope, $http) {
$http.get($scope.annotationUrl).then(function (response) {
$scope.data = response.data;
...
...

First, I suggest you change to
scope: {
annotationUrl: "="
}
the you can add a watcher to invoke $http whenever value is changed
$scope.$watch('annotationUrl', function(newVal, oldVal) {
if (newVal != oldVal) {
$http.get(newVal).then(function (response) {
$scope.data = response.data;
...
...
}
}
However, if you want to keep annotationUrl as it is, you need to use
$attr.$observe('annotationUrl', fn) to catch value changes.

Related

Change Directive variable inside a directive is not working

I am trying to change a controller variable inside a directive and this is my code:
the main controller is :
angular.module("app").controller('vehicleManagementController', ['$scope', 'toastr', '$filter' ,
function ($scope, toastr, $filter) {
.....
$scope.filteredDevices = //Some List
$scope.allDevices = [];
}
}]);
and the directive is :
angular.module('app').directive('advanceSearchDirective', ['deviceAdvancedSearchService', 'mapService', function (deviceAdvancedSearchService, mapService) {
return {
restrict: "E",
controller: 'myDirectiveController',
scope: { filteredDevices: '=filteredDevices' },
templateUrl: '/app/templates/advanceSearchDirective.html'
};
}]);
angular.module("app").controller(myDirectiveController( $scope) {
$scope.search = function() {
$scope.filteredDevices = [];
$scope.$apply();
}
});
the thing is it faild to run the apply() method through this error.
and here how i am using it :
<advance-search-directive filtered-devices="filteredDevices" model="$parent"></advance-search-directive>
I have access to $scope.filteredDevices inside the directive controller but when i change its value it doesn't change in the main controller. what am I doing wrong?
if you want to save the changes on the parent controller scope you should use
scope:false,
change the directive to :
return {
restrict: "E",
controller: 'myDirectiveController',
scope: false,
templateUrl: '/app/templates/advanceSearchDirective.html'
};
here is an useful article .

How to call custom directive in scope function of another controller

I am new to AngularJS. Can anyone tell me with example that how to call a custom directive in a scope function of another controller.
For example I have a controller with a function as follows:
angularApp.controller('sample_Ctrl', function ($scope, $http, $timeout, $rootScope, $location) {
$scope.showReport = function(id) {
};
});
I created a customDirective as follows:
var showModalDirective = function ($timeout) {
return {
restrict: 'E',
templateUrl: '/Partials/template1.html',
};
};
angularApp.directive('showModal', showModalDirective);
So how to call this directive in the showReport function, and how can I pass id to template URL?
You can't call directive in controller. It should be a service.
Or you can use directive in view:
Directive:
var showModalDirective = function ($timeout) {
return {
restrict: 'E',
scope: {
modalId: '='
},
templateUrl: '/Partials/template1.html',
};
};
angularApp.directive('showModal', showModalDirective);
Controller:
angularApp.controller('sample_Ctrl', function ($scope, $http, $timeout, $rootScope, $location) {
$scope.showModal = false;
$scope.showReport = function(id) {
$scope.showModal = true;
$scope.modalId = id;
};
});
View:
<div ng-if="showModal">
<show-modal modal-id="modalId"></show-modal>
</div>
showModal directive is call when showModal variable is true.
To use your directive first u need to create HTML element with same name as your directive then provide data for that directive from your controller. Suppose there is a div in your html page.
<div show-modal> </div>
Then on this page your template1.html will call internally through directive and suppose there is some html element in template1.html like
code in your controller -
angularApp.controller('sample_Ctrl',function() {
$scope.firstName= "My First Name"
})

Unable to call Angular directive method

I've got an Angular view thusly:
<div ng-include="'components/navbar/navbar.html'" class="ui centered grid" id="navbar" onload="setDropdown()"></div>
<div class="sixteen wide centered column full-height ui grid" style="margin-top:160px">
<!-- other stuff -->
<import-elements></import-elements>
</div>
This is controlled by UI-Router, which is assigning the controller, just FYI.
The controller for this view looks like this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
$scope.loadOfficialFrameworks();
// other stuff here
});
The <import-elements> directive, looks like this:
angular.module('pcfApp').directive('importElements', function($state, $stateParams, $timeout, $window, Framework, OfficialFramework) {
var link = function(scope, el, attrs) {
scope.loadOfficialFrameworks = function() {
OfficialFramework.query(function(data) {
scope.officialFrameworks = data;
$(".ui.dropdown").dropdown({
onChange: function(value, text, $item) {
loadSections($item.attr("data-id"));
}
});
window.setTimeout(function() {
$(".ui.dropdown").dropdown('set selected', data[0]._id);
}, 0);
});
}
return {
link: link,
replace: true,
templateUrl: "app/importElements/components/import_elements_component.html"
}
});
I was under the impression that I'd be able to call the directive's loadOfficialFrameworks() method from my controller in this way (since I'm not specifying isolate scope), but I'm getting a method undefined error on the controller. What am I missing here?
The problem is that your controller function runs before your link function runs, so loadOfficialFrameworks is not available yet when you try to call it.
Try this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
//this will fail because loadOfficialFrameworks doesn't exist yet.
//$scope.loadOfficialFrameworks();
//wait until the directive's link function adds loadOfficialFrameworks to $scope
var disconnectWatch = $scope.$watch('loadOfficialFrameworks', function (loadOfficialFrameworks) {
if (loadOfficialFrameworks !== undefined) {
disconnectWatch();
//execute the function now that we know it has finally been added to scope
$scope.loadOfficialFrameworks();
}
});
});
Here's a fiddle with this example in action: http://jsfiddle.net/81bcofgy/
The directive scope and controller scope are two differents object
you should use in CTRL
$scope.$broadcast('loadOfficialFrameworks_event');
//And in the directive
scope.$on('loadOfficialFrameworks_event', function(){
scope.loadOfficialFrameworks();
})

Load directive after AJAX call

I have build a directive for pagination that takes two arguments; the current page and the total number of pages.
<pagination page="page" number-of-pages="numberOfPages"></pagination>
The issue is that I will only know the value of numberOfPages after an AJAX call (through ng-resource). But my directive is already rendered before that the AJAX call is done.
app.controller('MyController', function ($scope, $routeParams) {
$scope.page = +$routeParams.page || 1,
$scope.numberOfPages = 23; // This will work just fine
MyResource.query({
"page": $scope.page,
"per_page": 100
}, function (response) {
//This won't work since the directive is already rendered
$scope.numberOfPages = response.meta.number_of_pages;
});
});
I prefer to wait with the rendering of my controllers template until the AJAX call is finished.
Plan B would be to append the template with the directives template when the AJAX call is done.
I'm stuck working out both scenarios.
But isn't it possible to just prevent the rendering until all is done
I think ng-if would do that, contrary to ng-show/ng-hide which just alter the actual display
You have to wait for the value using a $watch function like:
<div before-today="old" watch-me="numberOfPages" >{{exampleDate}}</div>
Directive
angular.module('myApp').directive('myPagingDirective', [
function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
scope.$watch(attr.watchMe,function(newValue,oldValue){
//check new value to be what you expect.
if (newValue){
// your code goes here
}
});
}
};
}
]);
Imporant: Your directive may use an isolated scope but even so the same principle stands.
If you use resolve from ui-router, you can have meta or meta.number_of_pages injected in your controller BEFORE it's view gets rendered.
//routes.js
angular.module('app')
.state('some_route', {
url: '/some_route',
controller: 'MyController',
resolve: {
meta: ['MyResource', function (MyResource) {
return MyResource.query({
"page": $scope.page,
"per_page": 100
}, function (response) {
return response.meta;
});
}]
}
});
//controllers.js
app.controller('MyController', function ($scope, $routeParams, meta) {
$scope.page = +$routeParams.page || 1,
$scope.numberOfPages = meta.number_of_pages;
});

AngularJS accessing an attribute in a directive from a controller

I need to pass an Id defined in the directive to the associated controller such that it can be used in a HTTP Get to retrieve some data.
The code works correctly in its current state however when trying to bind the Id dynamically, as shown in other questions, the 'undefined' error occurs.
The Id needs to be defined with the directive in HTML to meet a requirement. Code follows;
Container.html
<div ng-controller="IntroSlideController as intro">
<div intro-slide slide-id="{54BCE6D9-8710-45DD-A6E4-620563364C17}"></div>
</div>
eLearning.js
var app = angular.module('eLearning', ['ngSanitize']);
app.controller('IntroSlideController', ['$http', function ($http, $scope, $attrs) {
var eLearning = this;
this.Slide = [];
var introSlideId = '{54BCE6D9-8710-45DD-A6E4-620563364C17}'; //Id to replace
$http.get('/api/IntroSlide/getslide/', { params: { id: introSlideId } }).success(function (data) {
eLearning.Slide = data;
});
}])
.directive('introSlide', function () {
return {
restrict: 'EA',
templateUrl: '/Modules/IntroSlide.html',
controller: 'IntroSlideController',
link: function (scope, el, attrs, ctrl) {
console.log(attrs.slideId); //Id from html present here
}
};
});
Instead of defining a controller div that wraps around a directive, a more appropriate approach is to define a controller within the directive itself. Also, by defining an isolated scope for your directive, that slide-id will be available for use automatically within directive's controller (since Angular will inject $scope values for you):
.directive('introSlide', function () {
// here we define directive as 'A' (attribute only)
// and 'slideId' in a scope which links to 'slide-id' in HTML
return {
restrict: 'A',
scope: {
slideId: '#'
},
templateUrl: '/Modules/IntroSlide.html',
controller: function ($http, $scope, $attrs) {
var eLearning = this;
this.Slide = [];
// now $scope.slideId is available for use
$http.get('/api/IntroSlide/getslide/', { params: { id: $scope.slideId } }).success(function (data) {
eLearning.Slide = data;
});
}
};
});
Now your HTML is free from wrapping div:
<div intro-slide slide-id="{54BCE6D9-8710-45DD-A6E4-620563364C17}"></div>
In your IntroSlide.html, you probably have references that look like intro.* (since your original code use intro as a reference to controller's $scope). You will probably need to remove the intro. prefix to get this working.
Require your controller inside your directive, like this:
app.directive( 'directiveOne', function () {
return {
controller: 'MyCtrl',
link: function(scope, el, attr, ctrl){
ctrl.myMethodToUpdateSomething();//use this to send/get some msg from your controller
}
};
});

Resources