I've created a hypothetical example; since i can't share my real example with you all. So forgive my hasty json file i created.
To the problem. Say i have a select populated like so using a json file which contains an array of (US State) objects:
State.json
{ "states":
[
{
code: "AL",
name: "Alabama"
},
{
code: "AK",
name: "Alaska"
},
{
code: "AS",
name: "American Samoa"
},
{
code: "AZ",
name: "Arizona"
},
{
code: "AR",
name: "Arkansas"
},
{
code: "CA",
name: "California"
},
{
code: "CO",
name: "Colorado"
},
{
code: "CT",
name: "Connecticut"
},
... etc...
]}
I pull in the json file and set it to a scope item like so:
main-controller.js
app.controller('MainCtrl', function ('$scope') {
$scope.states = [
{ code: "AL": name: "Alabama" },
//etc
];
$scope.selectStateChange = function (stateCode) {
console.log(stateCode);
}
});
index.html
Here's my select:
<select ng-model="selectedState" ng-change="selectStateChange(selectedState)">
<option ng-repeat="state in states">{{state.name}}</option>
</select>
My Problem
How does one get the actual state code to be passed into function selectStateChange on my ng-change?
You should try using ng-options instead of a ng-repeat on options.
This way your model will be up to date and it will be quite convenient to access the selected object.
It should looks like this in your case :
<select ng-model="selectedState" ng-options="state.name for state in states" ng-change="selectStateChange()">
</select>
and your JS should display your object:
app.controller('MainCtrl', function ('$scope') {
$scope.states = { "AL": "Alabama", //etc }
$scope.selectedState = null;
$scope.selectStateChange = function () {
console.log(selectedState);
}
});
This way, selectedState is equal to {
code: "AL",
name: "Alabama"
}
What get's logged into the console by console.log(stateCode);?
Did you try
$scope.selectStateChange = function (selectedState) {
console.log(selectedState.code);
}
Related
I am new to angular. I have a json object:
[
{
"id": 1,
"name": "name1",
"speciality": "speciality1",
"address": "address1",
"phoneNumber": 9999999999
},
{
"id": 2,
"name": "name2",
"speciality": "speciality2",
"address": "address2",
"phoneNumber": 9999999999
},
...
]
and I want to filter based on two properties: name and speciality. This search is matching insensitive substring. How to do this? The way I am doing this is as follows, which doesn't work:
$filter('filter')(doctor.details, { $: query }, false, [name, speciality]);
$filter('filter')(doctor.details, { name: query } || {speciality : query }, false);
doctor.details is the json object array, query is the (sub)string to be matched with name or speciality.
You can write a AngularJS filter to filter out the list based on query matching name and speciality properties.
app.filter('filterDetails', function() {
return function(details, query) {
var filteredDetails = [];
filteredDetails = details.filter(function(obj) {
// look whether query is substring of name or speciality
if (~obj.name.indexOf(query) || ~obj.speciality.indexOf(query)) {
return obj;
}
})
// finally return filtered array
return filteredDetails;
};
});
HTML
Query :
<input type="text" ng-model="query" />
<ul>
<li ng-repeat="detail in doctor.details | filterDetails:query">
{{ detail }}
</li>
</ul>
Plunker
Did it using lodash as this was the easiest I could think of:
_.union($filter('filter')(doctor.details, { name: query }), $filter('filter')(doctor.details, { speciality: query }));
I apologise if this has been answered already, but I'm new to Angular so might have missed it.
I have a need to provide multiple sets of checkbox lists, which need to be combined to create an AND query.
It's on Plunker here http://plnkr.co/OGmGkz22n4J4T8p74yto but enclosed below. At the moment I can select the bottom row and the correct names appear from storeArray, but I cannot work out how to add the Format array into the filter.
I've tried:
<div ng-repeat="store in storeArray | filter:(formatFilter && tillFilter)">
and
<div ng-repeat="store in storeArray | filter:formatFilter:tillFilter">
but they don't work.
Any suggestions please?
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.formatFilter = function(a) {
for (var fmt in $scope.formatsArray) {
var f = $scope.formatsArray[fmt];
if (f.on && a.format.indexOf(f.name) > -1) {
return true;
}
}
};
$scope.tillFilter = function(a) {
for (var till in $scope.tillsArray) {
var t = $scope.tillsArray[till];
if (t.on && a.tills.indexOf(t.name) > -1) {
return true;
}
}
};
$scope.formatsArray = [{
name: "Super",
on: false
}, {
name: "Express",
on: false
}, {
name: "Other",
on: false
}];
$scope.tillsArray = [{
name: "Main",
on: false
}, {
name: "Service",
on: false
}, {
name: "Petrol",
on: false
}];
$scope.storeArray = [{
"id": "1",
"name": "101",
"format": "Super",
"tills": ["Main", "Service", "Petrol"]
}, {
"id": "2",
"name": "102",
"format": "Express",
"tills": ["Main", "Service"]
}, {
"id": "3",
"name": "103",
"format": "Other",
"tills": ["Main", "Petrol"]
}, {
"id": "4",
"name": "104",
"format": "Super",
"tills": ["Service", "Petrol"]
}];
}
While you can chain filters together like this:
<div ng-repeat="store in storeArray | filter:formatFilter | filter:tillFilter)">
This won't fix your problem since the first filter will do it's job, and filter items out that you may want to include in your second filter. I'm not sure of any way to do an "or" filter. Is there any reason you can't do a custom filter that includes both? I modified your plunker with a custom filter:
http://plnkr.co/edit/han1LFl7toTsSX27b9Q0?p=preview
The code isn't super clean... it does the job. :) You may want to polish it up a bit.
I am using an API to load (Data) to my $scope resource, and I took an example from a directive online to create a treeview. Recursive Tree View Example
However I am changing a few things to load data from an API. Please note the commented data... when I uncomment my data everything works great, however when I use $scope.treeFamily = TreeView.query() I think there is a delay between the directive executing and me getting no data. Any insight will be helpful. Thank you!
var module = angular.module("module", ["ngResource", "ngRoute"]);
module.factory('TreeView', function ($resource) {
return $resource('/api/TreeView/:Id', {}, {
//show: { method: 'GET', isArray: true }, //<--- need to do query instead of show....
query: { method: 'GET', isArray: false},
update: { method: 'PUT', params: { id: '#id' } },
delete: { method: 'DELETE', params: { id: '#id' } }
})
});
module.controller('TreeCtrl', function ($scope, TreeView) {
$scope.treeFamily = TreeView.query();
//$scope.treeFamily = {
// name: "Parent",
// children: [{
// name: "Child1",
// children: [{
// name: "Grandchild1",
// children: []
// }, {
// name: "Grandchild2",
// children: []
// }, {
// name: "Grandchild3",
// children: []
// }]
// }, {
// name: "Child2",
// children: []
// }]
//};
});
module.factory('RecursionHelper', ['$compile', function ($compile) {
var RecursionHelper = {
compile: function (element) {
var contents = element.contents().remove();
var compiledContents;
return function (scope, element) {
if (!compiledContents) {
compiledContents = $compile(contents);
}
compiledContents(scope, function (clone) {
element.append(clone);
});
};
}
};
return RecursionHelper;
}]);
module.directive("tree", function (RecursionHelper) {
return {
restrict: "E",
scope: { family: '=' },
template:
'<p>{{ family.name }}</p>' +
'<ul>' +
'<li ng-repeat="child in family.children">' +
'<tree family="child"></tree>' +
'</li>' +
'</ul>',
compile: function (element) {
return RecursionHelper.compile(element);
}
};
});
The Result from what i get there using the following HTML.
<!DOCTYPE html>
<html lang="en" ng-app="module">
<head>
<title></title>
</head>
<body>
<div class="container">
<div ng-controller="TreeCtrl">
<table>
<tr>
<th>Name</th>
</tr>
<tr ng-repeat="result in treeFamily">
<td> From Table: {{result.name}}</td>
</tr>
</table>
<tree family="treeFamily"></tree>
</div>
<div ng-view=""></div>
</div>
Result :
Name
From Table: Parent
HOWEVER, this is from the the ng-repeat within my table, so i know the API is sending DATA and it is readable.
{
ID: "1",
type: "Folder",
name: "Parent",
children: []
}
The problem is that it seems that the directive is not loading this data.... If however uncomment the built in data I have for that scope it works fine...
I have a feeling that my directive is loading faster than my API call so I get no data. Am i doing something wrong?
Any help will be appreciated!
Additional Research...
$scope.treeFamily = { "ID": "1", "type": "Folder", "name": "Harvest", "children": null };
$scope.treeFamily = [{ "ID": "1", "type": "Folder", "name": "Harvest", "children": null }];
This is the difference.....
If i try to do ng-repeat on
$scope.treeFamily = { "ID": "1", "type": "Folder", "name": "Harvest", "children": null };
It will not work because it is expecting an object [...]
$scope.treeFamily = [{ "ID": "1", "type": "Folder", "name": "Harvest", "children": null }];
Thus the above will work.
However, when using the recursive tree, it seems as though it does not EXPECT to see an object other than children... thus
$scope.treeFamily = [{ "ID": "1", "type": "Folder", "name": "Harvest", "children": null }];
will fail......
HOWEVER, I changed my API to return like this:
{ "ID": "1", "type": "Folder", "name": "Harvest", "children": null }
It still wont work!!!!!
This is probably an Angular version issue. Automatic promise unwrapping was removed in version 1.2. Change the code to:
var treeFamily = TreeView.query(function(){
$scope.treeFamily = treeFamily;
});
or use the more explicit promise syntax:
TreeView.query().$promise.then(function(treeFamily){
$scope.treeFamily = treeFamily;
});
I don't think the order matters. Since in the documentation of $resource it says:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
Are you sure data is returned from the server?
I have this model structure in my mind:
var app = app || {};
// Caratteristica
app.Attribute = Backbone.Model.extend({
defaults: {
name: '',
selected: false
}
});
app.Attributes = Backbone.Collection.extend({
model: app.Attribute
});
// Tipo Caratteristica
app.AttributeCategory = Backbone.Model.extend({
defaults: {
name: '',
attributes: new app.Attributes()
}
});
app.AttributeCategories = Backbone.Collection.extend({
model: app.AttributeCategory,
url: '/ajax/attributes.cfm'
});
My API in '/ajax/attributes.cfm' will give me a response like that:
[
{
"id": "1",
"name": "Type1",
"attributes": [
{
"id": "1",
"name": "Attribute1"
},
{
"id": "2",
"name": "Attribute2"
},
{
"id": "3",
"name": "Attribute3"
}
]
},
{
"id": "2",
"name": "Type2",
"attributes": [
{
"id": "1234",
"name": "Attribute1234"
},
{
"id": "2567",
"name": "Attribute2567"
}
]
}
]
My question is: will this json data be parsed correctly into my nested data structure?
I mean I want to end up having two app.AttributeCategory items in my app.AttributeCategories collection. Each of these two items must then have its attributes property filled with the corresponding app.Attributes collection.
If the answer was NO, how would I override the parse() function for achieving that result?
I did it like this:
// Tipo Caratteristica
app.AttributeCategory = Backbone.Model.extend({
defaults: {
name: ''
},
initialize: function(options) {
this.set('attributes', new app.Attributes(options.attributes));
Backbone.Model.prototype.apply(this, arguments);
}
});
But better use RationalModel for set up relations betweens models
You can create the collection inside an initialize method in your AttributeCategory model, like this:
app.AttributeCategory = Backbone.Model.extend({
...
initialize: function () {
this.set('attributes', new app.Attributes(this.get('attributes')));
}
});
While going through some AngularJS examples, I see how easy it is to repeat and create structures. However, I couldn't figure out how to do the following.
Assume we have a json structure like
{
"Asia": {
"India": {
"Bangalore": {},
"Mumbai": {},
"New Delhi": {}
},
"China": {
"Beijing": {},
"Shanghai": {}
}
},
"Europe": {
"France": {
"Paris": {}
},
"Germany": {
"Berlin": {}
}
}
}
What I want to do is - Convert this JSON structure to an Unordered list - The depth of this kind of structure is not known, and can possibly go deeper. How do I perform repeats dynamically using Angular JS?
Your JSON is poorly structured, you're using property names to carry data.
What you really want is something like this:
$scope.continents = [
{
name: 'Asia',
countries: [
{
name: 'India',
cities: [
'Bangalore',
'Mumbai',
'New Delhi'
]
},
{
name: 'China',
cities: [
'Beijing',
'Shanghai'
]
},
]
},
{
name: 'Europe',
countries: [
{
name: 'France',
cities: [
'Peris'
]
},
{
name: 'Germany',
cities: [
'Berlin'
]
},
]
}
];
That said... what it sounds like you're looking to do is create a recursive tree directive of some sort. That gets a little tricky. You'll need to normalize your structure a bit so you can recursively examine it. Then you'll have to create two directives. One for a list, and one for an item:
Here is an example of what I mean...
function Item(name, items) {
this.name = name;
this.items = items || [];
}
app.controller('MainCtrl', function($scope) {
$scope.items = [
new Item('test'),
new Item('foo', [
new Item('foo-1'),
new Item('foo-2', [
new Item('foo-2-1'),
new Item('foo-2-2')
])
]),
new Item('whatever')
];
});
app.directive('tree', function() {
return {
template: '<ul><tree-node ng-repeat="item in items"></tree-node></ul>',
restrict: 'E',
replace: true,
scope: {
items: '=items'
}
};
});
app.directive('treeNode', function($compile) {
return {
restrict: 'E',
template: '<li>{{item.name}}</li>',
link: function(scope, elm, attrs) {
//MAGIC HERE!!!: this will do the work of inserting the next set of nodes.
if (scope.item.items.length > 0) {
var children = $compile('<tree items="item.items"></tree>')(scope);
elm.append(children);
}
}
};
});
In case anyone is interested in the "least-effort" way to do this without creating a directive (not that you shouldn't, but just offering a variation), here is a simple example:
http://jsbin.com/hokupe/1/edit
Also here's a blog post and a 10-15 minutes video on how it works:
http://gurustop.net/blog/2014/07/15/angularjs-using-templates-ng-include-create-infinite-tree/
Sample Code:
<script type="text/ng-template" id="treeLevel.html">
<ul>
<li ng-repeat="item in items">
<input type="checkbox"
name="itemSelection"
ng-model="item._Selected" />
{{item.text}}
<div ng-include=" 'treeLevel.html'"
onload="items = item.children">
</div>
</li>
</ul>
</script>
<div ng-include=" 'treeLevel.html' "
onload="items = sourceItems">
</div>