MVC Route ignoring for angularjs directive templates - angularjs

I am using angularjs + MVC.
I am creating some angularjs directives. I want to put templates inside the View folder.. I think is a cleaner solution than having another folder that contains the template. Following the structure of my solution, I think that is nothing special:
Root
Areas
Scripts
Directives (here I have angular directives)
Views
Shared
Templates (here I have the templates of the directives)
Here an example of directive:
angular.module('qbDirectives', []).directive('qbPreloader', ['$document', function ($document) {
return {
templateUrl: '/Views/Shared/Templates/qbPreloader.html',
link: function (scope, element, attr) { ... }
...
};
}]);
As you can see, the template is an HTML file, not a CSHTML.. When I run my application I obtain this client error:
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:52746/Views/Shared/Templates/qbPreloader.html
The physical path is correct but the template is not found. I think the problem is that MVC can access the View folder just with routing, so MVC need a controller to load the resource.
I do not want to do a server side call to obtain the template so I am trying to ignore the route.
In my global.asax I wrote
routes.IgnoreRoute(
"{*staticfile}",
new { staticfile = #".*\.(jpg|gif|jpeg|png|js|css|htm|html|htc)$" }
);
or
routes.IgnoreRoute("Views/Shared/Templates/{file}.html");
Buth nothing works. I still have the same problem.
How do I have to ignore the route correctly?
Thank you

Related

Angular $templatecache not working when i tried to include the cached template in html

I am trying to cache $templatecache as my html is coming from controller as string than I just put it in to cache using $templatecache but when i am trying to include it in HTML using following statement than its showing me 404 not found error in console.
http://local.mydomain.com/myaccount/dqs.html 404 (Not found)
"<div id="dqs" ng-include="'dqs.html'" class="form__main" set-height></div>"
Angular code :
$templateCache.put('dqs.html', filteredFormHtml);
As you are adding your template in html, you need to first cache that template by using $templateRequest service.
$templateRequest('/myaccount/dqs.html').then(function (filteredFormHtml) {
$templateCache.put('filteredFormHtml', filteredFormHtml);
});
Once the template is cached, you can use it anywhere in your template using ng-include
"<div id="dqs" ng-include="'dqs.html'" class="form__main" set-height></div>"
Below are the user links for better understanding on $tempalteCache Service
1) https://docs.angularjs.org/api/ng/service/$templateCache
2) https://thinkster.io/templatecache-tutorial

Angular Template Cache with Service Worker Issue

