made angular scope available in Django TemplateResponse() - javascript

I am using Python/Django as back-end for framework and angular as front-end. At some point I am using TemplateResponse() in view function to get template response, this template includes HTML/CSS/JS/Angular code . TemplateResponse() can complie every other code but not angular bec angular scope is not available.
Here is sample code :
Django code
t = TemplateResponse(request,
'dynamic-content.html',
{'raw_html':raw_html,
'raw_css':raw_css,
'raw_js':raw_js,
'js_script':js_script,
'css_script':css_script,})
t.render()
Now I'll send t.content to angular which will include template response.
In angular I'll use directive to make angular scope available in the above template response .
<div dynamic="t.content"></div>
Directive :
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);
});
}
};
});
But the time b/w providing angular scope to template response takes time.
Another issue is js gets compiled before angular scope available.
So either I have to make angular scope available in Template or stop js to get compile before angular.
What approach i should take regarding this . Please help.

Related

Dependency injection in Angular Components like directives

Here's one thing I'm used to do with angular directives
angular.module('app.directives').directive('login', ['$templateCache', function ($templateCache) {
return {
restrict: 'E',
template: $templateCache.get('directives/login/login.html'),
controller: 'LoginController as vm',
scope: true
};
}]);
I've grown very attached to using Template Cache to inject HTML content in my directive's template. Now with Angular 1.5 there's this new thing all the cool kids are using called component() which I'm giving a look to see if it's really good and I'm stuck at this very beginning part: how to inject things in the component itself (not in the controller)?
In this case you can see that I'm injecting into the login directive the $templateCache dependency. How would I rewrite this directive as a component? (keeping in mind my desire to use $templateCache over templateUrl)
Well, In Angular 1.5 components, template property can be a function and this function is injectable (documentation).
So, you can just use something like:
...
template: ['$templateCache', function ($templateCache) {
return $templateCache.get('directives/login/login.html')
}]
...
Few links from google search: one and two.
Hope it will help.
MaKCblMKo 's answer is right, but remember that AngularJS will check the templateCache first before going out to to retrieve the template. Therefore, you don't have to make this call in your directive or component.
angular.module('myApp', [])
.component('myComponent',{
templateUrl: 'yourGulpPattern'
})
.run(function($templateCache) {
$templateCache.put('yourGulpPattern', 'This is the content of the template');
});
https://jsfiddle.net/osbnoebe/6/

Angular directive inside ng-template with modal

