Unit Testing: Directive with Dynamic Template - javascript

I am running across an issue when trying to use an unusual method to grab templates for my page-content. I have abstracted it but suffice to say that the code works fine but the testing is throwing up some issues. I have listed my directive below:
Directive:
return {
restrict: 'A',
scope: {
page: '=pageCode',
},
link: function (scope, element) {
$http.get('templates/' + scope.page + '.html', {cache: $templateCache})
.success(function(html) {
element.replaceWith($compile(html)(scope));
});
}
};
Basically, when this directive is called with the page-code attribute, it grabs it and pulls in a specific partial for that page content. I have tried mocking out a $scope to contain the code but that wasn't working so I am hardcoding the data-page-code="404" for now. The test is as follows:
Directive Test:
it('should compile and pass through the scope', function() {
mockDirectiveHtml= '<div data-page-content data-page-code="404"></div>';
mockTemplateHtml= '<h1>Hi</h1>';
$httpBackend.expectGET('templates/404.html').respond(mockTemplateHtml);
pageContentHtml= $compile(mockDirectiveHtml)($scope);
$scope.$digest();
$httpBackend.flush();
expect(pageContentHtml.html()).toEqual('<h1>Hi</h1>');
});
However when this runs, the console is telling me it is trying to hit GET('templates/undefined.html'). Now I am unsure if I have things in the right order, or if that is even the solution but has anyone came across something like this in the past? I assume the directive is running before the attributes are passed to it so when it comes to grab templates/' + 'scope.code' that value is undefined.

Related

Parse content within angular directive template before rendering

Since there is no ng-url directive to use with an #import statement the way ng-src works, I am trying to create a directive for this purpose, the code is like this
.directive('importCss',
function()
{
return {
restrict: 'E',
replace: true,
scope: { loadCss : '=' },
template:'<style>' +
' #import url("{{loadCss}}")' +
'</style>',
};
}
);
The idea basically is to create HTML like this:
<style>
#import url("http://url_to_css_in_parameter")
</style>
Probably this is something very basic, but what is happening is that in my controller I have some init code that runs before asigning values to the scope var assigned to the loadCss parameter. The value is initialized to '' at the start of the controller code. The problem is that a request is made to the server requesting "http://serverAddr/{{loadCss}}" before it does the actual correct request with the values applied to the template.
Here is the callstack for the wrong request (screenshot):
Any idea while the template is being rendered before compiling and how to solve it? Thanks very much.

AngularJS directive with script

Basically, I have my directive defined sample.js as:
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'link-to-sample.html'
};
});
And in my sample.html I got (for example):
<script>
$(div).css('height', {{info}}+'px') //THIS DOESN'T WORK
</script>
<div></div>
In my, say, index.html I would like to pass a numerical value, through the info='' tag and use it in the script, running in the sample.html file.
<my-directive info='100'></my-directive>
But using just {{info}} in <script></script> tags seems not to work.
Would be grateful, if you, guys, help me with this.
P.S. I know, that I could write a code inside directive, use compile, or template:, but I would really like to keep in the separate .html file.
Embedding script tags in template files to deal with DOM in directives is strongly discouraged. It does not play well with the Angular way of DOM updates, and it does not have access to scope and the surrounding directive. Also, it will break under CSP where inline scripts are disallowed.
The recommended way would be to use the link function and remove the script tag in templates.
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'link-to-sample.html',
link: function (element, $scope, attrs) {
element.find('div').css('height', $scope.info + 'px');
},
};
});
You can still keep the separate sample.html for your template HTML. You just don't put logic in it. This is better separation of concerns in my opinion.
<div></div>

Angular code stops working when it's moved to a template file

