How do you require angular template html? - angularjs

When creating custom directives, if you want to put your view/template html in separate files, Angular seems to load the template from a public URL, making an HTTP request for it.
How do you include this template HTML inline while keeping it in a separate file?

With ES6 nothing is impossible:
import yourTemplate from 'path/to/file';
// inject $templateProvider in start point of your application
$templateProvider.put('path/to/file', yourTemplate);
$templateProvider at its own is simple $cacheFactory instance, where you can put any html, by any key, that can be used in ng-include or simply used in your directive as shown below:
//Directive
import yourTemplate from 'path/to/file';
that is used within directive configuration:
...,
controller: xxx,
template: yourTemplate,
link: () => { ... }
...

Use directive to fix this
HTML
<div custom-include url="{{url}}"></div>
Directive
app.directive('customInclude', ['$http', '$compile', '$timeout', customInclude]);
function customInclude($http, $compile, $timeout) {
return {
restrict: 'A',
link: function link($scope, elem, attrs) {
//if url is not empty
if (attrs.url) {
$http({ method: 'GET', url: attrs.url, cache: true }).then(function (result) {
elem.append($compile(angular.element(result.data))($scope));
//after sometime we add width and height of modal
$timeout(function () {
//write your own code
}, 1, false);
});
}
}
};
}

"inline" in a separate file is contradicting itself.
Since angular is client side code, loading a template that is stored in it's own separate file will always require a http request.
The only way to avoid these requests is to add them inline with your other code, so not in a separate file.

Related

Angular and preloader for an ajax template

I have a dashboard with several templates.
In one of the templates, I have a simple list. I'm using ng-repeat on my li's and that way I keep my list dynamic.
But here's the thing -
Since I'm getting the data for this list using $http, it is likely to have an empty list for a second or two.
A good solution for this would be to add a preloader for my list by default, but how would you suggest to add the logic for that? The easiest way would be to add it like so:
$http({
method: 'GET',
url: './data/websites.json'
}).then(function successCallback(response) {
// hide preloader, etc
});
Would it be the right way to go?
Also - is there anyway to have control on the template transitioning? for example, when a user left a page I want to show a preloader for X milliseconds, and only then move to the requested page
It is better to have a directive does everything for you:
angular.module('directive.loading', [])
.directive('loading', ['$http' ,function ($http)
{
return {
restrict: 'A',
link: function (scope, elm, attrs)
{
scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};
scope.$watch(scope.isLoading, function (v)
{
if(v){
elm.show();
}else{
elm.hide();
}
});
}
};
}]);
With this directive, all you need to do is to give any loading animation element an 'loading' attribute:
<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>

angularjs: injecting async service into directive with $resource

