AngularJS setting dummy option element before valid ajax data arrives - javascript

As I understand AngularJS sets default value when using ng-model directive. I need to populate select element with only valid options which retrieved by $http.get. The problem is that on top of all valid elements I have one dummy element vith value ?. How to get rid of it?
<select id="environment" name="environment_name" ng-model="environment" ng-options="e.name for e in ENVIRONMENTS" required>
</select>
$scope.ENVIRONMENTS = [];
$http.get("/getEnvironments").success(function(data){
data.forEach(function(el){
$scope.ENVIRONMENTS.push(el);
});
}).error(function (data) {
$scope.showErrorMsg("Cannot get ENVIRONMENTS.");
});
$scope.environment = $scope.ENVIRONMENTS[0];

You can add temporary value to your $scope.ENVIRONMENTS array ie:
$scope.ENVIRONMENTS = [{name:"Please wait"}];
and delete it after you get data from your backed
$scope.ENVIRONMENTS.shift()
Please see demo below
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope, $http) {
$scope.ENVIRONMENTS = [{
name: "Please wait"
}];
$http.get("/getEnvironments").then(function(data) {
$scope.ENVIRONMENTS.shift()
data.forEach(function(el) {
$scope.ENVIRONMENTS.push(el);
})
//set default value after you get data from backend
$scope.environment = $scope.ENVIRONMENTS[0];
}, function(data) {
$scope.showErrorMsg("Cannot get ENVIRONMENTS.");
});
$scope.environment = $scope.ENVIRONMENTS[0];
$scope.showErrorMsg = function(msg) {
console.log(msg);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<select id="environment" name="environment_name" ng-model="environment" ng-options="e.name for e in ENVIRONMENTS" required>
</select>
</div>
</div>

Related

Angular filter: how to get filter complete callback?

I am looking for an callback function after $filter filter completes the filtering the data
HTML
<input type="text" ng-model="searchvalue">
<span ng-click="searchbtn()">Search</span>
JS
$scope.searchbtn = function() {
$scope.loading = true;
$scope.mysearchvalue = $scope.searchvalue;
}
When user enters keyword my data will be filtered and i need a callback function after filtering the data.
i have tried using "DOMSubtreeModified" but returning continues logs
var myElement = angular.element(document.getElementById("mycontent"));
myElement.bind("DOMSubtreeModified", function() {
console.log("keep outputting this message");
});
As said in my comment, why not just use a little delay (debounce) and filter results in controller w/o separate input button. Consider
HTML template
<body ng-controller="MainCtrl">
<pre ng-bind="filteredData | json"></pre>
<input type="text" ng-model="search" ng-model-options="{debounce:250}">
</body>
JavaScript
angular.module('app', [])
.controller('MainCtrl', function($scope, $filter) {
$scope.data = [{text:'aa'},{text:'ab'}];
$scope.$watch('search', function(val) {
$scope.filteredData = $filter('filter')($scope.data, val);
});
});

ngModel not updating in select

I have a directive rendering inputs based on a configuration sent by the server. Everything is working great except for the 'select' input.
No matter what I try, the ng-model does not update.
I have trimmed my code a lot to isolate the problem :
Javascript :
var myApp = angular.module('example', []);
myApp.factory('DynamicData', [function() {
return {
data: {
backup_frequency: 604800
}
};
}])
.directive('dynamicInput',
['$compile', 'DynamicData', function($compile, DynamicData) {
/**
* Render the input
*/
var render = function render() {
var input = angular.element('<select class="form-control" ng-model="inner.backup_frequency" ng-options="option.value as option.title for option in options"></select>');
return input;
};
var getInput = function ()
{
var input = render();
return input ? input[0].outerHTML : '';
};
var getTemplate = function(){
var template = '<div class="form-group">' +
'Select input ' +
'<div class="col-md-7">' + getInput() + '</div>' +
'</div>';
return template;
};
return {
restrict : 'E',
scope: {
content:'=content',
},
link : function(scope, element, attrs) {
var template = getTemplate();
scope.options = [
{title: "Daily", value: 86400},
{title: "Weekly", value: 604800},
{title: "Monthly", value: 2678400},
];
scope.inner = DynamicData.data;
console.info('inner data', scope.inner);
element.html(template);
element.replaceWith($compile(element.contents())(scope));
}
};
}])
.controller('FormCtrl', ['DynamicData', '$scope', function (DynamicData, $scope){
$scope.app = {};
$scope.save = function save() {
$scope.value = DynamicData.data.backup_frequency;
console.info('DynamicData', DynamicData.data);
};
}]);
HTML :
<head>
<script data-require="angular.js#1.3.8" data-semver="1.3.8" src="https://code.angularjs.org/1.3.8/angular.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body>
<h1>Dynamic Input :</h1>
<div data-ng-controller="FormCtrl">
<dynamic-input class="form-group" content="app"></dynamic-input>
<span data-ng-bind="value"></span><br/>
<button class="btn btn-primary" ng-click="save()">Save</button>
</div>
</body>
</html>
A working plunker is available : http://plnkr.co/edit/mNBTJzZXjX6mLyPz6NCI?p=preview
Do you have any ideas why the ng-model in the select is not updated ?
EDIT : What I want to achieve is updating the variable "inner.backup_frequency" (the reference to my data object returned by my factory DynamicData). As you can see in the plunker, whenever I change the option in the select, the variable contained in the ng-model is not updated.
Thank you for your time.
I had a similar issue and find if you directly bind ng-model to a scope variable as below, the scope variable selection somehow will not be updated.
<select ng-model="selection" ng-options="option for option in options">
Instead, define a view model in your scope and bind ng-model to a field of view model the view model field will be updated.
<select ng-model="viewmodel.selection" ng-options="option for option in options">
In your controller, you should do this:
app.controller('SomeCtrl', ['$scope'
function($scope)
{
$scope.viewmodel = {};
});
I've fixed it. What you needed to do was not to replace the element's content with the compiled content, but rather replace it with the raw HTML and only then compile it.
element.html(template);
$compile(element.contents())(scope);
Working Plunker.
EDIT: I've edited your code to work without the directive:
Plunker without directive.
EDIT 2: Also a version where it works with the directive, but without the compile:
Plunker.

Default select not working in ngoptions when model initialized with ng-init

I have a cascading select with 2nd dropdown appears based on the first dropdown selection. An extra blank option appears which is not part of intended behaviour of the dropdown.
<select ng-init="order.attempt_status_sub = order.attempt_status_sub || subStatuses[0].name"
data-ng-model="order.attempt_status_sub" ng-options="subStatus.name as
subStatus.name for subStatus in subStatuses">
</select>
How do I avoid empty extra select from appearing in dropdown?
code for my cascading dropdowns is
<div class="form-group" ng-class="{ 'has-error' : submitted && orderForm.content.$invalid}">
<div class="controls">
<select ng-change="getSubStatuses(order.attempt_status)" data-ng-model="order.attempt_status" ng-options="status.name as status.name for status in statuses">
</select>
</div>
</div>
<div class="form-group" ng-show="subStatuses.length" ng-class="{ 'has-error' : submitted && orderForm.content.$invalid}">
<div class="controls">
<select ng-init="order.attempt_status_sub = order.attempt_status_sub || subStatuses[0].name" data-ng-model="order.attempt_status_sub" ng-options="subStatus.name as subStatus.name for subStatus in subStatuses">
</select>
</div>
</div>
$scope.getSubStatuses = function(attempt_status) {
var statuses = $scope.statuses;
for(var index in statuses) {
if(statuses[index].name === attempt_status) {
if(statuses[index].children) {
$scope.subStatuses = statuses[index].children;
} else {
$scope.subStatuses = [];
}
break;
}
}
};
I am assuming you are setting this for an asynchronously bound data. ng-init is really bad for this purpose. Thumb rule is do not use ng-init for something which controller is supposed to do. You should set up your view model in the controller. The reason why it could fail when the data is bound asynchronously is because ng-init'ed expression is not watched. So it just runs once during the first rendering and the subStatuses is not yet populated and it falls back to showing the default empty option. And even if the data is populated and view is updated during the later digest cycle ng-init expression does not run again.
From Doc
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
So just initialize it in your controller, instead of using ng-init.
$scope.getSubStatuses = function(attempt_status) {
var statuses = $scope.statuses;
for(var index in statuses) {
if(statuses[index].name === attempt_status) {
if(statuses[index].children) {
$scope.subStatuses = statuses[index].children;
} else {
$scope.subStatuses = [];
}
break;
}
}
//Initialize the model here, assuming $scope.order is already initialized
$scope.order.attempt_status_sub = ($scope.subStatuses[0] ||{}).name;
};
angular.module('app', []).controller('ctrl', function($scope, $timeout) {
$timeout(function() {
$scope.subStatuses = [{
name: 'test1'
}, {
name: 'test2'
}, {
name: 'test3'
}];
$scope.order = {
attempt_status_sub1: $scope.subStatuses[0].name
};
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
With Ng-Init
<select ng-init="order.attempt_status_sub = order.attempt_status_sub || subStatuses[0].name" data-ng-model="order.attempt_status_sub" ng-options="subStatus.name as
subStatus.name for subStatus in subStatuses">
</select>
With controller initialization.
<select data-ng-model="order.attempt_status_sub1" ng-options="subStatus.name as
subStatus.name for subStatus in subStatuses">
</select>
</div>

JSON populated form elements not filtering using angularJs filter

The Select element does get populated by the http request, but the options in the select element are not updating as the ng-model is changed. I am new to angularJs so im assuming its a simple fix. I have tried many variation with no positive results.
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $http) {
$http.get('http://graph.facebook.com/platform')
.success(function(data) {
})
});
</script>
<body ng-controller="MainCtrl">
Search:
<input type="search" ng-model="searchText" />
<select>
<option ng-repeat="item in data | filter: searchText">{{item}}</option>
</select>
<br>
<pre>{{ data | json}}</pre>
</body>
http://plnkr.co/edit/C39yVDsG3OcfvwjVSxP9?p=preview
TarranJones, this Plunker should clear things up. Hard to give you a 100% clear answer without a look at your data. You should be able to figure it out from here.
http://plnkr.co/edit/UYm0SwtU6ePZkZLx2w2U?p=preview
To Answer your question in the comments I would replace:
app.controller('MainCtrl', function($scope) {
$scope.colors = [
{name:'black', shade:'dark'},
{name:'white', shade:'light'},
{name:'red', shade:'dark'},
{name:'blue', shade:'dark'},
{name:'yellow', shade:'light'}
];
});
With:
app.controller('MainCtrl', function($scope, $http) {
$http.get('http://www.foo.com')
.success(function(data) {
$scope.colors = data;
})
.error(function() {
console.log('My name is Error, now eat it!);
});
});
Make sure to inject the $http.
Plunker here. http://plnkr.co/edit/UYm0SwtU6ePZkZLx2w2U?p=preview
UPDATE:
Tarran also ran into the problem of filtering a single returned JSON object from an API. Angular Filters can only accept arrays and so would not accept the object. In order for Tarran to filter the object he must first iterate through the object and store the object properties to an array. Once the array is then returned to the $scope you can filter the results. The plunker and code is provided below: http://plnkr.co/edit/9M3zZFN5jyV8w7fg7EE3?p=preview
Controller:
$http.get('http://graph.facebook.com/4')
.success(function(data) {
//CREATE AN EMPTY ARRAY
result = [];
//ITERATES THROUGH THE OBJECT SAVING THE OBJECTS PROPERTIES TO ARRAY
for (var i in data) {
if (data.hasOwnProperty(i)) {
//PUSHES THE PROPERTIES ONTO THE ARRAY
result.push(data[i]);
}
}
//SETS THE NEW DATASET TO THE ARRAY AND RETURNS TO $SCOPE
$scope.dataset = result;
});
HTML:
{{dataset}}
<BR>
<BR>Search:
<input type="search" ng-model="searchText" />
<BR>
<BR>
<select>
<option ng-repeat="data in dataset | filter: searchText">{{data}}</option>
</select>
Try to init your data as object before all.
$scope.data = {}
Or in HTML :
<div ng-controller="FetchCtrl" data-ng-init="data={}; fetch()">

Sending POST with hidden <input> value does't work in AngularJs

In my web app, There are many form on a page. I want to submit it with AngularJS for specific form.
In each of form, it need unique ID with Hidden Value to submit. But value="UNIQUE_ID" seen doesn't work in hidden input box in AngularJS.
My HTML
<div ng-app>
<div ng-controller="SearchCtrl">
<form class="well form-search">
<input type="text" ng-model="keywords" name="qaq_id" value="UNIQUE_ID">
<pre ng-model="result">
{{result}}
</pre>
<form>
</div>
</div>
This is js script
function SearchCtrl($scope, $http) {
$scope.url = 'qa/vote_up'; // The url of our search
// The function that will be executed on button click (ng-click="search()")
$scope.search = function() {
// Create the http post request
// the data holds the keywords
// The request is a JSON request.
$http.post($scope.url, { "data" : $scope.keywords}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
$scope.result = data; // Show result from server in our <pre></pre> element
})
.
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}
It may be that the only reason your code is not working is that $scope.keywords is a simple variable (with a text value) instead of an Object, which is required - see http://docs.angularjs.org/api/ng.$http#Usage
As angularJS works with variables within its own scope - its models, a form becomes just a way to interact with those models, wich can be sent via whatever method you want.
You can have a hidden field, yes, but in angularJS it isn't even necessary. You only need that information to be defined in the controller itself - randomly generated for each instance, or received from some other source.. Or you can define it yourself, upon the loading of the controller, for instance.
So (and only for sake of clarity) if you define a formData variable within your formCtrl:
Your HTML:
<div ng-app>
<div ng-controller="SearchCtrl">
<form class="well form-search">
<input type="text" ng-model="formData.title">
<input type="textarea" ng-model="formData.body">
<button ng-click="sendData()">Send!</button>
</form>
<pre ng-model="result">
{{result}}
</pre>
</div>
</div>
And your controller:
function SearchCtrl($scope, $http) {
$scope.url = 'qa/vote_up'; // The url of our search
// there is a formData object for each instance of
// SearchCtrl, with an id defined randomly
// Code from http://stackoverflow.com/a/1349426/1794563
function makeid()
{
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
$scope.formData = {
title: "",
text: ""
};
$scope.formData.id = makeid();
// The function that will be executed on button click (ng-click="sendData()")
$scope.sendData = function() {
// Create the http post request
// the data holds the keywords
// The request is a JSON request.
$http.post($scope.url, { "data" : $scope.formData}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
$scope.result = data; // Show result from server in our <pre></pre> element
})
.
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
}
Also: If you wanted to set the unique id on the html itself, you could add an input type="hidden" and set it's ng-model attribute to formData.id, and whichever value you set it to, the model would have it binded. using a hidden input won't work, as the value attribute doesn't update the angularJS Model assigned via ng-model. Use ng-init instead, to set up the value:
HTML with 2 forms:
<div ng-controller="SearchCtrl" ng-init="formData.id='FORM1'">
<form class="well form-search">
<input type="text" ng-model="formData.title">
<input type="textarea" ng-model="formData.body">
<button ng-click="sendData()">Send!</button>
</form>
</div>
<div ng-controller="SearchCtrl" ng-init="formData.id='FORM2'">
<form class="well form-search">
<input type="text" ng-model="formData.title">
<input type="textarea" ng-model="formData.body">
<button ng-click="sendData()">Send!</button>
</form>
</div>
You can add a hidden field, but it accomplishes nothing - the ng-init attribute does everything you need.

Categories

Resources