Directive on ng-repeat not working - javascript

I have this directive:
.directive('img', function () {
return {
restrict: 'E',
link: function (scope, elem, attr) {
if (attr.src && attr.type === "extension"){
var url = "chrome-extension://" + chrome.runtime.id + attr.src
// console.log(url)
elem[0].removeAttribute('src')
// elem[0].setAttribute("src", url)
// elem[0].dataset.ngSrc = url
console.log(elem[0])
console.log(elem[0].src)
}
}
};
})
profile.html:
<tr ng-repeat="integration in profile.integrations">
<td>
<!-- <h3>{{integration.provider}}</h3> -->
<img type="extension" src="/images/icons/{{integration.provider}}.png" alt="" width="50px">
</td>
</tr>
My console log still shows the src and does not remove it nor will replace it. It's something to do with ng-repeat because this works on another image perfectly.

You are basically recreating one of angular's convenience directives that was made for the situation where you use an expression in the src attribute. Use ng-src instead of src, and you won't need your img directive
ng-src doc: https://docs.angularjs.org/api/ng/directive/ngSrc
Using Angular markup like {{hash}} in a src attribute doesn't work
right: The browser will fetch from the URL with the literal text
{{hash}} until Angular replaces the expression inside {{hash}}. The
ngSrc directive solves this problem.
<img type="extension"
ng-src="/images/icons/{{integration.provider}}.png" alt="" width="50px">
As noted by runTarm in the comments this does not add the chrome-extension protocol to the image url, to do that you need to do two things
Add chrome-extension to the AngularJS whitelist, otherwise Angular will prepend unsafe: to the url
Add the expression to the ng-src to add the protocol
Adding to the whitelist
//modified from http://stackoverflow.com/a/15769779/560593
var app = angular.module( 'myApp', [] );
app.config( ['$compileProvider', function( $compileProvider ) {
//adds chrome-extension to the whitelist
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
]);
app.run(function($rootScope){
//put the id on the rootScope so we can use it in expression
$rootScope.extensionUrl = "chrome-extension://"+chrome.runtime.id;
});
HTML
<img ng-src="{{extensionUrl}}/images/icons/{{integration.provider}}.png" alt="" width="50px">
JSFiddle

Related

href is not automatically generated when ui-sref is used

I'm trying to use angular js ui router to route my page.
Here's the code that I've written:
<a ui-sref="transporterEditDetails({companyId: '{{d.companyId}}' )" style="cursor: pointer">{{d.companyName}}</a>
Here's the js code :
var routerApp = angular.module('routerApp', ['ui.router','ui.bootstrap','xeditable','google.places','angular-loading-bar','ui.select','angularUtils.directives.dirPagination','notificationsTruckway','ui.tinymce'])
routerApp.config(function($stateProvider, $urlRouterProvider,cfpLoadingBarProvider) {
$stateProvider
.state('transporterEditDetails', {
url: '/transporterEditDetails/:companyId',
controller:'TransporterEditDetailsController',
templateUrl: 'Transporter-editDetails.html'
})
routerApp.controller('TransporterEditDetailsController', function($scope,$location,$http,usersFactory,$stateParams) {
$scope.companyId = $stateParams.companyId;
}
I don't know what's wrong in my code, I'm unable to get the href attribute which should be generated automatically.
Did you try without quotes and parentheses
<a ui-sref="transporterEditDetails({companyId: d.companyId})" style="cursor: pointer">{{d.companyName}}</a>
You don't need to use {{}}(interpolation) inside ui-sref. Also remove '(wrapped single quote)
ui-sref="transporterEditDetails({companyId: d.companyId })"

Render html tags from AngularJS object

I've looked up similar questions on how to render html tags within angularJS but none of them worked for me because I am using ng-repeat. Anyone have any advice.
<div ng-repeat="o in SurveyResults.SurveyQuestions">
{{o.SurveyQuestionId}}
{{o.QuestionText}}
</div>
My o.QuestionText has data such as <b>How well do we deliver on our promises?</b> <br />Consider our responsiveness to your requests, ability to meet deadlines and <i>accountability for the outcomes</i>.
It displays the tags without rendering the tags.
I did try putting this in my JS file
homePage.directive('htmlSafe', function($parse, $sce) {
return {
link: function(scope, elem, attr) {
var html = attr.ngBindHtml;
if(angular.isDefined(html)) {
var getter = $parse(html);
var setter = getter.assign;
setter(scope, $sce.trustAsHtml(getter(scope)));
}
}
};
});
Then setting html bind in the repeat as such
<div ng-repeat-html="o in SurveyResults.SurveyQuestions">
{{o.SurveyQuestionId}}
{{o.QuestionText}}
</div>
Any other advice?
Just use ng-bind-html, you don't need new directive
<div ng-repeat="o in SurveyResults.SurveyQuestions">
{{o.SurveyQuestionId}}
<span ng-bind-html="o.QuestionText"></span>
</div>

angular directive , how is directive magically rendered when I do not see a call to it?

in a Directives.js file I see the following code
app.directive('arpLink', function ($http) {
return {
scope: {
link: '=',
className: '#cl'
},
replace: true,
template:
'<span ng-click="clicked()">' +
'<a class="{{ className }}" target="_blank" ng-href="{{ link.url }}">{{ link.title }}</a>' +
'</span>',
link: function (scope, el, attrs) {
scope.clicked = function () {
if (this.link !== undefined && this.link.id !== undefined) {
$http.put('api/links/' + this.link.id, { hitcount: true });
}
};
}
};
});
This ends up displaying a link button on the home page in which I want to remove this button link , normally I would simply delete a line or two of code in other web applications.
I see that in chrome that code does render out to
<span ng-click="clicked()" cl="btn-blue btn-large" arp-link="" link="link" class="ng-isolate-scope"><a data-blah="adf" class="btn-blue btn-large" target="_blank" ng-href="http://eso/docold/" href="http://eso /docold/">Old DOC Home Page</a></span>
<a class="btn-blue btn-large" target="_blank" ng-href="http://eso/docold/" href="http://eso/docold/">Old DOC Home Page</a>
I don't see any files when searching that even call the directive "arpLink"
How is this directive called and how can I stop it from being called?
It's called using the attribute shown in the markup arp-link
The name of the directive is a camelCase version of that attribute.
Directives can be implemented using class, attriute, element and even an html comment
<div class="arp-link"></div>
<arp-link></arp-link>
<div arp-link></div>
<!-- arp-link -->
The restrict option can be used to define which method would reference specific directive you register
To stop it , don't include the directive declaration and/or remove the attribute. Removing only one of them will have no adverse impact...the directive will just never be invoked

AngularJS : Compile directives inside HTML returned by an API

So I have access to a REST API that I am hitting, that returns the following pre-generated HTML:
<p class="p">
<sup id="John.3.16" class="v">16</sup>
<span class="wj">“For </span>
<span class="wj">God so loved </span>
<span class="wj">the world,</span>
<span class="wj">that he gave his only Son, that whoever believes in him should not </span>
<span class="wj">perish but have eternal life.</span>
</p>
This has presented an interesting new challenge for me in my learning of AngularJS. I have no control over the HTML that is returned from the API, since it's not an API that I built.
What I'm trying to do (and this could be the completely wrong approach) is to build a class directive on the "v" class, so that I can add an ng-click attribute to the verse number and pass the verse information on to another part of my application.
Below is the code I currently have, which doesn't seem to do anything, though I thought it would.
var app = angular.module('ProjectTimothy');
app.filter("sanitize", ['$sce', function($sce) {
return function(htmlCode){
return $sce.trustAsHtml(htmlCode);
}
}]);
app.controller("timothy.ctrl.search", ['$scope', '$http', function($scope, $http){
$scope.url = "http://apiendpoint.com/";
$scope.copyright = "";
$scope.search = function() {
// Make the request to the API for the verse that was entered
// Had to modify some defaults in $http to get post to work with JSON data
// but this part is working well now
$http.post($scope.url, { "query" : $scope.searchwords, "version": "eng-ESV"})
.success(function(data, status) {
// For now I'm just grabbing parts of the object that I know exists
$scope.copyright = data.response.search.result.passages[0].copyright;
$scope.result = data.response.search.result.passages[0].text;
})
.error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}]);
app.directive("v", ['$compile', function($compile) {
return {
restrict: 'C',
transclude: true,
link: function(scope, element, attrs) {
element.html("<ng-transclude></ng-transclude>").show();
$compile(element.contents())(scope);
},
scope: { id:'#' },
/*template: "<ng-transclude></ng-transclude>",*/
replace: false
};
}]);
HTML Template that is being populated with the HTML returned by API:
<div class="bible_verse_search_container" ng-controller="timothy.ctrl.search">
<div class="input-group">
<input type="text" class="form-control" placeholder="Bible Verse To Read (i.e. John 11:35)" ng-model="searchwords">
<span class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="search()">Search</button>
</span>
</div>
<div class="well" ng-show="copyright" ng-bind-html="copyright | sanitize"></div>
<div ng-bind-html="result | sanitize"></div>
</div>
So What I was hoping would happen would be that the HTML is populated into the bottom div that binds the html, and then somehow $compile would be called to convert the "v" class sup's into directives that I can modify. Again, I'm pretty new to Angular, so there may be a super easy way to do this like most other things in Anguler that I just haven't found yet.
Really, the end goal is that each verse number is converted into a directive of its own to be able to make it clickable and access the id attribute that it has so that I can send that information with some user content back to my own API.
This feels like a lot of information, so let me know if anything is unclear. I'll be working on it, so if I figure it out first, I'll be sure to update with an answer.
IN PROGRESS
Checked out this question: https://stackoverflow.com/a/21067137/1507210
Now I'm wondering if it would make more sense to try and convert the section where the verse is displayed into a directive, and then making the search controller populate a scope variable with the HTML from the server, and then use that as the template for the directive... think think think
I think your second approach--convert the section where the verse is displayed into a directive--would be a nice way of doing it.
You could replace this:
<div ng-bind-html="result | sanitize"></div>
with a directive like this:
<verse-display verse-html="{{result}}"></verse-display>
The directive definition would look like this:
app.directive('verseDisplay', ['$compile', function($compile) {
function handleClickOnVerse(e) {
var verseNumber = e.target.id;
// do what you want with the verse number here
}
return {
restrict: 'E',
scope: {
verseHtml: '#'
},
replace: true,
transclude: false,
template: '<div ng-bind-html="verseHtml | sanitize"></div>',
link: function(scope, element, attrs) {
$compile(element.contents())(scope);
element.on('click', '.v', handleClickOnVerse);
}
};
}]);
So you could apply your own click handler to the element.
Here's a fiddle. (Open the console to see the verse number getting logged out.)
Probably the most unwisest of things I have posted here, but it's pretty cool code. I do not know if I recommend actually running this, but here's a jsfiddle
So one of the reasons I call this unwise is because the injected code will run any directives you have not just the one you wanted. There may be many other security risks beyond that as well. But it works fantastically. If you trust the HTML you are retrieving then go for it.
Check out the fiddle for the rest of the code:
function unwiseCompile($compile) {
return function (scope, element, attrs) {
var compileWatch = scope.$watch(
function (scope) { return scope.$eval(attrs.unwiseCompile); },
function (unwise) {
element.html(unwise);
$compile(element.contents())(scope);
// for better performance, compile once then un-watch
if (scope.onlyOnce) {
// un-watch
compileWatch();
}
});
};
}

Cleaner way to provide different href URLs on ng-repeat?

I am using hide and show to display different href links on ng-repeat depending if the userid exists on routeParams. The image inside the href tag is the same for both conditions.
For example:
<div ng-repeat="trip in trips">
// If userid exists, the user is authenticated so provide userid on url
<a ng-show="userid" ng-href="#/trip/{{trip.tripid}}/user/{{userid}}">
// Same exact image as in ng-hide
<img ng-src="trip.jpg" width="200" height="200" alt="Trip Photo">
</a>
// If userid does not exist, the user is not authenticated so no userid on url
<a ng-hide="userid" ng-href="#/trip/{{trip.tripid}}/">
// Same exact image as in ng-show
<img ng-src="trip.jpg" width="200" height="200" alt="Trip Photo">
</a>
</div>
I feel there is a less redundant way to avoid using ng-show and ng-hide just to change the URL and writing duplicate image tags inside the anchor tag.
You could use a filter and pass the data, as such:
filter('urlFormatter', function() {
return function(tripid, userId) {
if(angular.isDefined(userId)) {
return "#/trip/"+tripid+"/user/"+userId;
}
return "#/trip/"+tripid+"/";
}
});
Then use it in your attribute as:
ng-href="trip.tripid | urlFormatter:userid"
Or
ng-href="trip.tripid | urlFormatter"
You can also use the $location service and change your path:
$location.path('/path/foo/bar')
With this method, you would bind an ng-click to the tag and apply the location logic within the controller, that is, if the userid is passed.
An example:
<a ng-click="goSomewhere(trip.tripid, userid)">
Where the controller would be
controller('demoCtrl', ['$scope', '$location', function($scope, $location) {
$scope.goSomewhere = function(tripid, userid) {
if(angular.isDefined(userid) {
$location.path("/trip/"+tripid+"/user/"+userid);
}
else {
$location.path("/trip/"+tripid+"/");
}
}
}]);

Categories

Resources