cannot manipulate string with angular filter - angularjs

I am an angular newbie and I study the book "Angular JS by example" and I try to create my own filter. (pp. 93-94).
In my controller this is the string I want to manipulate
procedure: "Assume a position, with feet together .\Slightly bend your knees, .\While in air, bring your legs out .\ As you are moving your legs outward"
and then I sanitise it
$scope.trustedHtml = $sce.trustAsHtml($scope.currentExercise.details.procedure);
Since this is an SPA , the filter is in the description-panel.ejs file, that is inside workout.ejs, that is inside index.ejs
description-panel.ejs has
<div class="panel-body" ng-bind-html ="trustedHtml | myLineBreakFilter"> </div>
workout.ejs has
<div id="video-panel" class="col-sm-3" ng-include="'description-panel.ejs'">
and index.ejs has
<script src="/javascripts/7MinWorkout/filters.js"></script>
filter.js has the filter
angular.module('7minWorkout').filter('myLineBreakFilter', function () {
return function (input) {
var str = input;
var br = "</br></br>";
var position = str.indexOf(".");
var output = [str.slice(0, position), br, str.slice(position)].join('');
return output ;
}
});
The filter should replace all the . with </br></br>.
This does not work and I get no text at all in my front-end. I get this error in the console
TypeError: str.slice is not a function at filters.js:22
Shouldn't basic js stuff like str.slice be supported out of the box? What am I missing?
Thanks

$sce.trustAsHtml() return you an object so slice will not work on it.You can pass that object to $sce.getTrustedHtml(object) to obtain the original value and then can apply slice on it.
angular.module('7minWorkout').filter('myLineBreakFilter', function ($sce) {
return function (input) {
var str = $sce.getTrustedHtml(input);
var br = "</br></br>";
var position = str.indexOf(".");
var output = [str.slice(0, position), br, str.slice(position)].join('');
return $sce.trustAsHtml(output) ;
}
});