I am curious about the way angular works with preloading directives since I have a problem with a directive that resides in a <script> tag and ng-template.
When I pause execution in chrome dev-tools, during the initial document load, I can clearly see that the code inside the directive's controller does not get called if my directive lies in some arbitrary template. Here is the example code, when e.g. myDirective is included in index.html as a part of myModule.js module, also included both in index and in the main app module:
This is some other directive's html containing the problematic myDirective
<script type="text/ng-template" id="callThis">
<myDirective></myDirective>
</script>`
and I call it on click with ngDialog like this
ngDialog.open({
template: 'callThis',
scope: $scope
});
and it can't run the directive since it doesn't have any html to work with (thats the error, about some html element missing).
Finally here is the code for the module which holds myDirective
angular.module('myModule', ['myDirectiveTemplates', 'myDirectiveDirective'])
angular.module('myDirectiveTemplates', []).run(["$templateCache", function($templateCache) {$templateCache.put("myDirectiveTemplate.html","<h1></h1>");}]);
angular.module('myDirectiveDirective', []).directive('myDirective', function ($window, $templateCache) {
return {
restrict: 'E',
template: $templateCache.get('myDirectiveTemplate.html'),
controller: function($scope) {
//some arbitrary code
}
};
})
Interestingly if i put <my-directive></my-directive> right in index.html file it works ok, and the code inside the controller gets loaded on startup. I'm unsure how to solve this.
From what I understand of this problem you need to use the $compile service. There is a tutorial here that might help : $compile
The reason given in the tutorial is:
"The newly minted template has not been endued with AngularJS powers
yet. This is where we use the $compile service..."
And quoted from the Angular Docs:
Compiles a piece of HTML string or DOM into a template and produces a
template function, which can then be used to link scope and the
template together.
Here is a brief code example as per the tutorial :
app.directive('contentItem', function ($compile) {
/* EDITED FOR BREVITY */
var linker = function(scope, element, attrs) {
scope.rootDirectory = 'images/';
element.html(getTemplate(scope.content.content_type)).show();
$compile(element.contents())(scope);
}
/* EDITED FOR BREVITY */
});

Working Angularjs template cache on build and deployment

I am developing angular directives that used html template.
angular.module("app")
.directive("ticket", [function(){
return {
restrict: "E",
templateUrl: "app/ticket/ticket.html"
}
}])
This directive is working and I can change ticket.html content and run application to see changes.
But I read about $templateCache method to increase performance of big projects. And I can use grunt-angular-template to create all of template cache.
But I need to change my directive.
angular.module("app")
.directive("ticket", ["$templateCache",function($templateCache){
return {
restrict: "E",
template: $templateCache.get("app/ticket/ticket.html")
}
}])
Can I separate the build and deployment usage of template.
templateUrl: "app/ticket/ticket.html"
template: $templateCache.get("app/ticket/ticket.html")
I will select one of that ways. What is the professional approach for this?
You do not need to update this, you will automatically get it from the cache if you leave your code as it is, here you are just passing a template path to angular and angular gets it for you from the cache. $templateCache.get("app/ticket/ticket.html") is only needed if you want to load a template via js.

$templateCache is undefined In directive though I set `{cache: $templateCache}` in $http call

I have two html pages, snippet1.html & snippet2.html. I want use them inside my directive. Because I'm going to add multiple template by using single directive.
I tried this thing with by adding html templates inside <script> tag & gave type="text/ng-template" to them like Below.
<script type="text/ng-template" id="snippet1.html">
<div>Here is Snippet one</div>
</script>
<script type="text/ng-template" id="snippet2.html">
<div>Here is Snippet two</div>
</script>
And then I use $templateCache.get('snippet1.html'). This implementation is working Fine.
But in my case I need to load them from html itself, so I decided to load template by ajax and make $http cache: $templateCache
Working JSFiddle
Run Block
myApp.run(['$templateCache','$http', function($templateCache, $http){
$http.get('snippet1.html',{ cache : $templateCache });
$http.get('snippet2.html',{ cache : $templateCache });
}]);
But inside my controller $templateCache.get('snippet1.html') is undefined.
My question is, Why it is working while i declared template inside <script>' tag & Why it don't work when I html inside$templateCachewhile making$http` ajax call?
Plunkr With Problem
Can anyone help me out with this issue? Or I'm missing anything in code.
Help would greatly appreciated. Thanks.
This is an interesting issue, and I can offer an interesting workaround and my thoughts on what is going on. I think a better solution may exist, but finding such solutions also proved to be a challenge. Nonetheless, I think the main issue is simply your console.log($templateCache.get('snippet1.html')) is returning undefined because of the race condition with your $http.get's not resolving first.
Examining the api for $templateCache, I couldn't find any sort of helpful way of knowing when templates resolve requested via ajax. To see the simple issue, run this in your directive to see some basic information about what is currently stored in your $templateCache
console.log($templateCache.info())
With the result of
Object {id: "templates", size: 0}
For observing the core of the issue, run the same JS in the directive, but with a timeout as such
setTimeout(function() {
console.log($templateCache.info())
}, 1000);
With the result of
Object {id: "templates", size: 2}
Interesting, so they're in there... but getting a handle on them is now the challenge. I crafted the following workaround to at least give us something for now. Inject $q and $rootScope into your .run function as such
myApp.run(['$templateCache', '$http', '$q', '$rootScope', function($templateCache, $http, $q, $rootScope){
$q.all([
$http.get('snippet1.html',{ cache : $templateCache }),
$http.get('snippet2.html',{ cache : $templateCache })
]).then(function(resp){
$rootScope.templateCache = resp
})
}]
);
Examining this, you'll notice I place an arbitrary var on our $rootScope as such $rootScope.templateCache for the purpose of placing a $watch on it in our directive. Then in our directive, let's call into our $templateCache when we then know there is a value on $rootScope.templateCache, indicating the $q service has resolved our promises as such
link: function(scope, element, attrs) {
scope.$parent.$parent.$watch('templateCache', function(n, o) {
if(n) {
element.append($compile($templateCache.get('snippet1.html')[1])(scope));
}
});
}
And hey look! Our template directive is correctly rendered. The hacky looking scope.$parent.$parent is because in this directive, we have isolated our scope and now need to climb some ladders to get the value defined on $rootScope.
Do I hope we can find a cleaner more consise way? Of course! But, hopefully this identifies why this is happening and a possible approach to get up and running for now. Working plunker provided below.
Plunker Link
Edit
Here is a completely different approach to solve this which involves manual bootstrapping
var providers = {};
var $injector = angular.injector(['ng']);
var myApp = angular.module('myApp', []);
$injector.invoke(function($http, $q, $templateCache, $document) {
$q.all([
$http.get('snippet1.html',{ cache : $templateCache }),
$http.get('snippet2.html',{ cache : $templateCache })
]).then(function(resp){
providers.cacheProvider = $templateCache;
angular.bootstrap($document, ['myApp']);
});
});
myApp
.controller('test',function() {
})
.directive('myTemplate', function ($templateCache, $compile) {
return {
restrict: 'EA',
scope: {
snippets: '='
},
link: function(scope, element, attrs) {
element.append($compile(providers.cacheProvider.get('snippet1.html')[1])(scope));
}
};
});
Updated Plunker
This is the expected behavior. When you include a template inside of a script tag, angular finds it during the bootstrap process and adds it to the cache before any code runs. That's why it is available in your directive.
When you use $templateCache.put() (or use $http.get to retrieve an html file as you specify in your code, angular has to use ajax to resolve the template. While the request is "in flight", the template cache doesn't know anything about it - the file is only added to the template cache after the response is received.
Since your directive runs as part of the first digest cycle (on startup), there will never be any remote files in the cache, so you get the error you see.
The "correct" way to do what you are trying to do is to not use the $templateCache directly, but rather use the $http service to request the remote template. If the original response has returned, $http will just call $templateCache.get for you. If it hasn't, it will return the same promise that the original $http request generated.
Doing it this way, there will be no requirement to use $timeout or $watch. The template will be compiled as soon as it is available using promises.
myApp.controller('test',function(){})
.directive("myTemplate", function ($http, $compile) {
return {
restrict: 'EA',
scope: {
template: '&?myTemplate',
src: '&?'
},
link: function(scope, element, attrs) {
$http.get(scope.template() || scope.src()).then(function(result) {
element.append($compile(result.data)(scope));
});
}
};
});
<my-template src="snippet1.html"></my-template>
or
<div my-template="snippet1.html"></div>
Here is a working Plunk
EDIT: Alternative without $compile and $http
myApp.controller('test',function(){})
.directive("myTemplate", function ($http, $compile) {
return {
restrict: 'EA',
scope: {
snippets: '='
},
template: 'snippet1.html',
link: function(scope, element, attrs) {
}
};
});
As to your final question (why you have to use [1] to get the html - $http service does not store only html in the cache - it stores a data structure that may contain a promise or the element (if loaded from a script tag). Since it knows what it put in, it knows how to get it out. When you short circuit things you're only guessing.
Long story short - don't use $templateCache to resolve templates yourself.
EDIT: Code from $http demonstrating the different types of data that might be stored in the cache.
if (cache) {
cachedResp = cache.get(url);
if (isDefined(cachedResp)) {
if (isPromiseLike(cachedResp)) {
// cached request has already been sent, but there is no response yet
cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
} else {
// serving from cache
if (isArray(cachedResp)) {
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
} else {
resolvePromise(cachedResp, 200, {}, 'OK');
}
}
} else {
// put the promise for the non-transformed response into cache as a placeholder
cache.put(url, promise);
}
}

Angular: my isolated attribute not binding in simple directive

I am using this tutorial to understand how to display an image using directives with an isolate scope. What am i missing to get the attribute to bind? In my ide, I can output the image but the directive is not working. The fiddle displays nothing :-(
Fiddle:http://jsfiddle.net/gogirl/Stm3j/
This is outputted in my program:
output:{{photo.url}}
<img ng-src='http://www.w3schools.com/js/pic_bulboff.gif' />
This is not outputted:
<my-d photo-src={{photo.url}} ></my-d>
You have residual data in your jsfiddle configuration
Here is what I did to make it work
Remove the external resources because you use angular 1.0.1 with angular 1.2.1
Remove in options panel the body tag myModule
Remove the call of the undefined variable app and query it with angular.module
jsfiddle updated here
// My Main Application File
angular.module('myApp', ['myDirectives']);
// My Directives File
angular.module('myDirectives', []);
// Controller One File
angular.module('myDirectives', []).directive('myD', function () {
return {
restrict: 'E',
template: '<figure> <img ng-src="{{photoSrc}}"/> </figure>',
replace: true,
// pass these two names from attrs into the template scope
scope: {
photoSrc: '#'
}
}
})
All is working fine now when i switched the original module chaining method with the global app method:
var app = angular.module("myApp", []);
app.directive('myD', function() {
return {
restrict: 'E',
template: '<figure> <img ng-src="{{photoSrc}}"/> </figure>',
replace: true,
// pass this name from attrs into the template scope
scope: {
photoSrc: '#'
}
}
});
But I wont be a happy camper until I figure out what is wrong with the chaining declaration as it works fine elsewhere. Any takers? Fiddle still not working so i think it may be an issue with it being a fiddle.
EDIT: WORKS fine also with chaining method..One baby step at a time :-)

Categories

Resources