Change asynchronously coming data in angular direcitve - javascript

I have directive:
angular.module('mymodule').directive('someName', ['$http', function($http) {
return {
restrict: 'AE',
scope: {
headerId: '=header',
data: '=data'
},
templateUrl: 'path/to/template.html',
compile: function() {
return function($scope, $link, $attr) {
console.log($scope.headerId); // print actual header id
console.log($scope.data); // undefined
/* ... code ... */
The point is header is hardcoded as number in directive attribute. And everything fine with it. But data is got from server with angular service asynchronously. So at the moment when compile function runs there is no data in $scope. My problem is that I want to change data so that it will be readable by directive template.
How can I do it?

Add watcher to data
scope.$watch('data', function() {...})

Related

Angular: Variable template inside directive

In my Angular template I use an attributive directive as follows:
HTML:
<div id="my-template-one" my-template-directive></div>
JS:
// ...
.directive('myTemplateDirective', ['myconfig', function (myconfig) {
return {
templateUrl: myconfig.TEMPLATE_PATH + 'my-template-one.html',
controller: function ($scope, $rootScope) {
// code
},
controllerAs: 'dir'
}
}]);
For including another template, my-template-two.html, on another page, I would like to use the same directive. I do not want to duplicate the directive. How can I pass the template as an variable?
HTML on another page:
<div id="my-template-two" my-template-directive></div>
My goal is that somehow I can tell my directive to render my-template-two.html when this HTML is called.
The templateUrl property value may be a function which takes two arguments tElement and tAttrs and returns a string value:
app.directive('myTemplateDirective', ['myconfig', function (myconfig) {
return {
templateUrl: function (tElem, tAttrs) {
var template = "my-template-one.html";
if (tAttrs.use) {
template = tAttrs.use;
};
return myconfig.TEMPLATE_PATH + template;
},
controller: function ($scope, $rootScope) {
// code
},
controllerAs: 'dir'
}
}]);
Usage
<div my-template-directive use="my-template-two.html"> </div>
For more information, see AngularJS Comprehensive Directive API -- template

angular directive load after got variables

I have an angular directive that loads data from service,
BUT
it loads the data with a variable he go from a controller that it was loaded as well from a service.
code:
directive:
app.directive("posts", ['Posts', function(Posts) {
return {
restrict: 'E',
template: '' +
'<div ng-repeat="post in posts"></div>',
scope: {
showLoading: '&',
hideLoading: '&',
spot: '#'
},
controller: ['$scope', function ($scope) {
}],
link: function(scope, element, attrs) {
$scope.load = function () {
Posts.loadPostsBySpot(scope.spot)
};
}
};
}]);
Controller
app.controller('spotPageController', ['$scope', 'Spots', function ($scope, $Spots) {
doit = function () {
Spots.getSpot($)
.success(function (data) {
$scope.spotId = data.data;
console.log($scope.spot);
}).error(function (data) {
console.log('error');
});
};
}]);
html inside
<posts spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
but when the directive is loaded the "spot" is not yet set,
so how do I make the directive load only after the spot is set.
Use ng-if.
<posts ng-if="spotId" spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
This element will be rendered only after the spotId is initialized. Therefore, your directive will not be called before that.
If you want to encapsulate this behavior in directive, you should watch for changes of the scopeId. See the fiddle.

Angular custom directive: a strange error occurs when set controller attribute to `function(scope){}`

When I wrote a custom directive, a strange error blocks me.
angular.module('app.directives', [])
.directive('cyMenu', ['RecursionHelper', function(RecursionHelper) {
function postLink(){};
return {
restrict: 'E',
templateUrl: 'views/component/cy-menu.html',
replace: true,
transclude: false,
require: '?^cyMenu',
controller: function ($scope) { // when set this argument($scope) to scope, error occurs.
this.getList = function() {
return $scope.list;
}
},
scope: {
list: '=',
isSubmenu: '#'
},
compile: function(tElement) {
return RecursionHelper.compile(tElement, postLink);
}
}
}
As I pointed out(see comment), when I set controller attribute to controller: function (scope) {}, the error occurs:
Error: [$injector:unpr] Unknown provider: scopeProvider <- scope
http://errors.angularjs.org/1.3.6/$injector/unpr?p0=scopeProvider%20%3C-%20scope
...
I don't know why. Any help will be appreciated.
update
Here is angular's official demo, it looks similar to my directivehttps://code.angularjs.org/1.3.6/docs/guide/directive:
angular.module('docsTabsExample', [])
.directive('myTabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope) {
...
},
templateUrl: 'my-tabs.html'
};
})
A very good practice is to separate every component in a separate file, so I would start by putting your controller in a file like that :
//File recursionHelperController.js
(function() {
'use strict';
angular
.module('app.controllers')
.controller('RecursionHelperController', RecursionHelperController);
RecursionHelperController.$inject = ['$scope'];
function RecursionHelperController($scope) {
//do your stuff
}
})();
Note that I gave you the most correct format I know for a controller or any angular element in general, but you can do something simpler like:
angular.module('app.controllers')
.controller('RecursionHelperController', ['$scope', function($scope) {
//do your stuff
}])
You can then call this controller in your main file :
controller: 'RecursionHelperController'
Hope it helps
Update :
Sometimes the automatic injection has some troubles that's why I recommend doing it this way with an explicit injection. The angular doc only shows the simplest way for clarity and tutorial purposes
Update 2
If you don't want to separate the controller, try using the injection safe notation
controller : ['$scope', function($scope) {
//do your stuff
}])

Creating an angular directive that binds a service?

Not sure if I am misunderstanding how directives are created here. Say for example I have a controller such as:
angular.module('myApp.controllers').controller('MyController', ['$scope', 'MyService', function($scope, MyService) {
$scope.restangularService = MyService;
}
I then have a directive such as:
angular.module('myApp.directives').directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '&'
},
controller: ['$scope', function($scope) {
//access $scope.restangularService to run some queries
}
};
});
I then use my directive as such:
<div data-my-grid data-restangular-service='restangularService'></div>
I would expect that in my directive I could access $scope.restangularService and make calls however it's not being populated correctly. Am I doing this totally wrong? Any input? I have a feeling I need to be using the ngModel directive somehow.
The "&" prefix of an isolate scope value in a directive provides "one-way binding" which makes available a getter function in the directive's scope.
Any changes you make to the object will not make their way back up to the parent controller of the directive (it is "read-only"). So you can't access your 'restangularService' variable as you would in the controller's scope, without calling the getter function:
angular.module('myApp.directives', []).directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '&'
},
controller: ['$scope', function($scope) {
console.log($scope.restangularService()); // outputs service value
}]
};
})
Alternatively, you could use "=", which would allow you directly access the scope object you pass in:
angular.module('myApp.directives', []).directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '='
},
controller: ['$scope', function($scope) {
console.log($scope.restangularService); //outputs service value
}]
};
})
Plunk demonstrating both types

