I've got an app that has two separate selects with bound properties. I want the user to be able to change the selected option on either of the selects, which will trigger a function. But the function will use the value of the selected option from BOTH of the selects to send to an $http call for database query. Here are code snippets:
html:
<div class="yearFilter">
<label>Working Year : </label>
<select ng-model="filterYear" ng-options="year.code as year.code for year in filterYears | unique:'code'" ngclick="setWorkingData({{filterSport}},{{filterYear}})">
</select>
</div>
<div class="sportFilter">
<label>Working Sport : </label>
<select ng-model="filterSport" ng-options="sport.code as sport.desc for sport in filterSports | unique:'code'" ngclick="setWorkingData({{filterSport}},{{filterYear}})"></select>
</div>
js:
var PermitsCtrl = function ($scope, $http) {
var init = function() {
$http({
...
}).success(function(data) {
...
setWorkingData($scope.filterYear,$scope.filterSport);
}).error(function(error){
...
});
}
var setWorkingData = function (year, sport) {
$http({
url ....
...
data : "action=2&year_code="+year+"&sport_code="+sport,
...
}).success(function(data) {
$scope.workingData = data.workingData;
}).error(function(error) {
...
});
}
init();
}
Upon loading the page, init() is being triggered successfully, which builds the select options and in turn triggers the setWorkingData() function correctly, which changes other values in the DOM accordingly, based on the values in the two selects (filterYear and filterSport) sent by the calling init() function. All is good so far. But changing the value in either select does not trigger the setWorkingData function. The html that is rendered looks correct:
<select ng-model="filterYear" ng-options="year.code as year.code for year in filterYears | unique:'code'" ng-change="setWorkingData(15,FB)" class="ng-valid ng-dirty"><option value="0" selected="selected">14</option><option value="1" selected="selected">15</option></select>
When I change the selected option for either select in the browser, the parameter values for the setWorkingData() function are rendered correctly in the DOM for both selects, but the function is never triggered.
setWorkingData() is defined with private access in the controller. In order for it to be accessible from the view, it must be declared on the scope:
$scope.setWorkingData = setWorkingData;
BTW, you don't need {{ }} for the arguments in ng-change/ng-click:
ng-change="setWorkingData(filterSport, filterYear)"
Related
Let's say I have a form for editing customers with a cities list specified through a select element. The real form has a lot of lists, however, this will do for now.
The form is populated by making a rest api call called /customer/{customerId}.
The list is populated by making a rest api call called /cities.
The calls are made in sequence in the controller using $http.get to get the cities and the other one via a $resource get (not in the code snippet below).
The problem is that in case the call to get the customer data for comes back first, the form selects the first city in the list instead of the right city.
Here are some code snippets:
html:
<select id="Select3" class="form-control" ng-model="formData.CityId">
<option ng-repeat="j in Cities" value="{{j.CityId}}">{{j.City}}</option>
</select>
Controller:
.controller('customerInfoCtrl', ['$scope', '$http', '$modal', '$location', 'CustomerApi', function ($scope, $http, $modal, $location, CustomerApi) {
$http.get('../api/v1/cities').success(function (data) {
$scope.Cities = data;
});
$scope.formData = CustomerApi.get({ id: $scope.customerId }, function () {
...
});
....
How do I fix this? Keep in mind that the form has lots of lists. Is there a pattern that can be applied systematically?
I know about $scope.$apply() or $scope.$apply(fn). Should I call one of these in the success function for the call that gets the cities?
Thanks
An update:
Converting to ng-options seems to do the trick. I have to do more testing.
<select id="Select3" class="form-control" ng-model="formData.CityId" ng-options="j.CityId as j.City for j in Cities">
</select>
Another update:
I created a plnkr where you can see the issue: http://plnkr.co/edit/Se8OIuxYBv4QXeH3Jgqh?p=preview
if you want to make sure that some data loads before other, use the callback function you get when using both of your get() methods.
So if you wanted to load cities first, it could be something like that:
$http.get(../api/v1/cities').success(function (data) {
$scope.Cities = data;
//now that the cities are loaded, you can loead something else here.
$scope.formData = CustomerApi.get({ id: $scope.customerId }, function () {
//this will always be loaded after cities
});
$scope.loadSmthElse....
}
I am trying to call a function on onchange event but getting reference error everytime for that function my fromtend code is this:
<label class="item item-input item-select" ng-controller="manageTeam">
<div class="input-label">
Select Form
</div>
<select onchange="goToFormDashboard($(this).children(':selected').val())" ng-model="Formname" ng-options="f.Formname for f in forms track by f.Formid"></select>
</label>
My controller is this:
.controller('manageTeam', function($scope, $ionicLoading, $http, $timeout, $rootScope, $state) {
$scope.goToFormDashboard = function (formId) {
$ionicLoading.show({template: 'Loading'});
$timeout(function () {
$ionicLoading.hide();
}, 1500);
var formID = formId;
$scope.form.length = 0;
.....
is something missing in that?
You need to use ng-change directive & no need to use jquery here you can get the value inside ng-model, Formname contains whole object you can pass form id only by doing Formname.Formid to goToFormDashboard method.
Markup
<select ng-change="goToFormDashboard(Formname.Formid)" ng-model="Formname"
ng-options="f.Formname for f in forms track by f.Formid"></select>
In JavaScript "this" means the function, from which the actual method was called. Example:
var myObject = function();
myObject.prototype.testvar = "hello world";
myObject.prototype.test = function(){
return this.testvar;
}
var obj = new myObject();
console.log(obj.test());
this would display "hello world" in your chrome console. You dont need to pass the dropdown's value to the submit handler. just have a look at
Angular JS: Pass a form's select option value as parameter on ng-submit="doStuff()"
this is exactly what you are searching for
I'm struggling to understand how data binding and Service works togheter in AngularJS.
I've 1 controller and 1 service.
My service:
myAppServices.factory('SharedData',['$http',function($http){
var _data = {
"first_prop" :{
"name": "first_prop",
"info": "infofirst_prop"
},
"second_prop" :{
"name": "second_prop",
"info": "infosecond_prop"
}
};
return {
getProp: function (name) {
return _data[name];
},
setProp: function (name,value) {
_data[name] = value;
}
};
}])
My controller code:
webAppControllers.controller('FirstCtrl', ['$scope', '$http','SharedData',
function ($scope, $http,SharedData) {
$scope.data = angular.copy(SharedData.getProp("first_prop"))
$scope.print = function()
{
console.log(SharedData.getProp("first_prop"));
}
$scope.modify = function()
{
SharedData.setProp("first_prop",$scope.data)
}
}
]);
And this is my view:
<form role="form">
<div class="form-group">
<label for="nome">Name</label>
<input type="text" ng-model="data.name" class="form-control" id="name" placeholder="Modify the name...">
</div>
<div class="form-group">
<label for="info">Info</label>
<input type="text" ng-model="data.info" class="form-control" id="info" placeholder="Modify the info ...">
</div>
<button ng-click="modify()" class="btn btn-default">Modify</button>
<button ng-click="print()" class="btn btn-default">Print</button>
</form>
If i modify the view, overriding value in the form, and then i click on print button, console.log return me values store in services(into private variable _data), and this is what i excpected.
Then if i click on modify button, and again print button, this time cosole.log shows me modified values that was saved on private var _data in my service. Also this behavior is expected.
The strange thing appear when after i did this interaction with my webapp, i again modify value in the form and click on print button without clicking on modify button. Values stored in service are modified even if i never click modify button and so i never call SharedData.setProp() method.
With the method angular.copy(), i expected to create new variable(a new copy of a variable), and so if i modify this "$scoped" variable i expected it was impossible modify directly private _data variable in my service.
Why this happens? Are there a method to modify and access private variable in service only by some defined method?
Thanks in advance.
Best regard,
Alessandro.
After you click setProp, the object in SharedData and in the scope is the same object.
To solve this, call this line again after you call "modify()":
$scope.modify = function()
{
SharedData.setProp("first_prop",$scope.data)
// grab another copy of the first_prop object, or else its the same reference.
$scope.data = angular.copy(SharedData.getProp("first_prop"));
}
There are other ways to solve this, such as in setProp, pass angular.copy($scope.data), etc.
Always be aware that when you pass objects in javascript, the pass by reference, not by value.
I have four different inputs and want to send value of changed input to controller without pressing send button or whatever.
How i got it all inputs should to have next syntaxis.
<input ng-model="borderRadius" ng-change="change()">
<input ng-model="background" ng-change="change()">
Or not?
I want to get them in my app.js
control.controller('generatorOptions', function ($scope) {
$scope.buttonStyle = {
"border-radius" : " -- here is value of it -- ",
"background" : " -- here is value of it -- "
};
});
Update: That is working just fine, but how can i optimise code? https://github.com/tanotify/Button-style-generator/blob/master/public/assets/scripts.min.js
First you need to define in your controller:
control.controller('generatorOptions', function ($scope) {
$scope.buttonStyle={};
$scope.change = function() {
console.log("borderRadius value:"+$scope.buttonStyle.borderRadius);
console.log("background value:"+$scope.buttonStyle.background);
};
});
This is your view:
<input ng-model="buttonStyle.borderRadius" ng-change="change()">
<input ng-model="buttonStyle.background" ng-change="change()">
It means, that first you need define object(buttonStyle) in you controller, than this object is accesseble in you view and in view you will define new properties(radius and background) of your object which you can access in controller by object.
I had the problem of getting resource data from an API, loading that into a dropdown select, and setting the selected value of the dropdown. Basically it was trying to set the value of the dropdown before it was populated. I have two different ways to do this, but was wondering if anyone had a "better" way, or a "better practice" way. Here are my two ways.
Option 1: Directive attached to ng-repeat element
Controller
$scope.users = User.query();
$scope.dude={
name: "Dude",
id: 3
}
HTML
<select id="userSelect" ng-show="users.length">
<option ng-repeat="user in users" choose dude="dude">{{user.username}}</option>
</select>
Directive
.directive('choose', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
if (scope.user) {
if (scope.user.id === scope.dude.id) {
$("#userSelect").val(element.val());
}
}
}
}
});
Option 2: Watch for the users length to change (the call is returned, and the dropdown is populated)
Controller
$scope.users = User.query();
$scope.dude={
name: "Dude",
id: 3
}
$scope.$watch('users.length', function() {
$("#userSelect").val($scope.dude.id);
});
HTML
<select id="userSelect" ng-show="users.length">
<option ng-repeat="user in users" value="{{user.id}}>{{user.username}}</option>
</select>
Any opinions on which one is better practice? Or if there is any other better way?
So, promises are your friend for this sort of thing. I'm going to use $http instead of resources, because I'm more familiar with it, but I'm pretty sure recent version of
resources return promises (or can).
Also.. no jquery in your controllers. Use directives like ng-model to change input values.
Also using ng-options to populate the options for a select is more powerful than using ng-repeat on an "option" element.
Here's what a lot of my code looks like (except that I'm using jsonp here instead of just get). http://jsfiddle.net/HB7LU/142/
CONTROLLER:
function MyCtrl($scope, $http) {
// The id we want to select when the data loads:
var desiredId = 3;
// Overly complicated $http request so that I can load with jsonp:
// You could normally use just $http.get()
$scope.users = $http.jsonp('http://www.json-generator.com/j/geku?callback=JSON_CALLBACK').then(function(d) { return d.data.result; });
// Use the returned promise to set the selection when the data loads:
// I'm using the "underscore" library function "findWhere" to set my
// model to the object I want selected:
$scope.users.then(function(d) {
$scope.uservalue = _.findWhere(d,{id:desiredId});
});
}
HTML:
<div ng-controller="MyCtrl">
{{uservalue | json}}
<select ng-model="uservalue" ng-show="users.length" ng-options="user.name for user in users">
</select>
</div>