How can I nest angular expressions? - angularjs

I want to do something similar to (in my html):
{{ Customer.Name }}
when Customer.Name is
"{{ Customer.FirstName + ' ' + Customer.LastName }}"
How can I achieve this? The Customer.Name-String is a dynamic configuration that I read from the server. I would like to do this on several places in my application so I don't want a static code-behind-function that does this for me.
All I want is that Angular evaluates the expression recursively.

You can use the method $eval() of $scope to achieve this.
For example, your Controller would define your customer object like this:
function TestCtrl ($scope) {
$scope.customer = {
firstname : 'Darth',
lastname : 'Vader',
name : 'customer.firstname + " " + customer.lastname'
};
$scope.ev = $scope.$eval;
}
Then in your view you could use the configuration string like this:
<h1 ng-controller="TestCtrl">Hello {{$eval(customer.name)}}!</h1>
It is important that your customer.name property does not contain the curly brackets!
See this plunkr for a working example: http://plnkr.co/edit/r524cP6OAOBBzDReLNw4?p=preview

There are 2 way to do it.
Create a directive that use a customer object and concatenate both First and Lastname.
angular.module('customer.directives',[]).
.directive('customerName', function() {
restrict : 'EA', // both element and attr will work in case you are using a customer directive
replace : true,
scope : {
Customer : '='
},
template : '{{Customer.FirstName}} {{Customer.Lastname}}'
});
Example Call :
<div ng-repeat="Customer in Customers">
<customer-name='Customer'></customer-name>
</div>
Or you could use a function inside the CustomerService to send your controller the full name already concatenated.
Ex :
angular.module('Customer.Services', [])
.factory('CustomerService', function(){
return {
/* other methods here */
getFullName : function(Customer) {
return Customer.FirstName + ' ' + Customer.LastName
}
}
});
angular.module('Customer.Controllers', [])
.controller('CustomerController', function($scope.CustomerService) {
$scope.service = CustomerService.query();
$scope.getFullName = fucntion(customer) {
return CustomerService.getFullName(customer);
}
}
)
And call it with :
<div ng-repeat="Customer in Customers">
{{getFullName(Customer)}}
</div>

Related

AngularJS using a custom filter in the html tag of a custom directive?

