I have a custom directive hello
myapp.directive('hello', function() {
return {
link : function (scope, elem, attr) {
scope.url = attr.url;
console.log(scope.url);
},
template: '<div ng-include="\'[{url}]\'"></div>'
};
});
my html code is
<div url="mypage.html"></div>
I get the value mypage.html in my console.log but is not able to include the page it says
http://127.0.0.1:5000/[%7Burl%7D]
I think you need to sanitize the html before binding it to ng-include, but below is the alternate solution to your problem you are facing
app.directive('widget', function(){
return {
scope: {
url: '#'
},
template: '<div ng-include="getTemplateUrl()"></div>',
controller: function($scope) {
$scope.getTemplateUrl = function() {
console.log($scope.url)
return $scope.url
}
}
}
})
HTML Code
<div url="mypage.html"></div>
Related
I have a directive similar to this this:
app.directive('example', function() {
return {
restrict: 'E',
transclude: true,
scope: {
callback: '&'
},
template: '<span ng-click="example.callback()">Click Me</span>',
bindToController: true,
controllerAs: 'example',
controller: function() {
this.counter = 0;
this.incrementCount = function() {
this.counter++;
};
this.getCount = function() {
return this.counter;
};
},
link: function(scope, el, attrs, ctrl) {
var oldCallback = scope.callback;
ctrl.callback = function() {
console.log(ctrl);
return oldCallback.call(ctrl); // I want to be able to use `this` as the controller to access the API from within the callback
};
}
};
});
with a controller
app.controller("ctrl", ["$scope", function(s) {
s.callback = function() {
this.incrementCount();
console.log("Value: " + this.getCount());
};
}]);
And view
<div ng-app="app">
<div class="container" ng-controller="ctrl">
<example callback="callback()"></example>
</div>
</div>
(codepen)
When I log ctrl in within the ctrl.callback in the link function it logs the example controller as I expect but when oldCallback is called, it doesn't get ctrl rebound to this as I want. Is there any way to access the API defined in the directive's controller from within the callback on the scope while still using an isolate scope for the directive?
You could pass the directives controller out through the callback. e.g.
example html
<span ng-click="example.callback({$exampleCtrl:example})">Click Me</span>
index html
<example callback="callback($exampleCtrl)"></example>
controller
$scope.callback = function($exampleCtrl) {
$exampleCtrl.incrementCount();
console.log("Value: " + $exampleCtrl.getCount());
};
http://codepen.io/anon/pen/BzqqzV
Also note that bindToController is only supported in AngularJs 1.3+ and your code pen was using 1.2
I have a directive which loads a image data template.
The problem is that It doesn't update the image date after the service which retrieve the img information is called.
This is my code:
Controller method:
$scope.watchImage = function(file_id){
FileService.getFile(file_id)
.then(
function(data){
if(data.file){
$scope.img = data.file;
console.log('Service called');
}
}
);
}
Directive:
app.directive('imageDetails', function() {
return {
scope: {
img: '='
},
restrict: 'E',
link: function($scope, element, attrs){
$scope.$watch(function() {
return $scope.img;
}, function() {
console.log($scope.img);
});
},
template: 'IMG: {img}'
};
});
HTML:
<div class="ui container">
<h2 class="ui dividing header">Images</h2>
</div>
<div ng-view></div>
<image-details img="img"></image-details>
</div>
Log result:
undefined
Service called
Any idea how to solve it ?
Thanks!
First of all, thank you to everyone for your replies. All of them help me in the solution.
Finally this is my working code.
Directive:
app.directive('imageDetails', function() {
return {
scope: {
img: '='
},
restrict: 'E',
template: 'IMG: {{img}}'
};
});
And I added the directive to my template (I was adding it outside ngview).
you have some mistake in template and in link function.
var app = angular.module('myApp', []);
app.controller('mainCtrl', function ($scope) {
$scope.img = {id: 1, title: "avatar.jpeg", slug: "avatar.jpeg", filesize: 24875, created_at: "2016-03-10 11:44:59"};
})
app.directive('imageDetails', function() {
return {
scope: {
img: '='
},
restrict: 'E',
link: function(scope, element, attrs){
scope.$evalAsync(function() {
return scope.img;
});
},
template: 'IMG: {{img}}'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="mainCtrl">
<image-details img="img"></image-details>
</div>
I think your directive should be Like :
app.directive('imageDetails', function() {
return {
scope: {
img: '='
},
restrict: 'E',
link: function(scope, element, attrs){
scope.$watch('img',function(image) {
return image;
}, function() {
console.log(image);
});
},
template: 'IMG: {img}'
};
});
First of all use a controller instead of link function because you don't need that. Link function is deprecated for simple components like this in angular 1.5.
Then, for using $watch, you need to specify what variable you want to watch, and only after what to do when it's change.
$watch('varToWatch', function(newValue) {...});
That said, if you use a controller instead of the link function, you probably use also a "Controller as" syntax. When you use it, you need to specify the "view name" of the variable you want to watch. For example:
app.directive('imageDetails', function() {
return {
scope: {
img: '='
},
restrict: 'E',
controllerAs: '$ctrl',
controller: function($scope){
$scope.$watch('$ctrl.img', function(newVal) {
console.log(newVal);
// if you want you can assign new value to your variable
// $scope.img = newVal;
});
},
template: 'IMG: {img}'
};
});
Try that and tell me if it's works for you ;)
This is a clear case of when the scope is affected outside the module. For those cases the lifecycle will not do the digest of the scope as you will expect.
You have to manually $digest or $apply when you want to notify your app that the scope have changed inside your directive
I'm just trying to do a simple directive, but for some reason the 2 way data binding isn't working in my directive. From my code you can see that a console log in the directive that will read the correct information I have in the $scope.displayMaintenance variable, but I can't change it in my directive.
HTML:
<maintenance-banner display-maintenance="displayMaintenance"></maintenance-banner>
Controller:
$scope.displayMaintenance = false;
$scope.$watch('displayMaintenance', function(data) {
console.log("i changed!: " + data);
});
Directive:
.directive('maintenanceBanner', function() {
return {
restrict: 'E',
replace: true,
scope: {
displayMaintenance: '='
},
templateUrl: '/partials/navbar/maintenance-banner.html',
link: function(scope) {
console.log(scope.displayMaintenance);
scope.displayMaintenance = true;
}
};
})
Any suggestions?
The issue may be that you use your directive inside another isolated scope.
I have created a sample: http://jsfiddle.net/2063n7te/
changing the model value using assignment replaces the model object which may not be reflected in the parent scope.
in short: do not bind primitives directly to the scope.
instead of
$scope.text = "foo";
use
$scope.input = {
text: "foo"
};
a good read is: http://www.thinkingmedia.ca/2015/01/learn-how-to-use-scopes-properly-in-angularjs/
point #4 applies specifically to the behaviour you are seeing.
Check that your directive can find the template URL.
Works for me:
var app = angular.module('app',[]);
app.controller('ctrl', function($scope) {
$scope.displayMaintenance = false;
$scope.$watch('displayMaintenance', function(data) {
alert("i changed!: " + data);
});
});
app.directive('maintenanceBanner', function() {
return {
restrict: 'E',
replace: true,
scope: {
displayMaintenance: '='
},
template: '<div>{{displayMaintenance}}</div>',
link: function(scope) {
console.log(scope.displayMaintenance);
scope.displayMaintenance = true;
}
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<maintenance-banner display-maintenance="displayMaintenance"></maintenance-banner>
</div>
In documentation I can read next for the require option:
When a directive uses this option, $compile will throw an error
unless the specified controller is found. The ^ prefix means that this
directive searches for the controller on its parents (without the ^
prefix, the directive would look for the controller on just its own
element).
So I try to use it:
<div ng-sparkline></div>
app.directive('ngCity', function() {
return {
controller: function($scope) {}
}
});
app.directive('ngSparkline', function() {
return {
restrict: 'A',
require: '^ngCity',
scope: {},
template: '<div class="sparkline"><h4>Weather </h4></div>',
link: function(scope, iElement, iAttrs) {
// get weather details
}
}
});
But I have an error if my html have not ng-city attribute, so if I need controller of another directive - need to add exactly same attribute in html, but why (<div ng-sparkline ng-city="San Francisco"></div>)? And it looks on another directive's controller with this name (directive!!!) but not at controller with this name, is that true? Thanks. Just want to make it clear
With require you can get the controller of another (cooperating) directive. The controller in Angular is not semantically a function, but an object constructor, i.e. called essentially as var c = new Controller() (this is a simplification for the sake of clarity). Since the controller is an object, it can have properties and methods. By requiring the controller of another directive, you gain access to those properties/methods. Modifying your example to demonstrate:
app.directive('ngCity', function() {
return {
controller: function($scope) {
this.doSomething = function() {
...
};
}
}
});
app.directive('ngSparkline', function() {
return {
restrict: 'A',
require: '^ngCity',
scope: {},
template: '<div class="sparkline"><h4>Weather </h4></div>',
link: function(scope, iElement, iAttrs, ngCityController) {
// use the controller, e.g.
ngCityController.doSomething();
}
}
});
In your case, the city would be a property of the controller of the ngCity directive, exposed as a property. It will be read by the ngSparkline to know for which city the graph is about.
<b> added directives.js</b>
<code>
app.directive('ngSparkline', function () {
return {
restrict: 'A',
require: '^ngCity',
scope: {
ngCity: '#'
},
templateUrl: '/scripts/templates/tpl.html',
controller: ['$scope', '$http', function ($scope, $http) {
var url = "https://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=imperial&cnt=7&callback=JSON_CALLBACK&q=";
console.log(url + $scope.ngCity);
$scope.showTemp = function () {
$scope.getTemp($scope.ngCity);
};
$scope.getTemp = function (city) {
$http({
method: 'JSONP',
url: url + city
}).success(function(data) {
var weather = [];
angular.forEach(data.list, function(value){
weather.push(value);
});
$scope.weather = weather;
});
}
}],
link: function (scope, iElement, iAttrs, ctrl) {
scope.getTemp(iAttrs.ngCity);
scope.$watch('weather', function (newVal) {
if (newVal) {
var highs = [];
angular.forEach(scope.weather, function (value) {
highs.push(value.temp.max);
});
//chartGraph(iElement, highs, iAttrs);
}
});
}
}
}).directive('ngCity', function () {
return {
controller: function ($scope) {
//console.log("hello");
}
}
});
</code>
<b> and added tpl.htm</b>
<code>
<div class="sparkline">
<input type="text" data-ng-model="ngCity">
<button ng-click="showTemp()" class="btn1">Check {{ngCity}}</button>
<div style="color:#2743EF">{{weather}} ÂșC</div>
<div class="graph"></div>
</div>
</code>
I'm trying to acheive databinding to a value returned from a service inside a directive.
I have it working, but I'm jumping through hoops, and I suspect there's a better way.
For example:
<img my-avatar>
Which is a directive synonymous to:
<img src="{{user.avatarUrl}}" class="avatar">
Where user is:
$scope.user = CurrentUserService.getCurrentUser();
Here's the directive I'm using to get this to work:
.directive('myAvatar', function(CurrentUser) {
return {
link: function(scope, elm, attrs) {
scope.user = CurrentUser.getCurrentUser();
// Use a function to watch if the username changes,
// since it appears that $scope.$watch('user.username') isn't working
var watchUserName = function(scope) {
return scope.user.username;
};
scope.$watch(watchUserName, function (newUserName,oldUserName, scope) {
elm.attr('src',CurrentUser.getCurrentUser().avatarUrl);
}, true);
elm.attr('class','avatar');
}
};
Is there a more succinct, 'angular' way to achieve the same outcome?
How about this ? plunker
The main idea of your directive is like
.directive('myAvatar', function (CurrentUserService) {
"use strict";
return {
restrict: 'A',
replace: true,
template: '<img class="avatar" ng-src="{{url}}" alt="{{url}}" title="{{url}}"> ',
controller: function ($scope, CurrentUserService) {
$scope.url = CurrentUserService.getCurrentUser().avatarUrl;
}
};
});