Conditionally-rendering css in html head - html

I am trying to dynamically add css to my html head using angular js. Here is sample code
<div ng-repeat="stylesheet in stylesheets">
<link href="/myapp/{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" media="{{stylesheet.media}}" title="{{stylesheet.title}}" />
</div>
This code works as expected, but when browser loads the page, it tries to fetch css resources with raw angularjs templates and I see "404 not found error" in network tab of firebug.
Eg: request http://localhost:8080/myapp/%7B%7Bstylesheet.href%7D%7D, status 404
When page is completely loaded, it does substitution of template values and loads proper css.
Is there a way to avoid 404 error and make it load css after angularjs processing?

You should use ng-href instead of href.
<link ng-repeat="stylesheet in stylesheets" ng-href="{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" />
Example

I made a AngularJS service to use easily the #Artem solution.
It's here on GitHub.

There's a another option using $route.resolve and promises. This will wait until the CSS is actually loaded not only added to the head (after that the browser just starts retrieving the file and depending on CSS size can cause page reflow).
// Routing setup
.config(function ($routeProvider) {
$routeProvider
.when('/home', {
controller: 'homeCtrl',
templateUrl: 'home.tpl.html'
}).when('/users', {
controller: 'usersCtrl',
controllerAs: 'vm',
templateUrl: 'users.tpl.html',
resolve: {
load: ['injectCSS', function (injectCSS) {
return injectCSS.set("users", "users.css");
}]
}
}).otherwise({
// default page
redirectTo: '/home'
});
})
Service implementation
.factory("injectCSS", ['$q', '$http', 'MeasurementsService', function($q, $http, MeasurementsService){
var injectCSS = {};
var createLink = function(id, url) {
var link = document.createElement('link');
link.id = id;
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
return link;
}
var checkLoaded = function (url, deferred, tries) {
for (var i in document.styleSheets) {
var href = document.styleSheets[i].href || "";
if (href.split("/").slice(-1).join() === url) {
deferred.resolve();
return;
}
}
tries++;
setTimeout(function(){checkLoaded(url, deferred, tries);}, 50);
};
injectCSS.set = function(id, url){
var tries = 0,
deferred = $q.defer(),
link;
if(!angular.element('link#' + id).length) {
link = createLink(id, url);
link.onload = deferred.resolve;
angular.element('head').append(link);
}
checkLoaded(url, deferred, tries);
return deferred.promise;
};
return injectCSS;
}])
You could add a timeout using tries if this is something you would like to include.
Check out this post for more details:https://medium.com/angularjs-meetup-south-london/angular-dynamically-injecting-css-file-using-route-resolve-and-promises-7bfcb8ccd05b

I've created a very simple example of how make a conditionaly css addition
<link rel="stylesheet" ng-if="lang_direction == 'rtl'" ng-href="{{lang_direction == 'rtl' ? 'css/rtl.css' : ''}}" >

For anyone wishing to create truly dynamic CSS at runtime with AngularJS this is what I used.
index.html
<head>
<style type="text/css" ng-bind-html="styles"></style>
</head>
cssService
this.$rootScope.myStyles = ".test { color : red; }";
This is just an example, it may be better for you to put the styles into an indexController if you have one and keep it off the $rootScope

Related

click to download Jquery HTML dynamic page to PDF in asp.net core

