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.
Related
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 }}
I'm writing an Angular app that will read a magnetic stripe card from a USB device. When I swipe a test card, I get a string back containing the card number. For example, ;12345?, where 12345 is the card number.
The data my app uses doesn't include these "control characters", so I'd like to strip them out of the search string if the string starts with a ; and ends with a ?.
When I write a custom filter:
angular.module('app.filters', [])
.filter('stripcardcontrolcharacters', function() {
return function(text) {
if(text.substring(0, 1) === ";" && text.substring(text.length - 1) === "?") {
return text.substring(1, text.length - 1);
}
};
});
It fails because I'm ng-repeating over an array, and not the string that I've searched for.
How would I get what string I'm filtering for and strip the characters from it?
EDIT: Current suggestion is to use a filter to modify the array to ADD in the control characters so filter: can find it. I might go with that for now, but I'm still curious to know if you can write such a filter
You're passing the entire array to your filter via
ng-repeat="user in users | stripcardcontrolcharacters ...
If that's how you want it to work, you would need to treat it as an array, for example
return function(textArray) {
var invalidChars = /\D/g; // just an example
return textArray.map(text => {
console.log(text);
return text.replace(invalidChars, '');
});
}
You are probably applying the filter to the array, not the string itself.
Look at this example:
angular.module('test', [])
.controller('testController', function($scope){
$scope.names = ['John Doe', 'Jane Doe'];
})
// The test filter
.filter('strip', function(){
return function(str) {
return str.substring(1, str.length - 1);
};
});
And here is how to use it:
<body ng-app="test" ng-controller="testController">
<p ng-repeat="name in names">
{{name | strip }}
</p>
</body>
Note that I'm applying the filter where I use the value, not in the ng-repeat statement.
And here is the working plunker
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);
I have a simple string array coming from a server:
[{"things":["me", "my"]}]
In my page, to display the array I have:
{{things}}
And it prints:
["me", "my"]
How do I control the printing, for instance, if I want to eliminate the brackets and quotes?
You can implement a scope function to display arrays as comma separated strings like shown in this fiddle.
$scope.array = ["tes","1","2","bla"];
$scope.arrayToString = function(string){
return string.join(", ");
};
Now you can call this function within the template:
{{arrayToString(array)}}
Update
You can also use the join() method of arrays directly within the template without using an extra function inbetween as displayed within the updated fiddle.
{{array.join(", ")}}
I think you'll want ngRepeat for something like:
<div class="list" ng-repeat="thing in things">
{{ thing }}
</div>
You can also create custom angular filter optionally with some advanced formatting:
module.filter('formatArray', ['OptionalInjection', function(OptionalInjection) {
return function(value) {
if (!angular.isArray(value)) return '';
return value.map(OptionalInjection.formatter).join(', '); // or just return value.join(', ');
};
}])
Then in html just write {{yourArrayValue | formatArray}}.
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)}}