I have a gulp setup that puts all my html in template cache for faster access in angular. I'm trying to add a service worker to my project, using sw-precache, so that it can be used offline. If I'm connected to the network, everything works fine. When I go offline, the requests for html resources (that are in the template cache) fail because it seems it is requesting the path from the network.
Is there something I need to add to my sw-precache config in order to have it defer to angular to handle retrieval of html files?
ok, so this is how I solved this. I am using Angular JS 1.6.4 with sw-precache.
I have CacheStorage via service workers, so using service workers I know I am expecting devices to support certain functionality, in my case we know our users will have Android Tablets with Chrome and support is valid.
I am developing a progressive web app with offline functionality.
So, the theory...I have directives which have templateUrls.
Using this post: https://thinkster.io/templatecache-tutorial
I basically have my directive code:
angular.module('app').directive('location',
['$timeout', 'notify.service', 'user.settings.service', 'log.service',
function ($timeout, notify, userSettings, log) {
return {
restrict: 'EA',
... controller etc..,
templateUrl: '/App/core/directives/location.html'
}
}
]);
Now, when this app goes offline, the cached instances of the content was not kicking it - annoying.
So, after much procrastinating I got down and dirty.
My solutio is, keep the templateUrl as it is, but overwrite the content via the $templateCache service.
To do this, you append a RUN function with your directive (for clarity). We know the service worker cache representation of our Url files contains the common path, in my case: '/App/core/directives/location.html'.
So, using new technology in the browser, window.caches gives me access to the CacheStorage that the service workers uses, I can then use the API available: https://developer.mozilla.org/en-US/docs/Web/API/Cache
I can then use the match method to find the matching service worker cache content, read that stream of binary and convert to HTML and then tell $templateCache to replace it with the service worker cached value.
So, for completeness (and you can create a common service which replaces the cached values based on templateUrl - which I will be doing for each directive)
(function () {
'use strict';
var templateUrl = '/App/core/directives/location.html';
// <location on-location='someFunc'></location>
// provides a form/control to find a location either by GEO location or manual city search
angular.module('app')
.run(['$templateCache', function ($templateCache) {
var cache = window.caches;
cache.match(templateUrl, { ignoreSearch: true }).then(function (response) {
if (response) {
response.body.getReader().read().then(function (cachedResponse) {
// convert the Uint8Array value to HTML string
var content = new TextDecoder('utf-8').decode(cachedResponse.value);
$templateCache.put(templateUrl, content);
//console.log($templateCache.get(templateUrl)); // debug
});
}
});
}])
.directive('location',
['$timeout', 'notify.service', 'user.settings.service', 'log.service',
function ($timeout, notify, userSettings, log) {
return {
restrict: 'EA',
... controller, scope etc...
templateUrl: templateUrl
}
}
]);
})();
Draw backs...the RUN process is synchronous, so initially they have to hit the site online first...but thats how service worker needs to work anyway, so thats handled in training :)
I expect there to be a better option, but for the time being thats the solution I have, I will be creating a template service for replacing $templateCache values based on the var templateUrl each directive will have, so the code becomes cleaner in each directtive....i considered having a global arr of templates and files, but, just a bit obscure, think its cleaner for each directive
my original solution was not 100%
To solve this, use sw-precache and sw-toolbox
Using a gulp configuration you can setup sw-precache to cache you content and extend this with sw-toolbox to use cached responses based upon routing configuration
see: https://developers.google.com/web/ilt/pwa/using-sw-precache-and-sw-toolbox

Angular Directive not executing on UI Bootstrap Modal open

