How to generate url encoded anchor links with AngularJS? - angularjs

<a href="#/search?query={{address}}" ng-repeat="address in addresses">
{{address}}
</a>
generates links that are not url encoded if I understand correctly. What is the proper way to encode #/search?query={{address}}?
Example address is 1260 6th Ave, New York, NY.

You can use the native encodeURIComponent in javascript.
Also, you can make it into a string filter to utilize it.
Here is the example of making escape filter.
js:
var app = angular.module('app', []);
app.filter('escape', function() {
return window.encodeURIComponent;
});
html:
<a ng-href="#/search?query={{address | escape}}">
(updated: adapting to Karlies' answer which uses ng-href instead of plain href)

#Tosh's solution will return #/search?query=undefined if address is undefined in
<a ng-href="#/search?query={{address | escape}}">
If you prefer to get an empty string instead for your query you have to extend the solution to
var app = angular.module('app', []);
app.filter('escape', function() {
return function(input) {
if(input) {
return window.encodeURIComponent(input);
}
return "";
}
});
This will return #/search?query= if address is undefined.

You could use the encodeUri filter: https://github.com/rubenv/angular-encode-uri
Add angular-encode-uri to your project:
bower install --save angular-encode-uri
Add it to your HTML file:
<script src="bower_components/angular-encode-uri/dist/angular-encode-uri.min.js"></script>
Reference it as a dependency for your app module:
angular.module('myApp', ['rt.encodeuri']);
Use it in your views:
<a href="#/search?query={{address|encodeUri}}">

Tosh's answer has the filter idea exactly right. I recommend do it just like that. However, if you do this, you should use "ng-href" rather than "href", since following the "href" before the binding resolves can result in a bad link.
filter:
'use strict';
angular.module('myapp.filters.urlEncode', [])
/*
* Filter for encoding strings for use in URL query strings
*/
.filter('urlEncode', [function() {
return window.encodeURIComponent;
}]);
view:
<a ng-href="#/search?query={{ address | urlEncode }}" ng-repeat="address in addresses">
{{address}}
</a>

this is a working code example:
app.filter('urlencode', function() {
return function(input) {
return window.encodeURIComponent(input);
}
});
And in the template:
<img ng-src="/image.php?url={{item.img_url|urlencode}}"

Related

How to hide <img> using md-switch

angular-material newbie here. I'm trying to hide some elements in our site using a switch, as instructed by our client. This led me to angular-material's md-switch.
So I tried incorporating it like so...
<md-switch md-no-ink aria-label="switchView" ng-model="switchView">{{switchView}}</md-switch>
And called the value of the switch in my element like this:
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
After testing it though, it didn't hide my <img> even though my switchView changed its value. Am I missing something here?
Other methods I've tried:
Adding ng-change to my md-switch, which called a function that would equate another variable (e.g. $scope.toggleView = $scope.switchView) with switchView's value. $scope.toggleView would then be used in my ng-hide.
ng-hide = "switchView == true".
Any advice would be very much appreciated. Thank you.
UPDATE 1: To test it, I tried hiding the <div> beside my <md-switch> and it worked perfectly. However it's still not working with my <img>.
Further checking revealed that it was inside a <nav> element. However they're both using the same controller. I wonder if that's the problem? I assumed that it shouldn't be a problem because of this.
The structure is like this:
<nav ng-controller="MainController">
<!-- other navigation elements here -->
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
</nav>
<div ng-controller="MainController">
<div>Toggle Switch</div>
<md-switch md-no-ink aria-label="switchView" ng-model="switchView">{{switchView}}</md-switch>
</div>
UPDATE 2: I've added the following code in my JS file because there are plans to hide elements in other pages. It still didn't work.
$scope.onChange = function(value) {
$scope.$broadcast("view_mode", $scope.switchView);
}
$scope.$on("view_mode", function(event, switchValue) {
$scope.viewThis= switchValue;
});
My HTML now looks like this:
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="viewThis"/>
As for the controller, ngMaterial was called in a separate JS file (our main), together with all our dependencies and configs. Hence, this wasn't called inside MainController.
mainApp.js
var app = angular.module('myAppModule', [
// other references here
'ngMaterial'
]);
mySample.js
angular
.module('myAppModule')
.controller('MainController', ['$scope', function ($scope) {
// functions etc. go here
Hope this helps clear things up. Thank you.
Try something like this, its a trivial example but hope it helps. Here's a link to working codepen.
There seems to be a couple ways you could handle this according to the docs:
Angular Material- MD-Switch
function exampleController($scope) {
$scope.secondModel = false;
$scope.onChangeEvent = function(value) {
$scope.imgSource = (value) ? 'http://www.fillmurray.com/300/200' : 'http://www.fillmurray.com/g/155/300';
};
// alternatively: you could set the ternary to empty string value here.
}
angular
.module('BlankApp', ['ngMaterial'])
.controller('exampleController', exampleController);
<md-switch ng-model="switchValue" ng-change="onChangeEvent(switchValue)">
<img ng-src="{{imgSource}}" alt="" />
</md-switch>
<md-switch ng-model="secondModel">
<img src="http://www.fillmurray.com/300/200" alt="" ng-hide="secondModel" />
</md-switch>
Thank you to everyone who gave their inputs. After some research, I managed to solve this problem using factories (source).
I'm sharing my solution so that it may help others who experience the same problem.
HTML:
<nav ng-controller="MainController">
<!-- other navigation elements here -->
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
</nav>
<div ng-controller="MainController">
<div>Toggle Switch</div>
<md-switch md-no-ink aria-label="switchView" ng-model="switchView" ng-change="onChange(switchView)">{{switchView}}</md-switch>
</div>
JS:
angular
.module('myAppModule')
.controller('MainController', ['$scope', 'ViewModeFactory', function ($scope, ViewModeFactory) {
// functions etc. go here
// For client presentation mode
$scope.onChange = function(value) {
ViewModeFactory.setViewMode($scope.switchView);
}
$scope.$watch(function (){
$scope.switchView = ViewModeFactory.getViewMode();
});
}])
.factory('ViewModeFactory', function () {
var data = {isViewMode: ''};
return {
getViewMode: function () {
return data.isViewMode;
},
setViewMode: function(value) {
data.isViewMode = value;
}
};
});
I used factories so that other controllers in our site can use the value passed by md-switch.
Hope this helps.

Define custom angularjs filter not using module model

I have read about the module way of defining a filter:
myApp.filter('filtername', function() {
return function() {
};
});
But what if I am using a simple function controller for my angular app and not as a module?
I.e. all I have at the top of my html is:
<html ng-app ng-controller="MyApp">
and then I include a js file with a simple controller:
function MyApp($scope, $http){
}
but I need to define a custom filter - in my particular case I'm trying to order an ngRepeat using Justin Klemm's object ordering filter. So how can I define a filter not using the module style syntax?
If you want create a new filter in your app you could define the your custom filter like below:
in your js
angular.module("yourModule")
.filter("yourFilretName",function(){
return function(value,arg1,arg2 .....){
var result = [];
// filter processing the object that pass the filtering, must be push on the result array
return result;
}
})
in your template
<your-tag ng-repeat="book in bookList | yourFilretName :arg1:agr2:....:argn>
.....
</your-tag>
<div ng-app="myApp" ng-controller="myCtrl">
<ul>
<li ng-repeat="item in items | filter: myFilter">{{ item }}</li>
</ul>
...
var myApp= angular.module("myApp", []);
myApp.controller('myCtrl', function($scope ) {
function myFilter(item) {
return item == "some condition"
}
})

How to avoid adding prefix "unsafe" to javascript by AngularJs?

I try to add bookmarklet button to my website with generating link in a controller.
Template part:
<a id="bookmarklet"
class="bookmarklet"
onclick="alert('Drag and drop me to the bookmarks bar');return false;"
href="{{getCode()}}">
+ Add
</a>
Controller part:
$scope.getCode = function () {
var code = 'javascript:(function(){s=document.createElement(\'SCRIPT\');s.type=\'text/javascript\';' +
's.src=\'http://localhost:9000/scripts/bookmarklet/bookmarklet.js?x=' + ($scope.user.id) + '\';' +
'document.getElementsByTagName(\'head\')[0].appendChild(s);document.sc_srvurl=\'http://localhost:8080\'})();' ;
return code;
};
But I get following after compilation:
<a class="bookmarklet" href="unsafe:javascript:(function(){s=document.createElement('SCRIPT');s.type='text/javascript';s.src='http://localhost:9000/scripts/bookmarklet/bookmarklet.js?x=5517325d40c37bc2bfe20db6';document.getElementsByTagName('head')[0].appendChild(s);document.sc_srvurl='http://localhost:8080'})();">
+ Add
</a>
Link starts with "unsafe" and I can't get how to tell angular to trust this link.
This answer - Angular changes urls to "unsafe:" in extension page suggests to add protocol to whitelist.
I don't want to disable $sce or adding "javascript" to whitelist protocols as I think it's insecure.
May I tell somehow to angularjs to avoid adding prefix "unsafe" by using $sce? Unfortunately documentation is not clear for me and $sce.trustAsJs(code) haven't helped me.
!EDIT Angular version is 1.4.1.
Try to circumvent the restrictions by writing a custom directive:
var app = angular.module('myApp', []);
app.directive('bookmarklet', function () {
return {
restrict: 'A',
scope: {},
link: function($scope, element, attrs) {
if (element[0].tagName !== 'A') {
return; // simply do nothing (or raise an error)
}
element[0].href = 'javascript:alert("It works in 1.4.1, too!")';
}
};
});
Usage:
<div ng-app="myApp">
<a href="#foo" bookmarklet>click me</a>
</div>
I also created a fiddle demo to test it: http://jsfiddle.net/t0acdxcL/1/

ng-bind-html strips elements attributes

I'm trying to interpolate a string that contains some markup in a template.
In the controller:
$scope.message = "Hello moto <a ui-sref='home.test'>click</a>";
Template:
<div ng-bind-html="message.text"></div>
which renders as:
<div ng-bind-html="message.text" <div="" class="ng-binding">Hello moto <a>click</a></div>
Trying to use the following filter does not help either; the text is simpy escaped for either of the commented choices:
angular.module('test-filters', ['ngSanitize'])
.filter('safe', function($sce) {
return function(val) {
return $sce.trustAsHtml(val);
//return $sce.trustAsUrl(val);
//return $sce.trustAsResourceUrl(val);
};
});
How can I interpolate my string without escaping it nor stripping attributes?
Edit: Plunker http://plnkr.co/edit/H4O16KgS0mWtpGRvW1Es?p=preview (updated with sylwester's version that has reference to ngSanitize
Let have a look here http://jsbin.com/faxopipe/1/edit it is sorted now.
It didn't work because there was another directive inside a tag 'ui-sref',
so you have to use $sce service.
in your js please add method:
$scope.to_trusted = function(html_code) {
return $sce.trustAsHtml(html_code);
and in view :
<p ng-bind-html="to_trusted(message)"></p>
In scenario where you are using ui.router path you must need to use $compile in combination with $sce for your dynamic html so that ui-sref work properly. If you don't do that you'll just see a Link which actually do not work.
e.g <span> Hello moto <a ui-sref='home.test'> Link </a> </span>
//You must need to add boundary conditions, this is just for demonstration
$scope.to_trusted = function(someHTML) {
var compiledVal = $compile(someHTML)($scope);
var compiledHTML = compiledVal[0].outerHTML;
return $sce.trustAsHtml(compiledHTML);
}
And you use like this,
<p ng-bind-html="to_trusted(message)"></p>
Note that your message has to be a valid HTML starting from "<" so if you pass a non HTML to $compile you'll get jqlite error. I used <span> to handle your case.
You missed reference to angular-sanitize.js and you have inject it as well to angular.app
var app = angular.module('plunker', ['ngSanitize']);
the simplest option in to bind html is ng-bind-html :
<li>link ng-html-bind <div ng-bind-html="message"></div></li>
please see Plunkr

AngularJS: Binding to an array, but a specific property?

I'm using this directive (wallop-slider-angularjs) and it requires an array of image urls, but my urls are properties of an array of objects. How can I bind the property in such a way that it is acceptable to the directive?
<div ng-repeat="user in users">
<wallop-slider
data-images="??user.media.mediumURL??"
data-animation="rotate"
data-current-item-index="currentSliderIndex"></wallop-slider>
</div>
media = [{'mediumURL':'http://whatever.com/image.jpg'},{'mediumURL':'http://whatever.com/image2.jpg'}]
I solved this with a custom filter:
app.filter('extractProperty', function() {
return function(array, propertyName) {
return array.map(function(item) { return item[propertyName]; });
};
});
To get an array containing the specific property you must use it like that:
<div ng-repeat="user in users">
<wallop-slider data-images="{{ media | extractProperty:'mediumURL' }}"...></wallop-slider>
</div>
Here is a working example:
http://plnkr.co/edit/WpuOCU?p=preview
Just create a function on the scope which will extract the needed properties from the array. Something like:
scope.getMediumUrls = function(arr) {
return $.map(arr, function(item) { return item.mediumURL; });
}
And then use it on the directive:
<div ng-repeat="user in users">
<wallop-slider data-images="getMediumUrls(user.media)"...></wallop-slider>
</div>
You need to loop through the object along with the key and corresponding properties.
Suppose you have JS object as
var media = [
{'mediumURL':'http://whatever.com/image.jpg'},
{'mediumURL':'http://whatever.com/image2.jpg'}
];
Lets apply for in loop for getting values
for(key in media){
alert(media[key].mediumURL);
}
Here "key" refers to index for media[] and "mediumURL" is the individual corresponding property.
In your case,
<div ng-repeat="user in users">
<wallop-slider
data-images="??user.mediumURL??"
data-animation="rotate"
data-current-item-index="currentSliderIndex"></wallop-slider>
</div>
Note: You were using "user.media.mediumURL", hence it wont work because "media" is not property for each object structure under media[].
You can refer to this link for more details on ng-repeat looping examples.
Edit: If you already have a dependency on jQuery, I would pick Shay Friedmans answer.
I think this will do your trick (without the need for any additional libraries).
// Helper function to pluck the url property from the media items.
function pluckUrls() {
var ret = [], c = $scope.mediaItems.length;
while(c--) {
ret.push($scope.mediaItems[c].url);
}
return ret;
}
// Function that is called each watch cycle. The return value will differ
// if one of the urls has been modified.
function getUniqueWatchValue() {
return pluckUrls().join();
}
// Function that is called whenever one of the urls has been modified.
function watchValueChanged() {
console.log('One of the urls has been modified');
$scope.mediaUrls = pluckUrls();
}
// Hook up the watch.
$scope.$watch(getUniqueWatchValue, watchValueChanged);
Plunker in action can be found here.
Note: I noticed a piece of code in that wallop-slider thingy that is watching a reference to the array, not it contents. I haven't tested it but it probably requires you to recreate the array completely instead of simply adding or removing element from it.
$scope.$watch('images', function(images) {
if (images.length) {
_goTo(0);
}
});
The easiest way to do is using underscore like the following:
<wallop-slider
data-images="_.pluck(user.media.mediumURL, 'mediumURL')"
data-animation="rotate"
data-current-item-index="currentSliderIndex"></wallop-slider>
</div>
But before that you need do 2 things:
add underscore
<script src="/whatever/underscore.js"></script>
inject underscorejs into controller like the folloiwng
angular.module('app', [])
.controller('Ctrl', function($scope, $window) {
$scope._ = $window._
});
Hope that help,
Ron

Resources