AngularJS' $sce.trustAsHtml being ignored

I'm new to AngularJS and I feel like I'm just scratching the surface of what's possible with the framework. However, I'm running into problems with the sce.trustAsHtml function. I'm running AngularJS 1.2.4.
In my application, I'm loading items using JSON. These items are displayed in a list using a directive. Sometimes, I would want to inject HTML into the retrieved content (e.g. to make links clickable).
I've read I can use $sce.trustAsHtml to allow html in the binds. However, the following snippet isn't working. I would expect all items to be replaced with a bold text 'test', but instead it's displaying <strong>Test</strong> for each item.
Is there a simple way to make this snippet work?
angular.directive('ngStream', function($timeout, $sce) {
var url = "getitems.json";
return {
restrict: 'A',
scope: {},
templateUrl: 'templates/app_item.html',
controller: ['$scope', '$http', function($scope, $http) {
$scope.getItems = function() {
$http.get(url,{}).success(function(data, status, headers, config) {
$scope.items = data;
});
}
}],
link: function(scope, iElement, iAttrs, ctrl) {
scope.getItems();
scope.$watch('items', function(newVal) { if (newVal) {
angular.forEach(newVal, function(vars,i) {
# Example html string for testing purposes.
var editedContent = '<strong>Test</strong>';
newVal[i].contentHtml = $sce.trustAsHtml(editedContent)
});
}});
},
}
});
What's on your template? $sce.trustAsHtml must be used with ng-bind-html instead of normal ng-bind (or {{}})

Categories