Dynamic created map triggering warning in angular - angularjs

In angular I want to dynamically create a map based on the address entered. I have successfully done this in VueJS. But in angular this triggers security warnings.
HTML:
<iframe
ng-src="https://maps.google.com/maps?&q={{encodeURIComponent(item.address)}}&output=embed"
allowfullscreen>
</iframe>
I have tried creating the following:
app.filter('trustAsResourceUrl', ['$sce', function ($sce) {
return function (val) {
return $sce.trustAsResourceUrl(val);
};
then piping it as so:
| trustAsResourceUrl}}
Which works if using an already established URL but not since I'm trying to form URL from address. I get the following:
Error: [$interpolate:noconcat] Error while interpolating: Strict
Contextual Escaping disallows interpolations that concatenate multiple
expressions when a trusted value is required. See
http://docs.angularjs.org/api/ng.$sce
VueJS was so simple but I can't use it in this project. I'll include it in case it gives any ideas:
methods: {
getmap: function(){
setTimeout(function () {
const searchInput = document.getElementById('searchTextField');
let addr = searchInput.value;
let embed = "<div class='form-group'><label for='exampleInputPassword1'>Map Preview</label><iframe frameborder = '0' scrolling= 'no' marginheight= '0' marginwidth= '0' src= 'https://maps.google.com/maps?&q=" + encodeURIComponent(addr) + "&output=embed' > </iframe></div>";
$('.place').html(embed); }, 200)
},

Essentially you can't use this filter. You have to create the string you want to use directly on the scope:
this.src = $sce.trustAsResourceUrl("https://maps.google.com/maps?q="
+ encodeURIComponent(item.address) + "&output=embed");
Then <iframe ng-src="{{$ctrl.src}}">
You will have to update the entire src rather than just item.address.

Related

Pass variable to component view to be used as filter

I've build a simple component that allows passing a filter as a parameter, and I'm trying to use that parameter in the component view, but I don't know how. It gets passed as a string so it's treated as a string in the component view and thus not working.
Basically it looks something like this:
<number-compare value="some.value" filter="currency"/>
And in the component view:
<span> {{ numCompCtrl.value | numCompCtrl.value.filter }} </span>
But that doesn't work because it gets interpreted as {{ 10 | "currency" }}
I've tried to handle it in the controller instead, and apply the filter there but it gets really messy when the filter needs multiple parameters so the easiest thing by far would be if I could get the simple way working.
Is it possible?
Actually, I just discovered that I had already solved this previously with another filter as a workaround 🙈
(function() {
'use strict';
angular
.module('core')
.filter('dynamic', dynamic);
dynamic.$inject = ['$interpolate'];
function dynamic($interpolate) {
return function(value, name) {
if (!name) {
return value;
}
var result = $interpolate('{{ value | ' + name + ' }}');
return result({ value: value });
};
}
})();
And used like this:
{{ numCompCtrl.value | dynamic: numCompCtrl.value.filter }}

Migrating jQuery selector to angularjs for third party vendor client help functionality

I'm trying to migrate old jQuery code to angularjs.
The issue that I'm having is that I'm not sure on the best approach.
Bascially, depending on the selector a different type of 'event' needs to be pushed into a array called gt.
The purpose of the jQuery code is to provide detailed info of clients having issues while filling in a form. the gt array is picked up by third party software that helps the clients by asking if they want to chat.
Example of how the array is populated:
$('a').live('click', { element: this }, function (element) {
_clickedElement = this;
var linkUrl = element.currentTarget.hostname + element.currentTarget.pathname;
var querystring = window.location.search
var shortLocationUrl = window.location.href.replace(querystring, "").replace("http://", "").replace("https://", "");
if (element.currentTarget.hostname.length > 0 && element.currentTarget.target != "_blank" && linkUrl != shortLocationUrl) { //click on a link that opens in the current window and points to a page external to this part
_gt.push(['event', { eventName: 'Leave_Page_' + chat.name, name: chat.name, pageName: chat.pageName, locale: _locale, isClient: chat.isClient }]);
_pushLeavePageEvent = false;
}
else if (this.id == backButtonId) { //click "previous"
_gt.push(['event', { eventName: 'Go_Back_' + chat.name, name: chat.name, pageName: chat.pageName, locale: _locale, isClient: chat.isClient }]);
_pushLeavePageEvent = false;
}
return true;
});
So for all the a tags inside my page (or form) the above code needs to be executed.
What would be a good approach to have similar behaviour in Angularjs?
I was thinking of a directive but I'm not sure whether to make this a directive at the level of my form or make a directive that I then use throughout my page?
P.S.: similar behaviour is needed (pushing an event into the gt array) for all the input, textarea and select fields on the page as well as the errors on the page caused by the clients and when a client hovers over a tooltip.

how to avoid URL encoding with ui-sref

I've a link like this:
<a ui-sref="someState(Param:'مثال')"> A localized param </a>
when compiling the Angular-ui-router, generates a href like this :
A localized param
how can I avoid this?
what i have tried:
creating a new type using $urlMatcherFactoryProvider
$urlMatcherFactoryProvider.type('decoded', {
encode: function (item) {
return decodeURIComponent(item) // i put this to decode personally
},
decode: function (item) {
return decodeURIComponent(item);
},
is: function (item) {
return true;
}
});
this actually happens inside 'angular' not 'ngRoute' , angular forces encoding all urls using encodeURICompenent,
so you need to change encodeUriQuery inside angular.js so it would pass arabic characters without encoding
function encodeUriQuery(val, pctEncodeSpaces) {
var r = /[\u0600-\u06ff]|[\u0750-\u077f]|[\ufb50-\ufc3f]|[\ufe70-\ufefc]/;
if(r.test(val)){
return val.replace(/\s/g,'-');
}else{
return encodeURIComponent(val).
replace(/%40/gi, '#').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%3B/gi, ';').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
}
if you're using minified version or don't want to bother looking into code here is a monkey patch you can copy and paste this anywhere in your code
window.encode = window.encodeURIComponent;
window.encodeURIComponent = function(val){return /[\u0600-\u06ff]|[\u0750-\u077f]|[\ufb50-\ufc3f]|[\ufe70-\ufefc]/.test(val) ? val.replace(/\s/g,'-') : window.encode(val)};
notice that part replace(/\s/g,'-') it replaces whitespaces with a dash because in angular it causes some issue to have whitespace in the url
the solution it's very simple actually
you can use javaScript decodeURIComponent() function on $scope.item.title for me it's worked like charm .
in controller use it like
$state.go("single-page", { contentType: "portfolio", date: "139411", title: decodeURIComponent("اولین-نمونه-کار-تست") });
in inspect you see something like :
its better for google seo ، google understood farsi better that way
but when you click on the link in url place you see this

Trying to replace spaces with dashes using ng-model

I'm new to AngularJS and trying to create a simple app that will allow me to upload files to my Laravel driven website. I want the form to show me the preview of what the uploaded item will look like. So I am using ng-model to achieve this and I have stumbled upon the following:
I have an input with some basic bootstrap stylings and I am using custom brackets for AngularJS templating (because as I mentioned, I am using Laravel with its blading system). And I need to remove spaces from the input (as I type it) and replace them with dashes:
<div class="form-group"><input type="text" plaeholder="Title" name="title" class="form-control" ng-model="gnTitle" /></div>
And then I have this:
<a ng-href="/art/[[gnTitle | spaceless]]" target="_blank">[[gnTitle | lowercase]]</a>
And my app.js looks like this:
var app = angular.module('neoperdition',[]);
app.config(function($interpolateProvider){
$interpolateProvider.startSymbol('[[').endSymbol(']]');
});
app.filter('spaceless',function(){
return function(input){
input.replace(' ','-');
}
});
I get the following error:
TypeError: Cannot read property 'replace' of undefined
I understand that I need to define the value before I filter it, but I'm not sure where to define it exactly. And also, if I define it, I don't want it to change my placeholder.
There are few things missing in your filter. First of all you need to return new string. Secondary, regular expression is not correct, you should use global modifier in order to replace all space characters. Finally you also need to check if the string is defined, because initially model value can be undefined, so .replace on undefined will throw error.
All together:
app.filter('spaceless',function() {
return function(input) {
if (input) {
return input.replace(/\s+/g, '-');
}
}
});
Demo: http://plnkr.co/edit/5Rd1SLjvNI18MDpSEP0a?p=preview
Bravi just try this filter
for eaxample {{X | replaceSpaceToDash}}
app.filter('replaceSpaceToDash', function(){
var replaceSpaceToDash= function( input ){
var words = input.split( ' ' );
for ( var i = 0, len = words.length; i < len; i++ )
words[i] = words[i].charAt( 0 ) + words[i].slice( 1 );
return words.join( '-' );
};
return replaceSpaceToDash;
});
First, you have to inject your filter in you module by adding it's name to the array :
var app = angular.module('neoperdition',['spaceless']);
Secondly, the function of the filter have to return something. The String.prototype.replace() return a new String. so you have to return it :
app.filter('spaceless',function(){
return function(input){
return input.replace(' ','-');
}
});
Edit: dfsq's answer being a lot more accurate than mine.

How do I change AngularJS ng-src when API returns null value?

In working with the API from themoviedb.com, I'm having the user type into an input field, sending the API request on every keyup. In testing this, sometimes the movie poster would be "null" instead of the intended poster_path. I prefer to default to a placeholder image to indicate that a poster was not found with the API request.
So because the entire poster_path url is not offered by the API, and since I'm using an AngularJS ng-repeat, I have to structure the image tag like so (using dummy data to save on space):
<img ng-src="{{'http://example.com/'+movie.poster_path}}" alt="">
But then the console gives me an error due to a bad request since a full image path is not returned. I tried using the OR prompt:
{{'http://example.com/'+movie.poster_path || 'http://example.com/missing.jpg'}}
But that doesn't work in this case. So now with the javascript. I can't seem to get the image source by using getElementsByTagName or getElementByClass, and using getElementById seems to only grab the first repeat and nothing else, which I figured would be the case. But even then I can't seem to replace the image source. Here is the code structure I attempted:
<input type="text" id="search">
<section ng-controller="movieSearch">
<article ng-repeat="movie in movies">
<img id="myImage" src="{{'http://example.com/'+movie.poster_path}}" alt="">
</article>
</section>
<script>
function movieSearch($scope, $http){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://example.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
imgSrc = document.getElementById('myImage').src;
ext = imgSrc.split('.').pop();
missing='http://example.com/missing.jpg';
if(ext !== 'jpg'){
imgSrc = missing;
}
});
}
</script>
Any ideas with what I'm doing wrong, or if what I'm attempting can even be done at all?
The first problem I can see is that while you are setting the movies in a async callback, you are looking for the image source synchronously here:
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
});
// This code will be executed before `movies` is populated
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
However, moving the code merely into the callback will not solve the issue:
// THIS WILL NOT FIX THE PROBLEM
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
// This will not solve the issue
imgSrc = document.getElementById('myImage').src;
ext = img.split('.').pop();
// ...
});
This is because the src fields will only be populated in the next digest loop.
In your case, you should prune the results as soon as you receive them from the JSONP callback:
function movieSearch($scope, $http, $timeout){
var string,
replaced,
imgSrc,
ext,
missing;
$(document).on('keyup', function(){
string = document.getElementById('search').value.toLowerCase();
replaced = string.replace(/\s+/g, '+');
$http.jsonp('http://domain.com/query='+replaced+'&callback=JSON_CALLBACK').success(function(data) {
console.dir(data.results);
$scope.movies = data.results;
$scope.movies.forEach(function (movie) {
var ext = movie.poster_path && movie.poster_path.split('.').pop();
// Assuming that the extension cannot be
// anything other than a jpg
if (ext !== 'jpg') {
movie.poster_path = 'missing.jpg';
}
});
});
});
}
Here, you modify only the model behind you view and do not do any post-hoc DOM analysis to figure out failures.
Sidenote: You could have used the ternary operator to solve the problem in the view, but this is not recommended:
<!-- NOT RECOMMENDED -->
{{movie.poster_path && ('http://domain.com/'+movie.poster_path) || 'http://domain.com/missing.jpg'}}
First, I defined a filter like this:
In CoffeeScript:
app.filter 'cond', () ->
(default_value, condition, value) ->
if condition then value else default_value
Or in JavaScript:
app.filter('cond', function() {
return function(default_value, condition, value) {
if (condition) {
return value;
} else {
return default_value;
}
};
});
Then, you can use it like this:
{{'http://domain.com/missing.jpg' |cond:movie.poster_path:('http://domain.com/'+movie.poster_path)}}

Resources