I'm new to Angular, so the documentation is a bit of a bear. When I created the following code, and it worked, I thought I was in a good place.
<div ng-controller="postListController">
{{someDataStuff}} <!-- this always works -->
<div ng-repeat="post in data">
<b>{{post.title}}</b> <!-- This sometimes works -->
</div>
</div>
...and this Javascript in Angular (a snippet, really)
var listController = app.controller('postListController', function postListController($scope) {
$scope.someDataStuff = 'hey there.';
$scope.data = new Object(null);
$.get('get.php',function(data) {
$scope.data = JSON.parse(data);
console.log($scope.data);
});
});
someDataStuff printed out, and Angular looped happily through the post.titles. Then I tried moving this stuff, verbatim, to a template file and loading it appropriately...
app.directive('ngPostList', function() {
return{
restrict: 'E',
templateUrl: 'postlist.html'
}
});
This part... only sorta worked. While I was still getting data (returned it in console.log), and I was still getting someDataStuff to come back, it was no actually looping through the data.
Is there something in app.directive I'm missing to make it pass data on to the template I'm now using?
Your template should also take the controller, otherwise it is outside the scope and you are one level further down and you would have to call $parent.post.title etc... to reach the proper parent scope with the data.
try:
app.directive('ngPostList', function() {
return{
restrict: 'E',
Controller: 'postListController',
templateUrl: 'postlist.html'
}
});
Overall, pick up one of the Chrome extensions to debug Angular, like ng-inspector, so you can see what scope has what data and it makes your life a lot easier to debug this kind of things.
Angular issues are 95% of the time issues with scope not being what your think it is.

angular anchors break when switching to html5mode

I have an annoying issue I'm not sure there's a solution to;
We run html5mode in production, but not in development. The reason is that this is difficult to setup on spring-boot's embedded tomcat. We are coding our html anchors like this:
Route
which of course breaks when not in html5mode where angular will expect the href to be "#/route" instead - which is a real pain. The only way we've found to avoid this problem, is to code links like this:
Route
and have a global function in i.e. $rootScope:
$scope.goto = function (route) {
$location.path(route);
}
But this just seems wrong. I had hoped that using ng-href insted of href on the anchor would sort this out (perhaps detecting if we are in html5mode or not, and auto-add the hash to the url so that we do not need to refactor the entire app if we switch modes), but it doesn't.
Is there no way of using anchors WITHOUT explicitly stating hash urls?
Ok, I still believe this to be a design flaw in angular, and that this particular fix should exist within angular itself.
This directive is matched directly on the 'href' attribute. I've not done any testing on how this affects performance, but it does perform rewrites on internal url's only.
angular.module(_DIRECTIVES_).directive('href', ['$location', function ($location) {
'use strict';
return {
restrict: 'A',
scope: {
href: '#'
},
link: function (scope, element, attrs) {
// Check if we need to rewrite the href
var currentBase = $location.$$protocol + '://' + $location.$$host;
var newBase = element[0].href;
if (element[0].nodeName == 'A' && newBase.indexOf(currentBase) == 0 && !$location.$$html5) {
// Perform the href AFTER angular has done it's thing. Otherwise angular will rewrite back to the original
scope.$watch('href', function () {
// This function may be run several times. Make sure the rewrite only happens once.
if (scope.href.indexOf('#!') !== 0) {
scope.href = '#!' + scope.href;
element.attr('href', scope.href);
}
});
}
}
};
}]);
I hope this will help anybody else in the same predicament as me. Also hope that angular will fix this in the future.

ng-bind-html not processing INPUT

I would like to bind html with the content of $scope.value = "<input type=text name=a>"
Nothing is inserted inf the DOM, but if $scope.value = "Hello <i>Guys</i>" everything is fine.
Is there a limitation/bug with ng-bind-html? Is there a workaround?
I am using 1.2.4 version of angularJS
Thanks for your help, this is a big issue for my development
Christophe
Can't you use a Directive instead of that? This way I think you get rid of your problem
http://docs.angularjs.org/guide/directive
https://egghead.io/search?q=directive
A small example:
angular.module('myApp').directive('myDirective', function(){
return {
restrict: 'E',
replace: true,
// you can set 'transclude: true' instead of the following line to create a new scope but inheriting from the parent
scope: false, // this will make the directive have the same scope as the parent
templateUrl: 'my-html-template.html'// you can load the template like this
// You can also use 'template' and include the html code here
}
});

Categories

Resources