Rendering SVG via Angular ng-bind-html - angularjs

In a recent question (Reference Angular binding from javascript) I asked how to bind a generated SVG to a specific div. I got two good answers which I've been tinkering with since.
I'm trying to display a SVG image which gets built based on specific properties.
Basically I have a tiny Angular script which includes a number of functions for generating svg code, e.g.:
.controller('ctlr',['$scope','$sce', function($scope,$sce) {
$scope.buildSvg(width, height, obj){
var img = ...call a lot of svg-functions
return $sce.trustAsHtml(img);
}
My intention is to include this on the web page via:
<div ng-bind-html="buildSvg(60,140,item)">svg should go here</div>
However, I have a hard time getting this to work, at least with the javascript generated SVG tags, it works if I copy paste the img code into another $scope variable (and add a lot of escaping) and then include it via ng-bind-html.
As the code is available on Plunker here:
Example
I get the following error:
Error: [$sce:itype] http://errors.angularjs.org/1.4.0/$sce/itype?p0=html
at Error (native)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:6:416
at $get.trustAs (http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:140:269)
at Object.$get.d.(anonymous function) [as trustAsHtml] (http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:142:444)
at m.$scope.buildSvg (file:///C:/Dropbox/workspace/famview/public/javascripts/svgController.js:37:16)
at fn (eval at <anonymous> (http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:210:400), <anonymous>:2:301)
at Object.get (http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:115:108)
at m.$get.m.$digest (http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:131:221)
at m.$get.m.$apply (http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:134:361)
at http://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js:19:362
Do I need to somehow inform $sce to escape string literals in the SVG tags?

I think your problem is more regarding to how you are binding the object in html. I mean, the fact that you are returning the object through a function can be the cause of the problem. Moreover, if you see angular logs, they are complaining about "digest" and "apply", that is the life cycle of all the binding in Angular.
So basically, you will be able to solve that doing $sce.trustAsHtml(SVGSTRING) as you did, but saving it before in a variable like $scope.svg.
SCRIPT.js
$scope.svg = $sce.trustAsHtml(SVGSTRING);
And in the html should be as simple as that
HTML
<div ng-bind-html="svg"></div>
It should work, I am putting you a plunker in which you can see how it even works retrieving data from a request
http://embed.plnkr.co/gQ2Rrn/
Hope this helps

Related

Can i access the ng-app value in a protractor test?

I simplified the code i need to test to this:
<html ng-app="home" ng-strict-di=""><head>....
And i am running some protractor tests, i want to access the value of ng-app so i can compare and see which app is running in each page.
I have tried
var appName = element(by.xpath('/html/#ng-app'))
but it is not returning a usable promise or text i can compare with
appName.getText().then(function(name) {
expect(name).toBe('home')
});
But protractor complains:
InvalidSelectorError: invalid selector: The result of the xpath expression "/html/#ng-app" is: [object Attr]. It should be an element.
So i'm a bit baffled as how can i access my angular app name from protractor to test for app running independently of localization of labels.
Any insight into this enigma?
And it seems like magically, when you order your thoughts to formulate the question, the answer comes to you.
the trick is to get the html as element
var appNameHtml = element(by.xpath('/html'))
and then, in the test, get the ng-app attribute and use it:
appNameHtml.getAttribute('ng-app').then(function(value) {
expect(value).toBe('home');
});
And bingo, you can extract the app name.
Maybe this is a very basic question, but it was driving me insane :)
your answer will suffice I guess in this case. But just wanted to highlight a more generic approach in case ng-app may reside not only on html element.
var elementWithNgApp = element(by.css('*[ng-app]'))
And then wanted to add one more thing. You need not resolve the promise of getAttribute to do an expect on its value. Jasmine resolves the promise for you. You can have something like this
expect(elementWithNgApp.getAttribute('ng-app')).toBe('home');

Setting Angular2-style attributes on HTML node

I am migrating an application from Angular1 to Angular2, in which I used the svg.js library to draw an SVG. Some elements of the SVG contained "ng-click" directives, that I made the SVG library print to the final SVG.
That worked alright in Angular1, however, now the directive is called "(click)" and everything breaks. I traced the SVG sources and found that the following call
this.node.setAttribute(attr, value.toString())
results in the following error message:
VM22161:1 Uncaught DOMException: Failed to execute 'setAttribute' on 'Element': '(click)' is not a valid attribute name.(…)
Any ideas to solve that problem? Patching the SVG source would be ok for me, if there's no other way.
A valid and quick workaround is to use the binding syntax of Angular2, that allows to use "on-click" instead of "(click)".

ChartistJS : Converting jQuery solution to AngularJS

I am using Chartist JS for my charts in my Angular JS app. The issue is I am seeing this here. There is a JS bin that highlights the issue. The author gives a solution for it. The solution is doing DOM manipulations in Jquery which is easy to do. However with AngularJS the way you manipulate the DOM is via Directives. I have created a plunker here which highlights the same issue in Angular JS but I am confused as to how to put the solution provided by author into my Angular code.
Here is the solution
$('[data-tab]').on('toggled', function (event, tab) {
tab.find('.ct-chart').each(function(i, e) {
e.__chartist__.update();
});
});
Edit: As requested the JSFiddle is updated, so what I am trying to do is. I have three different tabs and three different graphs, whenever I click on them I should see the respective graph. To make the tab behavior possible I have written a basic code using scope and model. which facilitates the changing of tabs. The issue is that the chart is getting created for first or default tab but not for the second and third tab. There is a solution given by the author but I don't know how to implement that in AngualrJS
the jQuery solution that you post is basically finding all the chart references and then doing DOM manipulation and call the update() function.
The key is how to find the chart to update in Angular.
In this case, you can assign a variable when you create a chart. For example:
var chart4 = new Chartist.Bar('#chart4', data1);
var chart5 = new Chartist.Bar('#chart5', data2);
Now you have the reference of the chart. All you have to do is to call update() function to render the chart again.
if (value === "allDrivers") {
$scope.tab = "All";
chart4.update();
}
Here is the working plunker
One thing I like to point out is: right now you need to double click the tab in order to see the chart is being rendered or you resize the browser window. I am still trying to find a way to fix this. But at least this approach gives you an idea how to convert the jQuery solution to Angular solution.
I was able to solve this using angular.element() method. So if you wish you use jquery in your angular code. You have to do this via angular.element method. But make sure to include jquery before angular in your index.html
If jQuery is available, angular.element is an alias for the jQuery
function. If jQuery is not available, angular.element delegates to
Angular's built-in subset of jQuery, called "jQuery lite" or jqLite.
I did not know this. From here it was learning for me. Following advice of #pieterjandesmedt from this post. I was able to do this. For other people who want to learn how this works. I have created a GitHub repo which gives a solution to this issue. The link for problem is given in the question. Hope that helps

Using ngAnimate on a Array of data

I have made a basic carousel and and would like to use ng-animate to animate when entering and leaving each index in an array. The templates in Carousel.Data will soon contain html data. I would like to also know what would be the best way of passing html in an array so the user can click next and be presented with the next html template.
I have ng-animate loaded in the DOM with angular version 1.21 but I get an error when I try to inject the dependancy
var App = angular.module('App', ['ngAnimate']);
Here is what I have so far:-
http://codepen.io/anon/pen/wajRog
any help would be much appreciated
It's difficult to tell in CodePen but be sure that angular.min.js is loaded before angular-animate.js. Make sure the order is as follows:
https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.9/angular.min.js
https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.9/angular-animate.js
https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.9/angular-touch.js
You can rearrange the resources in code pen by clicking the hamburger icon near each resources and clicking/dragging to the top. If i got to https://docs.angularjs.org/api/ngAnimate for example and open the plunker for anchoring. If you move angular-animate cdn script tag above angular.min.js, it will give an injection error.
Let me know if that works.

How to avoid angular.js preemptively fetching data until certain actions

Just started out using angular.js and implemented a directive that reads from a property in the scope that's defined only when a button is clicked. The UI looks fine also because the directive part is only shown when the button is clicked. However in the console when the page is first loaded there is an error message saying "Cannot read property someProperty of undefined".
I must be violating some angular principles but I'm not sure how to fix it. Thanks for the help!
Note: Didn't do a fiddle because this is a general question.
Generally speaking, if you have code patten like
$scope.myObject.property
then you could see the error when myObject is undefined.
One possible way to eliminate the error the code work is make sure the object is always initialized when any property is intended to be referred.
You can put this line before the property is referred.
$scope.myObject = {};
Or do
if($scope.myObject !== undefined){
...
}
There is no rocket science here.

Resources