I'm very much trying to get my head around angularJS and directives still.
I have an existing REST service that outputs JSON data as follows (formatted for readability):
{"ApplicationType":
["Max","Maya","AfterEffects","Nuke","WebClient","Other"],
"FeatureCategory":
["General","Animation","Compositing","Management","Other"],
"FeatureStatus":
["Completed","WIP","NotStarted","Cancelled","Rejected","PendingReview"],
"BugStatus":
["Solved","FixInProgress","NotStarted","Dismissed","PendingReview"]}
I then have a service (which appears to be working correctly) to retrieve that data that I wish to inject into my directive.
(function () {
'use strict';
var enumService = angular.module('enumService', ['ngResource']);
enumService.factory('Enums', ['$resource',
function ($resource) {
return $resource('/api/Enums', {}, {
query: { method: 'GET', cache: false, params: {}, isArray: false }
});
}
]); })();
My intentions are to use the data from the json response to bind to html selector 'options' for the purposes of keeping the data consistent between the code behind REST service and the angular ( ie. the json data is describing strongly typed model data from c# eg. Enum.GetNames(typeof(ApplicationType)) )
projMgrApp.directive('enumOptions', ['Enums',
function (Enums) {
return {
restrict: 'EA',
template: '<option ng-repeat="op in options">{{op}}</option>',
scope: {
key: '#'
},
controller: function($scope) { },
link: function (scope, element, attrs) {
scope.options = Enums.query(function (result) { scope.options = result[scope.key]; });
}
};
}
]);
the intended usage would be to use as follows:
<label for="Application" class="control-label col-med-3 col">Application:</label>
<select id="Application" class="form-control col-med-3 col pull-right">
<enum-options key="ApplicationType"></enum-options>
</select>
which would then produce all of the options consistent with my c# enums.
In this case it appears the directive is never being called when the tag is used.
Note. I assume the factory is working fine, as i can inject it into a separate controller and it works as anticipated.
1) I guess projMgrApp is the main module. Have you included enumService as dependency to this module?
angular.module('projMgrApp',['enumServices'])
Otherwise your main module won't be aware of the existence of your service.
2) Are you aware how the directives are declared and used. When you declare
projMgrApp.directive('EnumOptions', ['Enums', function (Enums) {}])
actually should be used in the html code as:
<enum-options></enum-options>
I m not quite sure about the name but it should start with lowercase letter like enumOptions
3) I don't know why you use this key as attribute. You don't process it at all. scope.key won't work. You either have to parse the attributes in link function (link: function(scope, element, attributes)) or create isolated scope for the directive.
Add as property of the return object the following:
scope : {
key:'#' //here depends if you want to take it as a string or you will set a scope variable.
}
After you have this , you can use it in the link function as you did (scope.key).
Edit:
Here is a working version similar (optimized no to use http calls) to what you want to achieve. Tell me if I'm missing anything.
Working example
If you get the baddir error try to rename your directive name to enumOptions according to the doc (don't forget the injection):
This error occurs when the name of a directive is not valid.
Directives must start with a lowercase character and must not contain leading or trailing whitespaces.
Thanks to Tek's suggestions I was able to get this fully functioning as intended. Which means now my angularJS + HTML select tags/directives are completely bound to my APIs enums. I know down the line that I am going to be needing to adjust add to these occasionally based on user feedback, plus I use these enums to represent strongly typed data all over the app. So it will be a big time saver and help reduce code repetition. Thanks to everyone that helped!
The service and directive that I used is copied below, in case other people starting out with Angular run into similar issues or have similar requirements.
Service:
(function () {
'use strict';
var enumService = angular.module('enumService', [])
.service('Enums', ['$http', '$q', function ($http, $q) {
return {
fetch: function () {
var defer = $q.defer();
var promise = $http.get("/api/Enums").then(function (result) {
defer.resolve(result);
});
return defer.promise;
}
}
}]);})();
Directive:
angular.module('projMgrApp').directive('enumOptions', ['Enums', function (Enums) {
return {
restrict: "EA",
scope: {
key: "#"
},
template: "<select><option ng-repeat='enum in enumIds' ng-bind='enum'></option><select>",
link: function (scope) {
Enums.fetch().then(function (result) {
scope.enumIds = result.data[scope.key];
});
}
};
}]);

angular scope duplicate in dynamic directive

I'm building Chrome extension that inject script and Html tags to page.
And I use directive to dynamically load template and controller
as below
<content-page content-url="{{IndexCtrl.partialPage}}">
</content-page>
in contactPage directive:
myApp.directive('contentPage',function($compile){
return {
restrict: "E",
replace:true,
link: function(scope, el, attrs){
attrs.$observe('contentUrl',function(pageUrl){
$.ajax({
url : pageUrl,
success : function(result){
el.html(result).show();
$compile(el.contents())(scope);
}
});
})
}
}
})
and I assign controller in html file, for example 'pageUrl' point to this html file:
<div ng-controller="infoCtrl">
......
</div>
then problem comes, the 'scope' of infoCtrl will exist forever unless I reload page; and my controller code like this
myApp.controller('infoCtrl',function($scope,$rootScope){
$scope.$on('get data',function(e,args){
console.log('get data',args);
//make request
}
}
as my code, while infoCtrl element be recreate, scope will be duplicated, and make multiple same request
right now I use a ugly fix to check $scope.$id and save it locally, check it before request, only enable the controller with newest id.
Is there other way to prevent scope be duplicate?

Change templateURL of directive dynamically after $http.get()

I'm working on 'skeleton' loading the UI in different components. I have a directive that I'm loading a template initially (this template has low opacity and looks like a mock table). Once I get the data I need in an http.get() then I want to change the templateUrl of the directive. Below is what I've tried so far.
function registers($log, $state, $templateCache, currentCountData, storeFactory) {
var directive = {
restrict: 'A',
scope: true,
templateUrl: '/app/shared/tableMock/tableMock.html',
link: link
};
return directive;
function link(scope, element, attrs) {
storeFactory.getRegisters().then(function (data) {
scope.registers = data.registers;
$templateCache.put('/app/dashboard/registers/registers.html');
});
}
}
I'm not sure I'm on the right track. I can step through and see the storeFactory return the correct data from my factory. How can I then change the templateUrl of the directive?
For cases like this I usually do something like this in my directive template:
<div ng-switch="viewState">
<div ng-switch-when="gotRegisters">
Content for when you get the registers
</div>
<div ng-switch-default>
For when you don't have the registers
</div>
</div>
This way you could just change a variable to show your content ie scope.viewState = 'gotRegisters'; instead of waiting for it to download after you already downloaded your registers.
With a help from this question I was able to come up with this
function link(scope, element, attrs) {
storeFactory.getRegisters().then(function (data) {
scope.registers = data.registers;
$http.get('/app/dashboard/registers/registers.html', { cache: $templateCache }).success(function (tplContent) {
element.replaceWith($compile(tplContent)(scope));
});
});
}
tplContent correlates to the response of the $http.get(). It's the html in the registers.html file. element represents the directive itself.

Execution flow of a directive in angular js and using its attributes

hello i would like to know the steps of execution of directives and some fundamental details about how the processing happens this would be very helpful for more customization of directives and exploit its features...
i have a sample template with a directive and the attributes needed and few html tags as shown
template1:-
<directive1 s-web-service-path="object.WebServicePath" >
<h1>any html content</h1>
</directive1>
my directive is i.e. it calls a web service to get its content
widgetsModule.directive("directive1", function ($http) {
try {
return {
restrict: "E",
replace: true,
transclude: true,
scope: {
sWebServicePath: "="
},
template: '<div ng-transclude><h1>My content {{result }}</h1> </div> ',
link: function (scope, element, attrs) {
var request = '<request struct>';
$http({ method: "POST", url: scope.sWebServicePath, data: request }).
success(function (data, status) {
scope.result = data;
}).
error(function (data, status) {
alert("Service Call Error");
})
}
}
}
catch (e) {
alert(e);
}
});
What is the difference between attrs and $scope in the link function... in above case
$scope.sWebServicePath gives me the value of object.WebServicePath i.e. something like "http://anypath.asmx"
but
attrs.sWebServicePath give me object.WebServicePath... why is this difference and how is it useful?
I know that putting "ng-transclude" would enable me to have
<h1>any html content</h1>
in the specified div of my template with in the directive but how does the execution happens?
and why is that return is to be written in a directive it returns a link function alright and it can be used for DOM manipulation but any example of when do i use it?
i know these might sound very fundamental but please do throw some light on the execution flow of the directive...or some good source of reference...thank you! and advice/tips on the usage of the parameters of the directive would be very helpful
this link has the best documentation on usage of directives they should have put this link under directive definition ... js came across this page while reading the angular documentation
http://docs.angularjs.org/api/ng.$compile
if you use http request in directive you dont forget for $watch because http calls are asynchronous.
Diference between attr and scope is:
scope - is own scope of directive
attr - are attribs of directive
i think so data passed through attr not have to always same as your own scope, because you can make your new own scope for directive but cant change attr.
may be helpful for you
about-those-directives
egghead.io
onehungrymind.com/angularjs-directives-basics/
2013 - angularjs-directives-using-isolated-scope-with-attributes
many sources
AngularJS-Learning

Resources