I have a directive with a template that looks like:
<span> {{foo.bar}} {{foo.car}} {{foo.dar}}</span>
which is called in multiple locations. The way it is called is:
<foo-dir foo="fooData"></foo-dir>
Is there any way to apply a custom filter in the html directive tag and not in the directive itself?
I'm hoping something exists such as:
ng-innertext-filter="filterName:params"
but it may be wishful thinking.
If that is not possible, is there a way to use the filter in the directive controller without having to apply it everywhere the directive is used?
Thanks.
Edit: filter code:
import angular from 'angular';
function someFilter() {
return (input, filterParam) => {
if (input && filterParam && input.length)
if(filterParam)
return input.filterLogic();
return input;
}
}
The filter works fine when in a templated string {{somedata | someFilter}}. But I am looking for a way to apply the filter to the directive tag itself.
To do exactly what you want:
use the filter on the resulting string from a directive in the html tag
you need to create a template string and recompile your DOM.
This is a quite advanced use of AngularJS. I hope you enjoy it :-)
var app = angular.module("app", []);
app.controller('ctrl', function ($scope) {
$scope.foo = {
bar: "1",
car: "2",
dar: "3"
};
});
app.directive('foodir', function ($compile) {
return {
restrict: 'E',
scope: {
foo: '=', // Bind the variable to the directive's scope
filter: '#' // Just get the string for compilation
},
link: function ($scope, $element, $attrs) {
// This is the string on which you may want to apply a filter
var string_template = '" bar:" + foo.bar + " car:" + foo.car + " dar:" + foo.dar';
// Full string to compile without filter
var html = '<span>{{' + string_template + '}}</span>';
if ($scope.filter !== undefined)
// Full string to compile with filter (optional)
html = '<span>{{' + string_template + ' | '+$scope.filter+'}}</span>';
console.log(html);
var e = $compile(html)($scope); // Convert your string to a DOM element
$element.replaceWith(e); // Make it the content of the directive
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="app" ng-controller="ctrl">
<foodir foo="foo"></foodir>
<hr/>
<foodir foo="foo" filter="uppercase"></foodir>
<hr/>
<foodir foo="foo" filter="limitTo:10"></foodir>
</body>
Now, if you just want to apply a filter to some parts of your template (and not the full string), you can avoid the string_template and use basic interpolation like this:
var html = '<span> {{foo.bar}} {{foo.car}} {{foo.dar |' + $scope.filter + '}}</span>'
I'm not sure what the first part of your question means. If you're wanting to apply this filter to the compiled result of a directive, I'm not sure you can. I can help with using a filter in a controller, though.
If you define a custom AngularJS filter like so:
app.filter('makeItPretty', function() {
return function(value) {
return value.toUpperCase();
};
});
Then you can access your filter from within a controller by injecting the built-in $filter service:
app.controller('MyController', function($filter) {
var makeItPretty = $filter('makeItPretty');
var myPrettyText = makeItPretty('tHiS iS UgLy!');
});

AngularJS - Currency exchange filter with external output

I am trying to build a filter based on this post. I want to be able to switch from one currency to the other by switching a select input with an ng-click on it, but I'm failing to get the input into the filter.
My html is:
<div ng-controller="myCtrl">
you have, {{money | currency}}...<br><br>
</div>
<select>
<option ng-click="currencySymbol = 'USD'; currencyRate=exchange.usd.rate">USD<option>
<option ng-click="currencySymbol = '£'; currencyRate=exchange.pound.rate">£<option>
<option ng-click="currencySymbol = '€'; currencyRate=exchange.euro.rate">€<option>
</select>
And the Angular part:
angular
.module('myApp', [])
.controller('myCtrl', function($scope) {
$scope.money = 100;
$scope.exchange = [
{"usd":{"rate":1}},
{"pound":{"rate":0.702846}},
{"euro":{"rate":0.885055}}
];
})
.filter('currency', function() {
var defaultCurrency = 'USD';
return function(input, currencySymbol, currencyRate) {
var out = "";
currencySymbol = currencySymbol || defaultCurrency;
currencyRate = currencyRate || 1.00;
out = input * currencyRate;
return out + ' ' + currencySymbol;
}
});
You can check the JsFiddle here
Thanks in advance!
Quite a few updates here:
First off, your dropdown is located outside of the controller
You could use the ng-options directive to clean up your select menu
You have ng-click on each option which doesn't work well with the complex model you are trying to maintain
If you pass the whole selected option into the filter, you can manage everything from that one scope variable keeping the data in one place
Here is a working example
http://jsfiddle.net/r0m4n/dLLtzqyr/1/
This will be your simplified html:
<div ng-controller="myCtrl">
you have, {{money | currency: selectedCurrency}}...
<br>
<br>
<select ng-model="selectedCurrency" ng-options="item as item.label for item in exchange">
</select>
</div>
And your js:
angular
.module('myApp', [])
// controller here
.controller('myCtrl', function($scope) {
$scope.money = 100;
$scope.exchange = [{
label: "USD",
rate: 1
}, {
label: "£",
rate: 0.702846
}, {
label: "€",
rate: 0.885055
}];
$scope.selectedCurrency = $scope.exchange[0];
})
.filter('currency', function() {
return function(input, selectedCurrency) {
var out = "";
out = input * selectedCurrency.rate;
return out + ' ' + selectedCurrency.label;
}
});
You define 3 parameter in your filter currency. Whenever you use your filter, you need to give these parameters to your filter. In your case :
<div ng-controller="myCtrl">
you have, {{money | currency:currencySymbol:exchange[2].euro.rate}}...<br><br>
</div>
In AngularJS world, you call your filter after the | and you use : as a separator for parameters (the first parameter input is always given implicitly).
To make the debug easier, you can add this in your filter :
return function(input, currencySymbol, currencyRate) {
console.info('currency parameter', input, currencySymbol, currencyRate);
Please note that I hardcoded the parameter currencyRate because it's not handy to retrieve with the way you store your array.

Ng-repeat with dynamic ng-model on input not working

In controller I've a list like this:
scope.data = [ { user: { address: { city: 'Boston'} } } ];
And a property, where I've the name to access the object:
scope.propertyName = 'user.address.city';
In HTML, I've a ng-repeat where I put a dynamic input to edit that value.
<div ng-repeat="item in data">
<input ng-model="item[propertyName]">
</div>
My question is: How can I bind the value of the item with the ng-model?
You can do it like this because of ng-repeat:
<input ng-model="item.user.address.city" />
So you do not need to declare this:
$scope.propertyName = 'address.city';
Demo
You can create a directive with compile, to set ng-model indirectly:
compile: function(el, attrs) {
return function(scope, el) {
el.attr('ng-model', attrs.ngModelItem + '.' + scope[attrs.ngModelRef]);
$compile(el)(scope);
};
}
please look my sample on jsbin:
http://jsbin.com/xizucu/edit?html,js,output

AngularJS directive with select and ng-options in template

I'm going nuts!
Is it possible to create a directive that renders different times (00:15,00:30,00:45) etc into a select box where my ngModel uses objects for hours and minutes like below?
{
"h" : 1,
"m" : 30
}
My idea is to use ngModel.$formatters, ngModel.$parsers and ngModel.$render to make it possible have bi-directional bindings between the textual representation of the select box and the ngModel.
See JSFiddle http://jsfiddle.net/52kUy/23/
I have been playing with a solution to attach an ng-change to the selectbox but I also need the ng-change to be available in the application to react to time changes.
Note: To simplify the code I have removed padding of the strings (00:15 => 0:15 etc).
Super thankful for any directions.
Is this what you meant?
HTML:
<div ng-app="HelloApp" ng-controller="HelloController">
<select ng-model="blah" ng-options="value as value | myfilter for value in values"></select>
{{blah}}
</div>
JS:
app.filter('myfilter', function() {
return function(obj) {
return obj.h + ':' + obj.m;
}
});
app.controller('HelloController', function($scope) {
$scope.values = [{
"h" : 1,
"m" : 30
}, {
"h" : 1,
"m" : 25
}];
jsfiddle
EDIT:
Answering your question, if you want to initialize a the select model and your model is an object the comparison will be done by reference; have a look at this: angular docs, specially this line: "Note: ngModel compares by reference, not value. This is important when binding to an array of objects. See an example in this jsfiddle." Unless you define a track by expression, in your case we could do something like this:
HTML:
<div ng-app="HelloApp" ng-controller="HelloController">
<select ng-model="blah" ng-options="value as value | myfilter for value in values track by value.h + ":" + value.m"></select>
{{blah}}
</div>
JS:
app.filter('myfilter', function() {
return function(obj) {
return obj.h + ':' + obj.m;
}
});
app.controller('HelloController', function($scope) {
$scope.values = [{
"h" : 1,
"m" : 30
}, {
"h" : 1,
"m" : 25
}];
$scope.blah = {h: 1, m: 25};
});
plunker
Ng-options work by reference so by ffurther extending Wawy's solution with track by in ng-options it works by value.
HTML:
<div ng-app="TestApp" ng-controller="HelloController">
<select ng-model="blah" ng-options="value as value | myfilter for value in values track by id(value)"> </select>
{{blah}}
</div>
JS:
var app = angular.module('TestApp',[]);
app.filter('myfilter', function() {
return function(obj) {
return obj.h + ':' + obj.m;
}
});
app.controller('TestController', function($scope) {
$scope.id = function(obj) {
return obj.h + ":" + obj.m;
};
$scope.values = [{
"h" : 1,
"m" : 30
},
{
"h" : 1,
"m" : 25
}];
$scope.blah = {"h":1,"m":25};
});

Accessing controller function in Directive scope

I am trying to access the function defined in the controller scope from directive nested in another directive. I am using the "&" and passing the function name as the attribute to the directive, however I am still not able to reach the function in the controller scope.
Could some one correct where I am going wrong? I had spent few hours trying to find that I have to use the "&" for reaching controller scope but stuck here after whatever I did.
JSFiddle here - http://jsfiddle.net/fwR9Q/12/
Code:
<div ng-controller="PlayerCtrl">
<div class="col-md-4 col-sm-6 col-xs-12" ng-repeat="video in videos" >
<tf-video src="{{video.src}}" width="{{video.width}}" handleClick="playVideo(videoId, modeNum)" height="{{video.height}}" title="{{video.title}}"/>
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('PlayerCtrl',
function PlayerCtrl($scope,$log, trailers)
{
$scope.videos = trailers;
$scope.playVideo = function(videoId, modeNum){
alert("video Id = " + videoId + "; mode Num = " + modeNum);
return false;
};
}
);
myApp.directive('tfVideo', function() {
return{
restrict: 'AE',
scope: {
src: '#',
handleClick: '&',
width: '#width',
height: '#height'
},
template: '<img src="http://img.youtube.com/vi/{{src}}/0.jpg" height="{{height}}" width="{{width}}"/>'
};
});
myApp.factory('trailers', function(){
var trailerVideos = [
{
src:"6kw1UVovByw",
width:"324",
height:"300"
},
{
src:"uWgDVz4NEO4",
width:"324",
height:"300"
}
];
return trailerVideos;
});
</script>
Thank you
1) On your directive declaration:
<tf-video src="{{video.src}}" width="{{video.width}}" handleClick="playVideo(videoId, modeNum)" height="{{video.height}}" title="{{video.title}}"/>
Since attributes use the snake-cased form.
Instead of: handleClick=
You want the snake cased: handle-click=
2) In your template:
template: '<img src="http://img.youtube.com/vi/{{src}}/0.jpg" height="{{height}}" width="{{width}}"/>'
Instead of: {videoId: {{src}}, modeId: modeNum{{$parent.$index}} }
You want: {videoId: src, modeNum: $parent.$index }
Since you need the "modeNum" parameter name to match between the template and the directive and you want to map directly to the variables, not expressions.
Updated fiddle

Resources