Highlights don't work in external template - angularjs

I am a noob with angularjs and I have a problem.
I am using prism.js or highlights.js in my web (same result). It works correctly into index.html but It doesn't work in other templates that I load with ngRoute.
I believe that the problem is angularjs only it renders one more time the html and it doesn't work when I load my content-principal.html.
INDEX.HTML
//<pre><code class="language-javascript">
colour syntax is ok
//</code></pre>
APP.JS
ionicEsApp.config(function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'templates/content-principal.html',
//controller: 'IonicEsController'
}).
content-principal.html
//<pre><code class="language-javascript">
colour syntax is NO work
//</code></pre>
¿any solution? Thanks and sorry by my english :P.

SOLVED.
We need:
index.html
prims.js and prism.css from http://prismjs.com/#basic-usage
app.js
To create a new directive (VERY IMPORTANT before from .conf)
var ionicEsApp = angular.module('ionicEsApp', [
'ngRoute',
'ngResource',
'ionicEsController'
]);
ionicEsApp.directive('ngPrism', [function() {
return {
restrict: 'A',
link: function($scope, element, attrs) {
element.ready(function() {
Prism.highlightElement(element[0]);
});
}
}
}]);
ionicEsApp.config(function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'templates/content-principal.html',
//controller: 'IonicEsController'
}).
otherwise({
redirectTo: '/'
});
});
content-principal.html
We have to use the new directive into code tag.
<pre><code ng-prism class="language-javascript">
alert("Prims is ok");
</code></pre>
NOTE: There is a problem with html, we need replace the < symbol by &lt. Example:
<pre><code class="language-markup">
&lth1> Hello! &lt/h1>
</code></pre>

