AngularJS directive with select and ng-options in template - angularjs

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};
});

Related

Share $scope between DOM elements with same ng-controller

I'm stuck with something (I've been reading similar questions but none of them get me to the solution).
I have 2 DOM elements (let's say 2 div's) with different id's but the same ng-controller (this is for basic example, in my real app I have 2 differente pages but works the same).
<div id="layer1" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value" ng-change="changeType(selectedType.value)"></select>
</div>
<div id="layer2" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value" ng-change="changeType(selectedType.value)"></select>
</div>
And in JS
var myAppModule = angular.module('myApp', [])
.factory('selectedType', function(){
return{}
})
.controller('appCtrl', ['$scope',function($scope){
$scope.ptype = [
{
value: 1,
label:'Kg'
},
{
value: 2,
label:'Pza'
}];
selectedType = $scope.ptype[0];
$scope.changeType = function(value){
if(value==1){selectedType = $scope.ptype[0];}
else{selectedType=$scope.ptype[1];}
};
}])
As you can see I have the options for the SELECT and the ng-model, what I need is when change the selected value in any SELECT (doesn't matter which DIV) the other gets updated too.
Here a Plunker with code SEE HERE.
Thanks!
Using a Shared service like your attempt:
you are in the good way but you just forgot that :
1 - you use the scope in ng-model,
2 - to have 2 way data binding for factory,
bind the factory itself to a scope,
and use an item inside the factory not the factory itself (eg: data).
code:
.factory('selectedType', function(){
return {
data: {} // <---- We use 'data' here for example
}
})
and in the controller now:
.controller('appCtrl', ['$scope', 'selectedType',function($scope, selectedType) {
$scope.selectedType = selectedType; // <-- Not the selectedType.data (Important)
/* but the factory object */
$scope.ptype = [
{
value: 1,
label:'Kg'
},
{
value: 2,
label:'Pza'
}];
$scope.selectedType.data = $scope.ptype[0];
})
Now we dont need the ng-change at all:
<div id="layer1" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value"></select>
</div>
<div id="layer2" ng-controller="appCtrl">
<select ng-model="selectedType" ng-options="type.label for type in ptype track by type.value"></select>
</div>
plunker: https://plnkr.co/edit/5qg45F?p=preview
NB: Instead of using shared service, you can also use $rootScope or $scope.$parent for that.

how to split a input model string value and store the values as seperate properties in model object

I have a form with multiple currency value inputfields and I have a directive which restricts the currency value to be entered as something like space for example USD 100.But I need to split the model value and seperate the currency and number and store it in the same model object.
So , USD 100 can be splitted into, USD , 100 as integer
$scope.formData = {
Fee : "USD 100"
}
this should become as
$scope.formData = {
fee : 100,
feeCurrency : USD
}
there are multiple fields in the form along with other type of data also.Please help me with any thoughts where I can implement this with service or directive so that I can easily apply to all the similar fields.
Here is one way you could do it with a directive. I've made a lot of assumptions here and this is not production worthy code, but it should give you a base to start from. Basically you pass the object to the directive and then watch a property on that object for changes. When the property changes you can split on a space character and fill in the appropriate values on the object. Since it's a directive you can use it anywhere.
js
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.feeData = {
fee: 0,
feeCurrency: 'N/A',
value: ''
};
})
.directive('feeDirective', function() {
return {
restrict: 'A',
scope: {
feeData: '='
},
link: function(scope, element, attrs) {
scope.$watch('feeData.value', function(newValue, oldValue) {
console.log(newValue);
var data = newValue.split(' ');
if (data.length === 2) {
console.log(data);
scope.feeData.fee = data[0];
scope.feeData.feeCurrency = data[1];
}
}, true);
}
}
});
html
<div ng-app="app" ng-controller="ctrl">
<div>
<input type="text" ng-model="feeData.value" fee-directive fee-data="feeData">
</div>
<div>
Value: {{feeData.value}}
<br/> Fee: {{feeData.fee}}
<br/> Currency: {{feeData.feeCurrency}}
</div>
</div>
Working JSFiddle that illustrates the above code.

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.

AngularJS (Filter) : How to implement multi-keyword search on object attributes?

Check this jsFiddle : http://jsfiddle.net/mystikacid/hajo0c33/
I'm using the keyword in the input box to filter the objects to be shown. This works fine as far as I enter only one keyword. But if I try to filter by two keywords (for two attributes, example, my query is - 'Transformers 150V' as I want to look for those Transformers which use 150V, it does not show any result.
HTML
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type='text' ng-model='query' placeholder = "Search" />
<div ng-repeat="product in products | filter : query">
{{product.name}} |
{{product.family}} |
{{product.amperage}} |
{{product.volt}}
</div>
</div>
Javascript
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', function($scope){
$scope.products = [
{
name : '9T21S1050',
family : 'Transformer',
amperage : '20A',
volt : '150V'
},
{
name : '9T85B3092',
family : 'Transformer',
amperage : '15A',
volt : '200V'
},
{
name : 'AEU3182RCXAXB4',
family : 'Panel Board',
amperage : '30A',
volt : '250V'
},
{
name : 'AQU1182RCXAXB4',
family : 'Panel Board',
amperage : '25A',
volt : '300V'
},
{
name : 'AQU1422RCXAXT1',
family : 'Panel Board',
amperage : '35A',
volt : '150V'
}
]
});
I understand that I need to write a function for the filter, but I've not been able to make it work so far. Let me know if you'd like to see the function too.
Thanks.
i am creating a new filter that will give product if both value will be present in it.
var m = angular.module('yourModuleName');
m.filter('advancefilter', ['$filter', function($filter){
return function(data, text){
var textArr = text.split(' ');
angular.forEach(textArr, function(test){
if(test){
data = $filter('filter')(data, test);
}
});
return data;
}
}]);

How can I nest angular expressions?

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>

Resources