I have a page that is dynamically populated with AJAX once the page is loaded in asp.net core.
I have to use AJAX because I am populating the page from different sources using id parameter to get value and the page is populated. The HTML pages are working fine and the data are properly displayed.
So I tried using Rotativa base on this tutorial, I was able to get the pdf working but the PDF is empty because the page has not loaded before the PDF is generated.
The idea now is that if I could have a button on the page to convert the page to pdf and users can download.
Is there a way to achieve this?
The idea now is that if I could have a button on the page to convert the page to pdf and users can download.
Is there a way to achieve this?
You can use PDF.Core package to implement it, you can find it in Nuget.
Below is a working demo:
View:
<h1>Test</h1>
<input id="download" type="button" value="download" />
<script src="~/lib/jquery/dist/jquery.js"></script>
<script>
$(function () {
$("#download").on("click", function () {
var markup = document.documentElement.innerHTML;
$.ajax({
type: "post",
url: "/Home/Download",
data: { "htmlContent": markup },
success: function () {
window.location = '#Url.Action("Download", "Home")';
}
})
})
});
</script>
Controller
public IActionResult Download(String htmlContent)
{
if (!string.IsNullOrEmpty(htmlContent))
{
IronPdf.HtmlToPdf Renderer = new IronPdf.HtmlToPdf();
Renderer.RenderHtmlAsPdf(htmlContent).SaveAs("html-string.pdf");
return Ok();
}
else
{
var stream = new FileStream(Path.Combine(_hostingEnvironment.ContentRootPath, "html-string.pdf"), FileMode.Open);
return new FileStreamResult(stream, "application/pdf");
}
}
Our team Chose IronPDF and it worked based on the MVC tutorial posted:
https://ironpdf.com/docs/questions/asp-net-mvc-pdf-binary/
public FileResult GetHTMLPageAsPDF(long id) {
//Create a PDF Document
var PDF = Renderer.RenderHtmlAsPdf("<h1>html as required</h1>");
//return a pdf document from a view
var content = PDF.BinaryData;
Response.AppendHeader("Content-Length", content.Length.ToString());
Response.AppendHeader("Content-Disposition", "inline; filename=Document_" + id + ".pdf");
return File(content, "application/pdf;");
}
I later went for pugpdf because I couldnt afford IronPDF. Its a good package though.
Pugpdf did the job for me
public async Task<IActionResult> Download(String htmlContent)
{
if (!string.IsNullOrEmpty(htmlContent))
{
var renderer = new HtmlToPdf();
renderer.PrintOptions.Title = "Statement";
var pdf = await renderer.RenderHtmlAsPdfAsync(htmlContent);
pdf.SaveAs(Path.Combine(_webHostEnvironment.ContentRootPath, "html-string.pdf"));
return Ok();
}
else
{
var stream = new FileStream(Path.Combine(_webHostEnvironment.ContentRootPath, "html-string.pdf"), FileMode.Open);
return new FileStreamResult(stream, "application/pdf");
}
}

Unexpected token < when using reactjs app

I have been following a video tutorial which apparently using JSBin to show its code, when I tried out the code locally then it does not work for me. Could someone please help me to figure out what is the issue.
Below is the code
<!DOCTYPE html>
<html>
<head>
<title>Redux basic example</title>
<script src="https://unpkg.com/redux#latest/dist/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.0/react.min.js" type = "text/babel"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.0/react-dom.min.js" type = "text/babel"></script>
</head>
<body>
<div id='root'>
</div>
<script>
const counter = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
};
const Counter = ({ value}) => (<div>{value}</div>);
const { createStore } = Redux;
var store = createStore(counter);
const render = () => {
ReactDOM.render(
<Counter value={store.getState()} onIncrement = {
() => store.dispatch({type: 'INCREMENT'})
}
onDecrement = {
() => store.dispatch({type: 'DECREMENT'})
} />,
document.getElementById('root')
);
};
store.subscribe(render);
render();
</script>
</body>
</html>
You are using JSX in your code, which needs to be transpiled into standard javascript before executing it in the browser.
const Counter = ({ value}) => (<div>{value}</div>);
Look into Babel
The browser is complaining about the JSX code. You should transpile it to regular Javascript before including it in your page. There are several ways to do: Webpack, Babel...
Have a look to create-react-app npm package to get started fast: https://github.com/facebookincubator/create-react-app

invoke node.js server-side service in angularjs. getting following error. Error: [$injector:nomod] Module ‘angularjsNodejsTutorial’ is not available