Try this add this before the splice
str.toString();
str.splice(//pass parameters);

Related

How can I get ng-click to function with ng-repeat and ng-bind-html?

I am trying to get a ng-click directive to function within an ng-repeat and ng-bind-html. The ng-click code is added to a string of html from data pulled from the server (hence the ng-bind-html). The setup has a controller, a base template that is put onto the page with Drupal, and a partial that is loaded via the template from Drupal.
The controller looks like this at the moment:
var guideListController = angular.module('app')
.controller('guideListController', [
'$scope',
'$sce',
'$compile',
'ViewService',
'ToolKit',
function($scope, $sce, $compile, ViewService, ToolKit) {
// Storage for which rows need more/less links
this.rowIndex = [];
this.showFull = false;
this.showFullClick = function() {
alert('Showfull');
};
this.trustIntro = function(code) {
return $sce.trustAsHtml(code);
};
// Fetch the guide list view from services
var data = ViewService.get({view_endpoint:'guide-service', view_path: 'guide-list'}, function(data) {
//console.log(data);
// Update/process results
for (var row in data.results) {
// Create short intro w/ truncate.js
data.results[row].Intro_short = $sce.trustAsHtml($scope.guideList.getShortIntro(data.results[row], row));
//data.results[row].Intro_short = $scope.guideList.getShortIntro(data.results[row], row);
// Update intro
data.results[row].Introduction = $sce.trustAsHtml($scope.guideList.updateIntro(data.results[row], row));
//data.results[row].Introduction = $scope.guideList.updateIntro(data.results[row], row);
}
$scope.guideList.guides = data.results;
});
// Add a read less anchor tag at the end of the main intro
this.updateIntro = function(row, row_index) {
var intro = row['Introduction'].trim();
if ($scope.guideList.rowIndex[row_index]) { // only apply Less link if needed
var index = intro.length - 1;
var tag = [];
if (intro.charAt(index) === '>') { // we have a tag at the end
index--;
do {
tag.push(intro.charAt(index));
index--;
} while (intro.charAt(index) != '/'); // the closing tag
index--; // we move the index one more for the "<"
tag.reverse(); // Reverse
tag = tag.join('');
}
var inserts = ['div', 'p']; // we insert the Less link here.
if (jQuery.inArray(tag, inserts) >= 0) { // insert into the tag
intro = intro.substr(0, index) + ' <a class="less" ng-click="$parent.guideList.showFull = false">Less</a>' + intro.substr(index);
}
else { // insert at the end of the html
intro = intro + '<a class="less" ng-click="this.showFull = false">Less</a>';
}
}
return intro;
};
// Truncate the long intro into a shorter length blurb
this.getShortIntro = function(row, row_index) {
// Truncate if necc.
var short_intro = jQuery.truncate(row['Introduction'], {
length: 250,
words: true,
ellipsis: '\u2026 <a class="more moreish" attr-ng-click="guideList.showFullClick()">Read on</a>'
});
var more = jQuery('.more', short_intro); // select more link
if (more.length) { // do we have a more link
$scope.guideList.rowIndex[row_index] = true;
}
else { // no more link
$scope.guideList.rowIndex[row_index] = false;
}
$compile(short_intro)($scope);
return short_intro;
};
}]);
As you can see in the ViewService.get() call, data is fetched and then processed. The processing simply involves putting a link at the end of the "Intro" field that is intended to be clickable.
For a while I was having a tough time to even get the ng-click directive to even show (it was being filtered out w/out $sce.trustAsHtml). Now it is there but clicking it has no effect.
The main template (from Drupal) currently looks like:
<div class="guide-listing" ng-controller="guideListController as guideList">
<a ng-click="guideList.showFullClick()">Click me</a>
<div class="guide-teaser"
ng-repeat="guide in guideList.guides"
ng-include src="'/sites/all/themes/ngTheme/ngApp/partials/guide_teaser.html'">
<!-- See guide_teaser.html partial for guide teasers -->
</div>
</div>
The ng-click as placed in the Drupal template above works as expected.
And for the partial that is used in the ng-repeat, it looks like so:
<div ng-controller="guideListController as guideList">
<h2 class="guide-teaser-title">{{guide.node_title}}</h2>
<div class="guide-teaser-intro" ng-bind-html="guide.Introduction" ng-show="guide.showFull">
{{guide.Introduction}}
</div>
<div class="guide-teaser-intro-short" ng-bind-html="guide.Intro_short" ng-show="!guide.showFull">
{{guide.Intro_short}}
</div>
</div>
So far I have only been working on getting the ng-click to work on the short_intro and have had no success so far. Any ideas as to what I am doing wrong would be greatly appreciated!
Ok, So I did get some traction! I used the ngHtmlCompile (http://ngmodules.org/modules/ng-html-compile) directive that was created by https://github.com/francisbouvier (thanks!)
The issue was that the new (dynamic) html wasn't being compiled.
At first it didn't work. I had two issues that prevented it from firing:
A: I stopped using $sce.trustAsHtml. Using this in conjunction with the directive caused the content to disappear!
B: The other issue was one of scope. After I changed the directive such that transclude was set to false it worked just fine!

Notable to link javascript object and angularjs in controllers

I am trying to develop a mobile application in which i am getting JSON object using javascript page main.js,now I am trying to print the object using angualjs Controllers,but could not find any way.CAn anyone help me out on this?
function written in Main.js`
function getViewColumnsSuccess(result){
var httpStatusCode = result.status;
if (200 == httpStatusCode) {
var invocationResult = result.invocationResult;
var isSuccessful = invocationResult.isSuccessful;
if (true == isSuccessful) {
var result = invocationResult.text; //var FinalCol=reult;
} else {
alert("Error. httpStatusCode=" + httpStatusCode); } } }
The var result I want to get in DemoController in another page
app.controller('tableCtrlNew', function($scope,$http) { });
First controllers are not for print data, for that you should dataBinding, in views.
For Example:
In your controller you have this var;
$scope.name="John Doe";
So, then to print that in some view, you shoud print that in some view that has that scope, so to print in one view(html) that is simple like:
<span class="labelName"> {{name}}</span>
It is data-binding, with this you automatically print data from a controller into a view, but remember, it must be in the same scope to works.
Regards.
Assign Result to Window object
window.result = invocationResult.text;
In controller you can use $window
app.controller('tableCtrlNew', function($scope,$http,$window) {
console.log($window.result);
});

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.

Angular How to get the video ID from a full YouTube URL

In my data object I have a full YouTube URL (http://www.youtube.com/watch?v=kJ9g_-p3dLA).
In my partial I need to extract the video ID from this string (kJ9g_-p3dLA). I'm trying not to resort to running through all my data when the app starts and extracting the video ID through that way.
I'm looking for a filter or a directive that I can feed the full YouTube ID to which will return the video ID. Anyone?
Ended up writing my own filter for this:
app.filter("GetYouTubeID", function ($sce) {
return function (text) {
var video_id = text.split('v=')[1].split('&')[0];
return video_id;
}
})
Can be used in a partial like so:
{{content.complete_youtube_url | GetYouTubeID}}
<button onclick="myFunction()">Try it</button>
<p id = "demo" />
<script>
function myFunction() {
var url= "http://www.youtube.com/watch?v=kJ9g_-p3dLA"; //sample url
var res = str.split("http://www.youtube.com/watch?").splice("1");
document.getElementById("demo").innerHTML = url;
}
</script>
You could use regular expressions, which have no dependencies on Angular.
Note that I'm not a regex expert so my expression may not be perfect.
var regex = new RegExp(/(?:\?v=)([^&]+)(?:\&)*/);
var url = "http://www.youtube.com/watch?v=kJ9g_-p3dLA";
var matches = regex.exec(url);
var videoId = matches[1]; // kJ9g_-p3dLA
That regex will capture a group between ?v= and & (the & is optional).
Test with explode function of PHP
$youtubre_url = "http://www.youtube.com/watch?v=kJ9g_-p3dLA";
$array_id = explode("=",$youtubre_url);
$id = $array_id[1];

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