Can't comment on an answer yet, but I found this technique useful.
If you load your templates 'manually' and insert the text into the dom, Angular will automagically convert the content to HTML entities, meaning your raw template are still readable, but display correctly.
In my application I use $sce and $templateRequest to get the template, then set an angular template variable to the value of the fetched template.
A couple of notes:
I have multiple code samples per directive instance, identified by a codeType variable
My templates filenames are in the form of _{codeType}.sample e.g. _css.sample
The template location is passed in as an attribute of the directive in the dom
The dom element containers are identified by class .sample-{codeType} e.g .sample-css
The angular placeholder is identified by {{sample{codeType}} e.g. {{samplecss}}
To prevent race conditions, I use $timeout to wait a beat and allow the current $apply() to complete before calling Prism on the code.
This method also allows for multiple types of code with similar outputs - for example, in my styleguide I show both the output HTML (codeType = 'html') and the un-rendered Angular templates (codeType = 'ng') - both require the Prism .language-markup class.
This can be simplified a lot if you only have one code sample per directive.
function StyleGuideComponentController($scope, $element, $templateRequest, $sce, $timeout)
{
var codeSampleTypes =
[
'html',
'ng',
'ngjs',
'css',
'less'
];
insertAllCodeSamples();
function insertAllCodeSamples()
{
var key;
for (key in codeSampleTypes)
{
insertCodeSample(codeSampleTypes[key]);
}
}
function insertCodeSample(codeType)
{
var sampleUrl = $scope.templateLocation + '/_' + codeType + '.sample',
sampleCode = $sce.getTrustedResourceUrl(sampleUrl);
$templateRequest(sampleCode).then(function(template)
{
var codeElement = $element.find('.sample-' + codeType)[0],
prismLanguage = codeType,
prismLanguageTypes =
{
'html' : 'markup',
'ng' : 'markup',
'js' : 'javascript',
'ngjs' : 'javascript'
},
key;
for (key in prismLanguageTypes)
{
if (prismLanguage === key)
{
prismLanguage = prismLanguageTypes[key];
}
}
codeElement.className += ' language-' + prismLanguage;
$scope['sample' + codeType] = template;
$timeout(function()
{
Prism.highlightElement(codeElement);
});
}, function()
{
$scope['sample' + codeType] = 'An error occurred' +
' while fetching the code sample';
});
}
return {
restrict : 'E',
scope :
{
templateLocation: '='
},
controller : StyleGuideComponentController
};
}

There is a really easy way to do this if you are using ng-view to load the template:
if you have something like this:
<div ng-controller="MainCtrl">
<div id="content" ng-view>
</div>
</div>
You can add this to your MainCtrl controller:
$scope.$on('$viewContentLoaded', function(){
Prism.highlightAll();
});
Now, if you use the default way to highlight the code:
<pre><code class="language-javascript">
Prism will highlight it
</code></pre>
Prism will highlight it!

Related

How do I load an Image from $templateCache using Angular 1

I have a plnkr and the story is long but it comes down to I need to be able to use an image from the templateCache. I try to do this using ng-src like this...
$templateCache.put('img/help/copy_icon', '://www.drupal.org/files/issues/sample_7.png')
...
template: '<img ng-src="{{url}}">'
However this doesn't work and I get...
copy_icon':1 GET https://run.plnkr.co/ZyTH7LFhPLhdQpCI/'img/help/copy_icon' 400 ()
So it isn't trying to get it from templateCache. Is there a way to do this?
Update
I tried ...
$http.get('://www.drupal.org/files/issues/sample_7.png').success(function (t) {
$templateCache.put('img/help/copy_icon', t);
});
But that didn't work either
I do not know exactly what you try to achieve by using $templateCache, which in your case just saves image URL to the cache, but I modified your code a bit, so it displays an image, I added controller:
app.run(function($templateCache){
$templateCache.put('img/help/copy_icon', 'http://www.drupal.org/files/issues/sample_7.png')
})
app.directive('ngImg', function(){
return {
rectrict: "E",
scope: {
url: "#url"
},
template: '<img ng-src="{{ imgUrl }}">',
controller: function ($scope, $templateCache) {
$scope.imgUrl = $templateCache.get($scope.url);
}
}
})
plunker: https://plnkr.co/edit/q5a9ptq8rd1G4uGkkNMe?p=preview
BTW, using ng prefix for custom directives names is not recommended and should be reserved for angular core directives/components

Can directives be used outside of HTML tags?

Since learning Angular a few months ago, I was under the impression that `directives are special functions which are activated by placing a keyword in an HTML tag in one of two ways - either as an element, as an attribute.
For example:
<my-directive>Something</my-directive>
or
<div my-directive></div>
However, in a Git project I came across, I'm seeing a directive used in a totally different way and I don't understand how it works.
The directive is supposedly activated just by adding a key-value of css: {} to a .state() function in ui-router.
For instance:
.state('state1', {
url: '/state',
controller: 'StateCtrl',
templateUrl: 'views/my-template.html',
data: {
css: 'styles/style1.css'
}
})
How does this "directive" works?
----------
The Javascript source of the directive copied from the Git project, so it's preserved in this question:
/**
* #author Manuel Mazzuola
* https://github.com/manuelmazzuola/angular-ui-router-styles
* Inspired by https://github.com/tennisgent/angular-route-styles
*/
'use strict';
angular
.module('uiRouterStyles', ['ui.router'])
.directive('head', ['$rootScope', '$compile', '$state', '$interpolate',
function($rootScope, $compile, $state, $interpolate) {
return {
restrict: 'E',
link: function(scope, elem){
var start = $interpolate.startSymbol(),
end = $interpolate.endSymbol();
var html = '<link rel="stylesheet" ng-repeat="(k, css) in routeStyles track by k" ng-href="' + start + 'css' + end + '" >';
elem.append($compile(html)(scope));
// Get the parent state
var $$parentState = function(state) {
// Check if state has explicit parent OR we try guess parent from its name
var name = state.parent || (/^(.+)\.[^.]+$/.exec(state.name) || [])[1];
// If we were able to figure out parent name then get this state
return name && $state.get(name);
};
scope.routeStyles = [];
$rootScope.$on('$stateChangeStart', function (evt, toState) {
// From current state to the root
scope.routeStyles = [];
for(var state = toState; state && state.name !== ''; state=$$parentState(state)) {
if(state && state.data && state.data.css) {
if(!Array.isArray(state.data.css)) {
state.data.css = [state.data.css];
}
angular.forEach(state.data.css, function(css) {
if(scope.routeStyles.indexOf(css) === -1) {
scope.routeStyles.push(css);
}
});
}
}
scope.routeStyles.reverse();
});
}
};
}
]);
The directive is named after HTML <head> tag. It presumes that your html page contains a <head> tag and treats that as your directive declaration. It also presumes that the angular ng-app declaration is placed on <html> tag.
The directive does nothing but erases and writes css <link> tags within the html head tag content everytime the state changes.
Note that it is not preferable to name your directives after native html tags. This is why you see angular directives prepended by 'ng' so as to clearly demarcate them as angular tags. Otherwise, it can lead to confusion, as you have yourself found out with trying to understand this piece of git code.

AngularJS: What is the best way to bind a directive value to a service value changed via a controller?

I want to create a "Header" service to handle the title, buttons, and color of it.
The main idea is to be able to customize this header with a single line in my controllers like this:
function HomeCtrl($scope, Header) {
Header.config('Header title', 'red', {'left': 'backBtn', 'right': 'menuBtn'});
}
So I created a service (for now I'm only focussing on the title):
app.service('Header', function() {
this.config = function(title, color, buttons) {
this.title = title;
}
});
...And a directive:
app.directive('header', ['Header', function(Header) {
return {
restrict: 'E',
replace: true,
template: '<div class="header">{{title}}</div>',
controller: function($scope, $element, $attrs) {
$scope.$watch(function() { return Header.title }, function() {
$scope.title = Header.title;
});
}
};
}]);
So, this actually works but I'm wondering if there are no better way to do it.
Especially the $watch on the Header.title property. Doesn't seem really clean to me.
Any idea on how to optimize this ?
Edit: My header is not in my view. So I can't directly change the $scope value from my controller.
Edit2: Here is some of my markup
<div class="app-container">
<header></header>
<div class="content" ng-view></div>
<footer></footer>
</div>
(Not sure this piece of html will help but I don't know which part would actually...)
Thanks.
If you are using title in your view, why use scope to hold the object, rather than the service? This way you would not need a directive to update scope.header, as the binding would update it if this object changes
function HomeCtrl($scope, Header) {
$scope.header = Header.config('Header title', 'red', {'left': 'backBtn', 'right': 'menuBtn'});
}
and refer to title as
<h1>{{header.title}}</h1>
Update
Put this in a controller that encapsulates the tags to bind to the header:
$scope.$on("$routeChangeSuccess", function($currentRoute, $previousRoute) {
//assume you can set this based on your $routeParams
$scope.header = Header.config($routeParams);
});
Simple solution may be to just add to rootScope. I always do this with a few truly global variables that every controller will need, mainly user login data etc.
app.run(function($rootScope){
$rootScope.appData={
"header" : {"title" : "foo"},
"user" :{}
};
});
.. then inject $rootScope into your controllers as warranted.

Angularjs - Hide content until DOM loaded

I am having an issue in Angularjs where there is a flicker in my HTML before my data comes back from the server.
Here is a video demonstrating the issue: http://youtu.be/husTG3dMFOM - notice the #| and the gray area to the right.
I have tried ngCloak with no success (although ngCloak does prevent the brackets from appearing as promised) and am wondering the best way to hide content until the HTML has been populated by Angular.
I got it to work with this code in my controller:
var caseCtrl = function($scope, $http, $routeParams) {
$('#caseWrap').hide(); // hides when triggered using jQuery
var id = $routeParams.caseId;
$http({method: 'GET', url: '/v1/cases/' + id}).
success(function(data, status, headers, config) {
$scope.caseData = data;
$('#caseWrap').show(); // shows using jQuery after server returns data
}).
error(function(data, status, headers, config) {
console.log('getCase Error', arguments);
});
}
...but I have heard time and time again not to manipulate the DOM from a controller. My question is how can I achieve this using a directive? In other words, how can I hide the element that a directive is attached to until all content is loaded from the server?
In your CSS add:
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
and just add a "ng-cloak" attribute to your div like here:
<div id="template1" ng-cloak>{{scoped_var}}<div>
doc: https://docs.angularjs.org/api/ng/directive/ngCloak
On your caseWrap element, put ng-show="contentLoaded" and then where you currently have $('#caseWrap').show(); put $scope.contentLoaded = true;
If the caseWrap element is outside this controller, you can do the same kind of thing using either $rootScope or events.
Add the following to your CSS:
[ng\:cloak],[ng-cloak],.ng-cloak{display:none !important}
The compiling of your angular templates isn't happening fast enough.
UPDATE
You should not do DOM manipulation in your controller. There are two thing you can do...
1. You can intercept changes to the value within the scope of the controller via a directive! In your case, create a directive as an attribute that is assigned the property you want to watch. In your case, it would be caseData. If casedata is falsey, hide it. Otherwise, show it.
A simpler way is just use ngShow='casedata'.
Code
var myApp = angular.module('myApp', []);
myApp.controller("caseCtrl", function ($scope, $http, $routeParams, $timeout) {
$scope.caseData = null;
//mimic a delay in getting the data from $http
$timeout(function () {
$scope.caseData = 'hey!';
}, 1000);
})
.directive('showHide', function () {
return {
link: function (scope, element, attributes, controller) {
scope.$watch(attributes.showHide, function (v) {
if (v) {
element.show();
} else {
element.hide();
}
});
}
};
});
HTML
<div ng-controller='caseCtrl' show-hide='caseData'>using directive</div>
<div ng-controller='caseCtrl' ng-show='caseData'>using ngShow</div>
JSFIDDLE:http://jsfiddle.net/mac1175/zzwBS/
Since you asked for a directive, try this.
.directive('showOnLoad', function() {
return {
restrict: 'A',
link: function($scope,elem,attrs) {
elem.hide();
$scope.$on('show', function() {
elem.show();
});
}
}
});
Stick (show-on-load) in your element, and in your controller inject $rootScope, and use this broadcast event when the html has loaded.
$rootScope.$broadcast('show');
I have used Zack's response to create a 'loading' directive, which might be useful to some people.
Template:
<script id="ll-loading.html" type="text/ng-template">
<div layout='column' layout-align='center center'>
<md-progress-circular md-mode="indeterminate" value="" md-diameter="52"></md-progress-circular>
</div>
</script>
Directive:
directives.directive('loading', function() {
return {
restrict: 'E',
template: 'll-loading.html',
link: function($scope,elem,attrs) {
elem.show();
$scope.$on('loaded', function() {
console.log("loaded: ");
elem.hide();
});
}
}
});
This example uses angular-material in the html
The accepted answer didn't work for me. I had some elements that had ng-show directives and the elements would still show momentarily even with the ng-cloak. It appears that the ng-cloak was resolved before the ng-show returned false. Adding the ng-hide class to my elements fixed my issue.

Angular, ng-repeat to build other directives

I'm building a complex layout, that takes a JSON document and then formats it into multiple rows, with each row then having more rows and/or combinations of rows/columns inside them.
I'm new to Angular and am just trying to get to grips with Directives. They are easy to use for very simple things, but quickly become very difficult once you need to anything more complicated.
I guess I'm doing this the wrong way around, but is there a way to simply add the name of a directive (in the example below, I've used ) and get that directive to be rendered on an ng-repeat?
Maybe the same way that you can use {{{html}}} instead of {{html}} inside of mustache to get a partial to render as HTML and not text.
As expected, the example below simply writes the name of the directive into the dom. I need Angluar to take the name of the directive, understand it, and then render before before it is written. Due to the complex layout of the page I need to design, I could be rendering many different directives, all inside each other, all from 1 JSON document (which has been structured into different rows and then row / column combinations).
Example code that renders the name of the directive to the page, but gives you an idea of how I'd like to write a solution the problem...
<div app-pages></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<script>
var app = angular.module("app", ['main']);
angular.module('main', [])
.controller("appPageController", ['$scope', function( $scope ){
$scope.pages = [];
var page1 = {
title: 'Page 1',
directive: '<app-page-type-1>'
};
var page2 = {
title: 'Page 2',
directive: '<app-page-type-2>'
};
$scope.pages.push(page1);
$scope.pages.push(page2);
}])
.directive("appPageType2", function factory() {
console.log('into page type 2');
return {
replace: true,
template: 'This is the second page type'
};
})
.directive("appPageType1", function factory() {
console.log('into page type 1');
return {
replace: true,
template: 'This is the first page type'
};
})
.directive("appPages", function factory() {
console.log('into pages');
return {
replace: true,
template: '<ul><li ng-repeat="page in pages">{{page.directive}}</li></ul>'
};
});
</script>
This is one possible alternative to your idea. The idea is to append the directive you defined in page object for each html element inside the ng-repeat. Please take a look at the demo. Hope it helps.
<div ng-app="myApp" ng-controller="appPageController">
<ul>
<li ng-repeat="page in pages" app-pages></li>
</ul>
</div>
.directive("appPages", function ($compile) {
console.log('into pages');
return {
replace: true,
link: function (scope, elements, attrs) {
var html = '<div ' + scope.page.directive + '></div>';
var e = angular.element(html);
elements.append(e);
$compile(e)(scope);
}
};
});
Demo

Resources