This is the error: Error: [$injector:modulerr] Failed to instantiate module angularjsNodejsTutorial due to:
Error: [$injector:nomod] Module ‘angularjsNodejsTutorial’ is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.” exception. from browser or postman when I hit localhost:3000/dirPath then I get the data back but not through this html file.
//here are the files: index.html, app.js(angularjs) and index.js(node)
//index.html
<!DOCTYPE html>
<html ng-app="angularjsNodejsTutorial">
<head>
<title>Integrating AngularJS with NodeJS</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.2/angular.js"></script>
<script src="../node_modules/angular-route/angular-route.min.js"></script>
</head>
<body >
<div ng-controller="myController">
<ul >
<li> The Files Are: {{data}}</li>
</ul>
</div>
<script src="../public/javascripts/app.js" type="text/javascript"></script>
<script src="../node_modules/angular-route/angular-route.js"></script>
</body>
</html>
//(AngularJS Client-Side)app.js
var app = angular.module('angularjsNodejsTutorial',['ngRoute']);
app.controller('myController', function($scope, $http) {
$scope.data = [];
var request = $http.get('/dirPath');
request.success(function(data) {
console.print("The files from this directory are:", data);
$scope.data = data;
});
request.error(function(data){
console.log('Error: ' + data);
});
});
//Server-side node.js index.js file
var express = require('express');
var router = express.Router();
var path = require('path');
/* GET home page. */
router.get('/', function(req, res, next) {
res.sendFile(path.join(__dirname, '../', 'views', 'index.html'));
});
router.get("/dirPath", function(req, res) {
var fs = require("fs");
var dir = '/Users/swapnil/Documents/Test';
fileList = [];
var files = fs.readdirSync(dir);
for(var i in files){
if (!files.hasOwnProperty(i)) continue;
var name = dir+'/'+files[i];
if (!fs.statSync(name).isDirectory()){
fileList.push(name);
}
}
return res.send(fileList);
});
module.exports = router;
I think you are over thinking your route process, this will do the basics:
function config ($routeProvider, _) {
$routeProvider.
when('/Order', {
templateUrl: '../modern/sections/views/view.html',
controller: 'Controller as ctrl',
caseInsensitiveMatch: true,
resolve: {
data: function(Factory){
var view = window.location.href.match(/OrderID=(.*)#/) || 'undefined';
if(Id === 'undefined'){
return Factory.createOrder();
}else{
return Factory.currentOrder(OrderId[1]);
}
}
}
})
.otherwise({
redirectTo: '/'
});
}
FYI: Resolve calls Factories where i have services setup to get my API data.
Sorry if this is not direct, i copied this over from a current working project i have.
Resolved the issue by exposing the server side functionality as a RESTful API instead of node and modified my controller by adding appropriate header info for content type and implemented CORS filter on the server side.
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
// CORS "pre-flight" request
response.addHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers",
"X-Requested-With,Origin,Content-Type, Accept");
filterChain.doFilter(request, response);
}
Thanks everyone for taking time to look at my issue and offering help. Appreciate.
Check your modules and make sure they are being loaded appropriately. It's a very common error when you start out. Check your html file and make sure all the modules names are spelled correctly.
If your modules are sharing logic it's common to accidentally miss spell the module name. This case I normally copy and past it over.
Also look at your logic behind the module and make sure you are declaring it properly.

angularJS How can I ignore certain HTML tags?

I got this error because one of the users added in his post <3
Error: [$sanitize:badparse] The sanitizer was unable to parse the following block of html: <3
I wrote code that ng-bind-html ="Detail.details"
I want him to be taken only <a> tag and tag <br />
Is that possible?
Thank you!
You can create filter which will sanitize your html.
I used in it strip_tags function
http://phpjs.org/functions/strip_tags/
angular.module('filters', []).factory('truncate', function () {
return function strip_tags(input, allowed) {
allowed = (((allowed || '') + '')
.toLowerCase()
.match(/<[a-z][a-z0-9]*>/g) || [])
.join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, '')
.replace(tags, function($0, $1) {
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
});
}
});
controller:
angular.module('myApp', ['filters'])
.controller('IndexController', ['$scope', 'truncate', '$sce', function($scope, truncate, $sce){
$scope.text="";
$scope.$watch('text', function(){
$scope.sanitized = $sce.trustAsHtml(truncate($scope.text, '<a><br>'));
});
}]);
view:
<div ng-bind-html="sanitized"></div>
http://plnkr.co/edit/qOuvpSMvooC6jR0HxCNT?p=preview
I had the same problem and fixed it by using $sce.trustAsHtml, see this
$scope.body = $sce.trustAsHtml(htmlBody);
// In html
<div ng-bind-html="body">body</div>
It fix the issue
To preserve existing ng-bind-html behaviour without crashing you can catch the $sanitize:badparse exception.
The ngBindHtml component internally uses the ngSanitize service. Inject $sanitize into your controller and catch it.
The advantage of this versus the $sce.trustAsHtml methods is that $sanitize does not introduce any potential security holes (eg. javascript injection).
Controller (inject $sanitize):
$scope.clean = function (string) {
$scope.clean = function(string) {
try {
return $sanitize(string);
} catch(e) {
return;
}
};
};
This method could be improved with a cache of the last known good value.
View:
<div ng-bind-html="clean(body)"></div>