I have a one-page site that I am building out and this is my first time using Angular on a site. Building it on top of Laravel too for the backend but that is beyond the scope of this question.
I need to be able to open a modal on a main page view which will add a new resource (e.g. a new client) or edit a resource. I want to somehow get the form's html inside the modal body when the $uibModal.open()'s controller is called and set the $scope.modalBody equal to the injected items.modalBody (the only way this works is if I use:
$scope.modalBody = $sce.trustAsHtml(items.modalBody);
The only problem now is that anything inside the HTML body, Angular will not use it's magic and do any data-binding. It is still in the raw form of
{{ object.property }} or since I'm using Laravel and avoiding conflict with the Blade template engine:
<% object.property %>
See screenshot:
screenshot
I have been banging my head against the wall on this one...I have tried putting $scope.$apply() in my directive and my controller, neither of which worked. I have a feeling that is the source of my problem though. I have also tried making the html just a <new-client></new-client> directive and using templateUrl: 'views/clients/add.php' which would be ideal, but the template is not being included inside the <new-client></new-client>.
I'm using ui-bootstrap 0.14.3 and Angular 1.4.8.
Could this be a bug? Or am I doing something wrong? Anyone have a better way of getting a form into my modal? Let me know what code you want to see so I don't clutter this post with unnecessary code blocks.
I have come across a similar issue with using jQuery's AJAX to receive template strings and append it to a server.
So when HTML is added via jQuery, bound html string, etc., angular doesn't know it needs to automagically compile this data.
What you need to do is use the $compile service, to $compile your html and then attach the correct $scope to it:
`$compile('jQuerySelectorReturningHtmlOrAnHTMLStringThatNeedsToBeCompiled')($scope);`
There are multiple examples in Angulars Documentation for $compile that can give you an idea of what is happening. I think by what you have described the same thing is happening here in your situation.
The key is to call this $compile service function after the html has been bound to the page.
EDIT:
There are a few other options based on some comments, that will serve as a viable solution to rendering this content on your view. For example a directive that takes a string attribute representing the HTML string of your desired view.
1. Modify your directive template in the compile step:
You have the ability to modify your template before the directive compiles and binds any attributes to it, to that directives scope:
app.directive('myAwesomeCompileStepDirective', [myAwesomeCompileStepDirectivef]);
function myAwesomeCompileStepDirectiveFn() {
return {
restrict: 'EA',
compile: function compileFn(tAttrs, tElement) {
//Here you can access the attrs that are passed into your directive (aka html string)
tElement.html(tAttrs['stringThatYouWantToReplaceElementWith']);
return function linkFn(scope, element, attrs, controller, transcludeFn) {
//if all you want to do is update the template you really don't have to do anything
//here but I leave it defined anyways.
}
}
}
}
You can view a file I wrote for a npm component which uses this method to modify my directive template before it is compiled on the page & you can also view the codepen for the complete component to see it in action.
2. Use $compile service to call $compile in link function using directive attrs.
In the same way as the aforementioned method, you can instead inject the $compile service, and call the function mentioned above. This provides a bit more work, for you but more flexibility to listen to events and perform scope based functions which is not available in the compile function in option 1.

Directives calling services directly

Is it a good practice to call services directly from a Angular JS Directive?
Here is an example: directive to take name as input and calls a service(connects to db) to get the HTML content and render it.
a service call is made inside the linkFn to get the HTML content
or
return {
restrict:'EA',
scope:{
name:"=",
getPartial : "&"
},
link:linkFn
}
here getHTMLContent is implemented in the controller and calls the same service.
No. Imagine if you put that directive in an "ng-repeat". You'd have way too many calls to the server. How you retrieve your model should be separate from the way you present the model.

Intercept Angular template loading for use with Meteor+Blade

Little brief: I'm using AngularJs with Meteor+Blade without using Meteor_angularjs package for meteor. Blade constructs the body of the page in the server then I manually bootstrap angular in the client.
Blade has template files available under Template['template_name'] so they can be easily rendered on the client. I would like to do something like:
div(ng-include='content.blade')
// HTML => <div ng-include='content.blade'></div>
and somehow make it work.
To keep compatibility and not creating new directives I thought it could be possible to intercept the XHR requests angular makes to static templates and add the condition
if(URI ends with '.blade') then
name <- strip '.blade' in URI
return Template[name]()
Which should return the compiled HTML for that template.
UPDATE:
Coincidentally I ran into $templateCache and now I think it's the way to go.
I created a 'ngMeteor' module that I'll use for meteor-angular integration.
angular.module 'ngMeteor',[], ->
throw 'Meteor object undefined.' unless Meteor? # Is it fine to put here?
angular.module('ngMeteor.blade',['ngMeteor']).
run ($templateCache) ->
$templateCache.put "#{name}.blade", render() for own name, render of Template
In my app:
angular.element(document).ready ->
angular.bootstrap document, ['app']
app = angular.module 'app', ['ngMeteor.blade'], ->
app.controller 'mainCtrl', ($scope,$templateCache) ->
$scope.content = $templateCache.get "content.blade" # Works!!
Blade(body.blade):
#main(ng-controller='mainCtrl') {{ content }}
Now it works, I can get the rendered template from the controller after injecting $templateCache and geting the template by its name but ng-include still won't work.
My previous update in the question was actually the correct answer, ngInclude didn't work for me because of div(ng-include="'content.blade'") ... yes, the inner quotes! its like the Nth time I have that problem.
In resume the answer is:
angular.module('blade').
run ($templateCache) ->
$templateCache.put "#{name}.blade", render() for own name, render of Template
Template is the meteor global variable where blade will store templates ready to be rendered, then with $templateCache I put the rendered templates with the corresponding names/ids, that way Angular can use them.
EDIT: Based on this question I created a meteor package ng-meteor for braceless angular development in meteor.

Resources