I would like to dynamically load select elements from an API request.
Here is my controller:
var myApp = angular.module('myApp',[]).controller('tripCtrl', function($scope){
//Call to API to get people
$scope.people = [
{
"id": 1,
"name": "Joe Hamlet"
},
{
"id": 2,
"name": "Mary Jane"
},
{
"id": 3,
"name": "Tom Lee"
}
];
//Call to API to get the element to load
$scope.selectElement =
{
"Options": "person[dynamicValue] as person[dynamicDisplayName] for person in people",
"DisplayName": "name",
"Value": "id"
};
//Dynamicly load properties
$scope.dynamicValue = $scope.selectElement.DisplayName;
$scope.dynamicDisplayName = $scope.selectElement.Value;
});
HTML:
<select ng-model="selectedPerson" ng-options="{{selectElement.Options}}">
<option value="">Select</option>
</select>
{{selectedPerson}}
I created a JSFiddle trying to accomplish this. http://jsfiddle.net/HB7LU/9493/
I found this question which I was able to implement, but when I tried to set the ng-options from the Element's Options property, it failed to load. When inspected the HTML the code looks to be set properly, but the model binding isn't working.
Edit 12/28/2014:
After updating the Angular version in the original JS Fiddle, it worked properly, however when I expanded to use an actually API, I found another issue with loading ng-options dynamically. Here is the more in depth JS Fiddle: http://jsfiddle.net/zjFp4/330/
Also here is my updated controller. The dataService.getElement() calls a hard coded string, where as the dataService.getElementFromApi() calls the same exact string, just from json-generator (which is the mock API). When inspected the objects set from the API, everything is there, so it must be an issue with the binding in Angular. Any ideas on how to fix this?
function tripCtrl($scope, dataService) {
//Call to API to get people
dataService.getPeople().then(
function (event) {
$scope.people = event;
},
function (s) {
console.log(s); }
);
//Call to API to get the element to load
$scope.selectElement = dataService.getElement();
dataService.getElementFromApi().then(
function (event) {
$scope.apiElement = event;
$scope.dynamicValue = $scope.apiElement.Value;
$scope.dynamicDisplayName = $scope.apiElement.DisplayName;
},
function (s) {
console.log(s); }
);
}
Related
I am quite new to AngularJS and have the following problem: I have two select boxes which I fill with data that is stored in an external JSON file. I want to dynamically fill the second box depending on the item that was selected in the first box. Every item in the first box has a key called relation. I want to use the value of this key as a trigger for dynamically updating the items available in the second box.
Can anyone help me on this? I have already tried to write a function but it doesn't work. Thanx in advance.
This is the JSON structure:
{
"filterElements": [
{
"parameters": [
{
"paraName": "paraOne",
"relation": "default"
},
{
"paraName": "paraTwo",
"relation": "default"
},
{
"paraName": "paraThree",
"relation": "defaultshort"
}
]
},
{
"relations": [
{
"defaultrelation":
[
"equals",
"does not equal",
"greater than",
"less than"
]
},
{
"defaultshort":
[
"equals",
"does not equal"
]
}
]
}
]
}
MyFiddle
Based on you're fiddle, you're missing to bootstrap application. Example:
<body ng-app="list">
...
</body>
After that, you need to register ListCtrl to the existing application.
var app = angular.module('list',[]);
app.controller('ListCtrl', ListCtrl);
function ListCtrl($scope, $http) {
...
}
After doing this, you're controller will be executed, you're request will be made and you can bind the data to your view.
I got it. Here is the solution if anyone is interested:
$scope.change = function (paraSelection) {
$scope.filteredrelations = [];
for(var key in $scope.relations){
if(angular.isDefined($scope.relations[key][paraSelection.relation])){
$scope.filteredrelations = $scope.relations[key] paraSelection.relation];
}
}
};
Complete working example can be found here:
Plunker
I have quite an interesting question (I hope) for all you AngularJS gurus out there. I am looking to create a dynamic list of form input fields based on a SELECT dropdown. As an example, we have a number of categories with each category having a set of specifications which are unique to that category. To help with the explanation we have the following:
Firstly, in the controller we start by initializing the models.
$scope.category = {};
$scope.category.specs = [];
Next we ready the data to be used in the form (actually retrieved from the server via $http). We also initialize a variable to the first element in the categories array.
$scope.categories = [
{ "id": "1", "name": "mobile", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Camera type" } ] },
{ "id": "2", "name": "laptop", specs: [
{ "id": "1", "label": "Operating System" },
{ "id": "2", "label": "Graphics Card" } ] }
};
$scope.selectedCategory = $scope.categories[0];
In the form, we have a dropdown which when selected loads the appropriate input fields specific to that category. We use the ngRepeat directive to accomplish this. This is a dynamic list of fields based on $scope.categories.specs. (please note the ???)
<select ng-model="selectedCategory" ng-options="category.name for category in categories"></select>
<div ng-repeat="spec in selectedCategory.specs">
<label>{{spec.label}}</label>
<input type="text" ng-model="???">
</div>
Ultimately, when the user clicks the submit button, we would like to extract the category he/she has selected and then package it together with the specifications they have filled in. The post request should contain something like the following for instance (of course, I only included one spec item, but in reality there would be many):
{ "id": "1", specs [ { "id": "2", "details": "RADEON HD 8970M" } ] }
Unfortunately I am not really sure how to accomplish this. I need to somehow create an array for the spec model, and then ensure that both the ID and user entered data are appropriately extracted... what goes in the ??? and what do we do after? Any help would be much appreciated.
this is how I do it. I make a form, validate it with angular, and then when its valid I submit it with a function.
<form name="signup_form" novalidate ng-submit="signupForm()"></form>
$scope.signupForm = function() {
var data = $scope.signup;
$http({
method : 'POST',
url : 'http://yoursite.com/mail.php',
data : $.param(data), // pass in data as strings
headers : { 'Content-Type': 'application/x-www-form-urlencoded' } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
});
}
also if you want to look at another form validation system for angular check out http://nimbly.github.io/angular-formly/#!/ It may help you solve your current form system.
In the controller, initialize $scope.specDetails as follows:
$scope.specDetails = {};
angular.forEach($scope.categories, function (category, index1) {
$scope.specDetails[category.id] = {};
angular.forEach(category.specs, function (spec, index2) {
$scope.specDetails[category.id][spec.id] = '';
});
});
In the html, replace "???" with specDetails[selectedCategory.id][spec.id]
I'm not sure what I'm missing here but I'm trying to update a value inside an array, and then have this reflected in the repeater. Do I need to make a copy or something before applying the update in order for Angular to show the changes?
---sample json
"lists": [
{
"name": "one",
"enabled": true
},
{
"name": "two",
"enabled": false
}
]
!-- code
setTimeout(function(){
$scope.$apply(function(){
$scope.lists[1].enabled = true;
});
},1);
!--- html
<span data-ng-repeat="list in lists | filter: {enabled: true}"></span>
From the "other controller" you are talking about broadcast an event:
$rootScope.$broadcast('listChange', listArray);
Then in the controller that controls your $scope.lists value, listen for the event:
$scope.$on('listChange', function(event, list) {
$scope.$apply(function() {
// Update goes here
});
});
It might help if you use angulars built-in timeout function $timeout.
$timeout(function(){
$scope.lists[1].enabled = true;
},1);
Documentation here: https://docs.angularjs.org/api/ng/service/$timeout
Add your lists variable to the $scope inside your controller like this:
$scope.lists = [{
"name": "one",
"enabled": true
},
{
"name": "two",
"enabled": false
}];
You also have to add something inside the <span>, because the Html you posted would not display anything. (You need to do something inside the <span> tag in order to show something)
<span ng-repeat="list in lists | filter:{enabled: true}">{{list.name}}</span>
Now when you update the lists variable, this will be reflected in the Html immediately.
Works fine for me. Hope i could help!
What is the best place to load the options for select2 asynchronously. I want the same facility as ajax, but instead of select2 sending an ajax request, it needs to load the values asynchronously from a promise object. Below code works, in which I load the data in query, but which means every keystroke, invocation of select dropdown, it will query the data. so, what is the correct configuration?
code:
var items2 = [
{
"Id": 1,
"Name": "First"
},
{
"Id": 2,
"Name": "Second"
},
{
"Id": 3,
"Name": "Third"
}
];
var names = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve(items2);
}, 200);
return deferred.promise;
};
var query: function (query) {
var results = [];
names().then(function(d){
$.each(d, function(index, item){
results.push({
id: item.Id,
text: item.Name
});
});
query.callback({ results: results });
})
};
Edit
looking at the source, it looks like it only allows either ajax or local for querying data. It would have been ideal if local takes a function which returns the data. Am I on the right track? is there an easy way to patch it?
thanks
// exports
window.Select2 = {
query: {
ajax: ajax,
local: local,
tags: tags
}, util: {
debounce: debounce,
markMatch: markMatch,
escapeMarkup: defaultEscapeMarkup,
stripDiacritics: stripDiacritics
}, "class": {
"abstract": AbstractSelect2,
"single": SingleSelect2,
"multi": MultiSelect2
}
};
Edit2:
'local` indeed accepts a function. but it doesn't play nicely with remote data, as the data is received with a delay (async), drop down is not populated with the new data. I have to close and open the drop-down again. This is not intuitive for the user.
As far I see, select2 invokes an ajax call for each key stroke, and opening the select box. I can get the same behavior using query (with promise) as in the original question. I was expecting select2 loads the data once, then do rest locally (search, and further invocation till any data change). looks like this is not an option. I may just cache my results locally. Better answer welcome.
I have an object (analysisLogData) that I use to generate a table using KnockoutJS. Here's the viewModel containing this object:
function AppViewModel() {
var self = this;
self.analysisLogData = ko.observableArray();
self.analysisLogTitle = ko.observable("Warnings")
self.changeAnalysisLog = function(title) {
self.analysisLogTitle(title)
}
var data =
{
"Warnings": [
{
"number": 3002,
"description": "There may be a problem with the device you are using if you use the default profile"
},
{
"number": 3001,
"description": "There may be a problem with the device you are using if you don't use the default profile"
}
]
,
"Errors": [
{
"number": 1000,
"description": "No networks are loaded"
},
{
"number": 1002,
"description": "No devices are loaded"
}]
}
self.addLog = function (type, content) {
self.analysisLogData()[type].push(content);
}
self.analysisLogData.push(data)
}
ko.applyBindings(new AppViewModel());
You can see the result here in a JSFiddle: http://jsfiddle.net/etiennenoel/V4r2e/5/
I want to be able to add an error or a warning without losing the warnings or errors already present.
I tried to do the following in the self.addLog function:
self.addLog = function (type, content) {
self.analysisLogData()[type].push(content);
}
but it says that it can't push to an undefined object...
Ok, after playing around in fiddle. I believe that you need to do some changes in how you pushed data in the observable array. But without doing a lot of modification check my solution in this link.
jsfiddle example
self.addLog = function (type, content) {
self.analysisLogData()[0][type].push({
"number": 1002,
"description": content
});
}
And data object should be
"Warnings": ko.observableArray([........]),
"Errors": ko.observableArray([..........])
I did two things
Modify Warnings & Errors to be an Observable Array
I pushed the data in this self.analysisLogData()[0][type].push instead of self.analysisLogData()[type].push
self.analysisLogData() is an array which contains arrays of Errors/Warnings.
I'm not sure if that's how you want your data structured.
To get the fiddle to work you can replace the addLog function with this:
self.addLog = function (type, content) {
self.analysisLogData()[0][type].push(content);
}