How to configure an AngularJS app at load time?

I want to do something like this (but obviously not this exactly, because this function doesn't work this way)
angular.bootstrap( $("#myelement"), ['myModule'], {foo: bar} );
I want to pass in a configuration object, since we may want to have more than one instance of the app on a page, with different settings, etc. All I can think of are ugly workarounds. I'm thinking the best thing would be to override an "Options" service of my own making, but I still can't figure out the proper way to do that (tersely).
Thanks in advance!
How about you try something like this:
angular.module('configFoo', []).run(function() {});
angular.module('configBar', []).run(function() {});
angular.bootstrap(myEl, ['myModule', 'configFoo']);
angular.bootstrap(myOtherEl, ['myModule', 'configBar']);
http://docs.angularjs.org/api/angular.Module for all available module methods (you're probably only interested in .run() and .config())
Here is a working code:
http://jsfiddle.net/x060aph7/
angular.module('myModule', [])
.controller('myController', function($scope,myConfig) {
$scope.name = 'inst '+myConfig.foo;
})
;
var aConfig = [{foo:1},{foo:2},{foo:3}];
aConfig.forEach(function(config){
angular.module('fooConfig',[]).value('myConfig', config);
angular.bootstrap(getDiv(), ['myModule','fooConfig']);
});
function getDiv(){
var mDiv = document.createElement('div');
mDiv.setAttribute('ng-controller','myController');
mDiv.innerHTML = '<span>{{name}}</span>';
document.body.appendChild(mDiv);
return mDiv;
}
The following example helped us out bootstrapping a widget to a page. First a div is made - with a bit of jQuery - for the widget to load a template with an ng-include, it is controlled by WidgetLogoController. Next a module WidgetConfig is created that holds the widget's configuration.
$('#pageWidget').html(`<ng-include src="'/dist/templates/widgetLogo.html'"></ng-include>`)
.attr('ng-controller','WidgetLogoController');
var widgetConfig = {
'widgetId': data.pageWidgetId,
'areaId': data.area,
'pageId': data.pageId
};
angular.module('WidgetConfig', []).value('WidgetConfig', widgetConfig);
angular.bootstrap(document.getElementById('pageWidget'), ['Widget', 'WidgetConfig']);
Widget module includes the WidgetConfig configuration but also has a spot for it own in CONFIG:
(function (window, angular) {
'use strict';
window.app = angular.module('Widget', ['ngFileUpload', 'WidgetConfig'])
.constant('CONFIG', {
BASE_URL: 'http://osage.brandportal.com/'
});
})(window, angular);
WidgetController can access CONFIG and WidgetConfig.
(function (app) {
'use strict';
app.controller('WidgetLogoController', ['CONFIG', 'WidgetConfig',
function(CONFIG, WidgetConfig){
console.log('---WidgetLogoController');
console.log('CONFIG', CONFIG);
console.log('WidgetConfig', WidgetConfig);
}]);
}(app));
What about:
Load config and than load angular:
angular.element(document).ready(() => {
$.get('config', // url to my configuration
{},
function (data) {
window.config = data;
angular.bootstrap(document, ['myApp']);
}
);
});
Access the config:
angular.module('myApp').run(myAppRun);
function myAppRun($window) {
$window.config; // here I have config
}