Angular with directive select and submit - javascript

Angular creation of a blank option in select can be very tiresome. Any solutions for this:
angular.module('myApp', ['ui.bootstrap','angularSpinners'])
.controller("StudentController",function($scope,$http,$modal){
$scope.students=[
{"first_name":"Miguel","last_name":"Smith","id":10},
{"first_name":"Ann","last_name":"Jones","id":11}
];
$scope.courselist=[
{"first_name":"Dave","last_name":"Smith","id":8},
{"first_name":"Mike","last_name":"Winner","id":9}
];
$scope.sDefault=$scope.courselist[0].id;
})
.directive('thisView', function() {
return {
restrict:"E",
scope: {
students:"=",
courselist:"=",
sDefault:"="
},
templateUrl: 'this-view.html',
controller: function($scope) {
$scope.remStudent = function(remId) {
$scope.students.splice( $.inArray(remId, $scope.students), 1 );
$scope.courselist.push(data);//data will be returned from http not relevant for question
};
$scope.addStudent = function(addId) {//this from student list
$scope.students.push(data);//data will be returned from http not relevant for question
$scope.courselist.splice( $.inArray(addId, $scope.courselist), 1 );
};
}
});
On main page:
<div ng-controller="StudentController">
{{sDefault}} //works
<this-view students="students" courselist="courselist" sDefault="sDefault"></this-view>
</div>
this-view.html:
{{sDefault}} //doesn't work
<form ng-submit="addStudent(addStudentId)">
<select class="form-control" ng-model="addStudentId" >
<option value="" selected>Default</option>
<option ng-repeat="option in courselist" value="{{option.id}}" >{{option.first_name}} {{option.last_name}}</option>
</select>
<input type="submit" class="btn btn-primary btn-lg" value="Add Student" />
</form>
I have tried a select without a blank option but when I did this the model would not post on submit, so I added a default:
<option value="" selected>Default</option>
but when I submit the form the select array (courselist) is spliced and a blank option is added as well as the the default above and the remaining courselist.
Is there a way to just add the options from the courselist array (without the angular blank) and still be able to post it.
I have tried to send a default - "sDefault" but for some reason this doesn't pass into the directive.
Essentially I need to NOT have a default select, and be able to post to a form, simple you'd think?!
ng-options are an issue because it is not a single item output i.e. {{first_name}} {{last_name}} and this again wouldn't post, anyway.
Any help appreciated.
Further info:
Thanks to Timcodes below I have found the cause of the problem but still not a solution.
Example working without $http
The above code I gave was to avoid lots of $http. But I have noticed that using the real version with api creates the blank select:
.controller("StudentController",function($scope,$http,$modal){
$scope.studentlist = function() {
$http.get('/api').
success(function(data) {
$scope.courselist = data;
});
};
$scope.studentlist();
});
$scope.courselist outputs
[{"first_name":"Miguel","last_name":"Smith","id":10}]
If I put this directly into the controller:
$scope.courselist=[{"first_name":"Miguel","last_name":"Smith","id":10}]
it works,as in the plunker example, but with the $http it creates a blank first select option.
SOLUTION: I decided to preload the courselist from the backend into a javascript variable rather than api it. This way the list was defined before creating selectedItem.
I'd be interested to hear angular solutions though.

You can use 'As' Syntax to concat two values to gather. for example ng-options = "a as (a.first + a.last) for a in alist". Also you can use ng-model to set a default. To do this initialize the ng-model to the default value, in like a activation function that is called when you controller is first ran. Then to keep blank values from appearing after you splice the array reset the ng-model to another value. for instance you could have a model called selectedCourse and then set that intially to the first course in the array of courses, then when you call the add function, after you complete any ansync stuff, then splice the array. Reset the selected to the first course in the course array again. Something like below, also have linked plunk
// js
app.directive('thisView', function() {
return {
restrict:"E",
scope: {
students:"=",
courselist:"=",
},
templateUrl: 'thisView.html',
controller: function($scope) {
//intialize selected course
$scope.selectedCourse = $scope.courselist[0];
$scope.remStudent = function(remId) {
$scope.students.splice( $.inArray(remId, $scope.students), 1 );
$scope.courselist.push(data);//data will be returned from http not relevant for question
};
$scope.addStudent = function() {
// pretending this is async op that returns a promise
$scope.students.push(data).then(function(){
var idx = $scope.courselist.indexOf($scope.selectedCourse);
$scope.courselist.splice(idx,1);
// reset slected course
$scope.selectedCourse = $scope.courselist[0];
});
};
}
}
//html
<form ng-submit="addStudent()">
<select name="test"
ng-options=
"course as (course.first_name + ' ' + course.last_name)
for course in courselist"
ng-model="selectedCourse" >
</select>
<input type="submit" class="btn btn-primary btn-lg" value="Add Student" />
</form>
https://docs.angularjs.org/api/ng/directive/ngOptions
http://plnkr.co/edit/7Hrv2E5DEgjn56wU0emO?p=preview

Related

Checkbox only updates model after second click

Please look at the following Plunkr: http://plnkr.co/edit/hwVL3xtnD9hGJLpULDjI?p=preview
When you click the checkbox the first time the model doesn't update.
Why does this happens?
var app = angular.module('main', ['ngMaterial'])
.controller('DemoCtrl', function($scope, $filter, $http) {
$scope.propdata = [];
$scope.success ="loading..";
var url = "api.json";
$http({url:url,
method:"GET"
}).then(function (rs){
$scope.propdata = rs.data[0];
$scope.success ="done..";
});
});
You json is returning has a 1 value, not a true or false. If you change your data to true or false, it will recognize the initial value. Another option is to cast the "1" to boolean, and then assign it.
Edit:
Another option is to set the ng-true-value and ng-false-value on your checkbox, so it recognizes the 0 and 1 values you are using. Notice the simple quotes after the double ones "'1'" to recognize the string. Example here:
HTML
<div class="checkbox {{font_size}}">
<label for="garden_service">
<input type="checkbox" id="garden_service"
ng-checked="propdata.garden_service==1" ng-true-value="'1'" ng-false-value="'0'"
ng-model="propdata.garden_service">Garden Service <br/>model value:{{propdata.garden_service}}
</label>
</div>
http://plnkr.co/edit/t280UjC3NYNtQt28W1wm?p=preview
Official docs link: https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D

Angularjs ng-required call function

It is possible make the required value dependet of some funcion?
Something like this? I want to do this because I want to change the required attribute to some form inputs...
HTML:
Name: <input type="text" ng-model="user.name" ng-required="isRequired('name')" />
Age: <input type="text" ng-model="user.age" ng-required="isRequired('age')" />
JS:
$scope.isRequired(fieldName){
$scope.requiredFields = [];
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
for (i in requiredFields) {
if (requiredFields[i] == fieldName){
return true;
}
}
return false;
}
Updated Answer:
So based on your updated OP, what you want is certainly doable. The problem with what you were trying to do is that ng-required has no ability to execute a function, it only reads a boolean. But we can dynamically create variables based on data from the server to automatically set fields to required:
Updated Plunker
<form>
Name: <input type="text" ng-model="user.test" ng-required="name" /><br/>
<input type="text" ng-model="user.name" ng-required="age" />
<br/>
<button type="submit">Submit</button>
</form>
Note that I put a $scope property for each input in the ng-required attribute. Now we can dynamically create that $scope property and set it to true if our data says we need to:
$scope.isRequired = function(){
$scope.requiredFields = [];
$http.get('fields.json')
.success(function(data){
$scope.requiredFields = angular.fromJson(data);
console.log($scope.requiredFields.required)
for (i = 0; i < $scope.requiredFields.required.length; i++) {
$scope[$scope.requiredFields.required[i]] = true
}
console.log($scope[$scope.requiredFields.required[0]]);
})
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
}
$scope.isRequired()
So it is iterating over an array of required fields received from the server, and then dynamically creating a $scope property for each one that is required, and setting it to true. Any field that has that $scope property in it's ng-required will be required now. Anything not dynamically created will just return false, and ng-required doesn't trigger.
Original answer:
Plunker
As Pratik mentioned, ng-required only accepts a Boolean value, but we can toggle the value of that with a function.
HTML
<form>
Name: <input type="text" ng-model="user.name" ng-required="isRequired" />
<br/><button ng-click="toggle()">Required: {{isRequired}}</button>
<button type="submit">Submit</button>
</form>
code:
$scope.isRequired = true;
$scope.toggle = function() {
$scope.isRequired = !$scope.isRequired;
}
I know this is a couple of years old and so AngularJS may have changed, but the accepted answer as it stands today isn't correct. You can very easily execute a function within ng-required, as it takes an expression, which can be a function. For example:
index.html
<div ng-controller="ExampleController" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ X ]
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
script.js
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}]);
In script.js, a function addExp is defined and added to the scope, and then it's called in the ng-click directive of the a tag, which also takes an expression as its argument.
This code is taken directly from the AngularJS documentation on expressions. It doesn't use ng-require directly, but any directive that takes an expression will work the same. I have used the same syntax to use a function for ng-require.

How to display validation errors for array form fields in AngularJS?

I created a dynamic form where there are repeated fields submitted as an array. However, i want to validate each field individually and display the error message next to it. If i only have one row, it works fine, but once i add a second row, the first row stops displaying errors.
<form name='user' id='user' novalidate>
<div ng-repeat="bonus in bonuses">
<input name='codes[]' ng-model="bonus.code" lower-than="{{bonus.end_code}}" />
<input name='end_codes[]' ng-model="bonus.end_code" />
<span class="text-error" ng-show="user['codes[]'].$error.lowerThan">
Code must be less than End Code.
</span>
</div>
</form>
AngularJS
var app = angular.module('newBonus', []);
app.controller('NewBonusController', function($scope) {
$scope.bonuses = [];
$scope.addFields = function () {
$scope.bonuses.push({code:'', end_code: ''});
}
$scope.submit = function(){
console.log($scope.bonuses);
}
});
// Validate that one field is less or equal than other.
app.directive('lowerThan', [
function() {
var link = function($scope, $element, $attrs, ctrl) {
var validate = function(viewValue) {
var comparisonModel = $attrs.lowerThan;
if(!viewValue || !comparisonModel){
// It's valid because we have nothing to compare against
ctrl.$setValidity('lowerThan', true);
}
// It's valid if model is lower than the model we're comparing against
ctrl.$setValidity('lowerThan', viewValue <= comparisonModel );
return viewValue;
};
ctrl.$parsers.unshift(validate);
ctrl.$formatters.push(validate);
$attrs.$observe('lowerThan', function(comparisonModel){
return validate(ctrl.$viewValue);
});
};
return {
require: 'ngModel',
link: link
};
}
]);
plunker: http://plnkr.co/edit/Fyqmg2AlQLciAiQn1gxY
I can settle for not having it next to each field, as long as changes to other field sets does trigger the error message properly in which case i can just pop it at the top. The main issue i see is that because they're arrays codes[] which are passed to the form at the end, they will not work properly.
The submit button is disabled properly on form validation, so i'm not sure why the message only locks onto the last row added.
Use a child form to separate the scope.
<ng-form name="frmChild">
<input name='codes' ng-model="bonus.code" lower-than="{{bonus.end_code}}" />
<input name='end_codes' ng-model="bonus.end_code" />
<span class="text-error" ng-show="frmChild.codes.$error.lowerThan">
Code must be less than End Code.
</span>
</ng-form>

AngularJS does not send hidden field value

For a specific use case I have to submit a single form the "old way". Means, I use a form with action="". The response is streamed, so I am not reloading the page. I am completely aware that a typical AngularJS app would not submit a form that way, but so far I have no other choice.
That said, i tried to populate some hidden fields from Angular:
<input type="hidden" name="someData" ng-model="data" /> {{data}}
Please note, the correct value in data is shown.
The form looks like a standard form:
<form id="aaa" name="aaa" action="/reports/aaa.html" method="post">
...
<input type="submit" value="Export" />
</form>
If I hit submit, no value is sent to the server. If I change the input field to type "text" it works as expected. My assumption is the hidden field is not really populated, while the text field actually is shown due two-way-binding.
Any ideas how I can submit a hidden field populated by AngularJS?
You cannot use double binding with hidden field.
The solution is to use brackets :
<input type="hidden" name="someData" value="{{data}}" /> {{data}}
EDIT : See this thread on github : https://github.com/angular/angular.js/pull/2574
EDIT:
Since Angular 1.2, you can use 'ng-value' directive to bind an expression to the value attribute of input. This directive should be used with input radio or checkbox but works well with hidden input.
Here is the solution using ng-value:
<input type="hidden" name="someData" ng-value="data" />
Here is a fiddle using ng-value with an hidden input: http://jsfiddle.net/6SD9N
You can always use a type=text and display:none; since Angular ignores hidden elements. As OP says, normally you wouldn't do this, but this seems like a special case.
<input type="text" name="someData" ng-model="data" style="display: none;"/>
In the controller:
$scope.entityId = $routeParams.entityId;
In the view:
<input type="hidden" name="entityId" ng-model="entity.entityId" ng-init="entity.entityId = entityId" />
I've found a nice solution written by Mike on sapiensworks. It is as simple as using a directive that watches for changes on your model:
.directive('ngUpdateHidden',function() {
return function(scope, el, attr) {
var model = attr['ngModel'];
scope.$watch(model, function(nv) {
el.val(nv);
});
};
})
and then bind your input:
<input type="hidden" name="item.Name" ng-model="item.Name" ng-update-hidden />
But the solution provided by tymeJV could be better as input hidden doesn't fire change event in javascript as yycorman told on this post, so when changing the value through a jQuery plugin will still work.
Edit
I've changed the directive to apply the a new value back to the model when change event is triggered, so it will work as an input text.
.directive('ngUpdateHidden', function () {
return {
restrict: 'AE', //attribute or element
scope: {},
replace: true,
require: 'ngModel',
link: function ($scope, elem, attr, ngModel) {
$scope.$watch(ngModel, function (nv) {
elem.val(nv);
});
elem.change(function () { //bind the change event to hidden input
$scope.$apply(function () {
ngModel.$setViewValue( elem.val());
});
});
}
};
})
so when you trigger $("#yourInputHidden").trigger('change') event with jQuery, it will update the binded model as well.
Found a strange behaviour about this hidden value () and we can't make it to work.
After playing around we found the best way is just defined the value in controller itself after the form scope.
.controller('AddController', [$scope, $http, $state, $stateParams, function($scope, $http, $state, $stateParams) {
$scope.routineForm = {};
$scope.routineForm.hiddenfield1 = "whatever_value_you_pass_on";
$scope.sendData = function {
// JSON http post action to API
}
}])
I achieved this via -
<p style="display:none">{{user.role="store_user"}}</p>
update #tymeJV 's answer
eg:
<div style="display: none">
<input type="text" name='price' ng-model="price" ng-init="price = <%= #product.price.to_s %>" >
</div>
I had facing the same problem,
I really need to send a key from my jsp to java script,
It spend around 4h or more of my day to solve it.
I include this tag on my JavaScript/JSP:
$scope.sucessMessage = function (){
var message = ($scope.messages.sucess).format($scope.portfolio.name,$scope.portfolio.id);
$scope.inforMessage = message;
alert(message);
}
String.prototype.format = function() {
var formatted = this;
for( var arg in arguments ) {
formatted = formatted.replace("{" + arg + "}", arguments[arg]);
}
return formatted;
};
<!-- Messages definition -->
<input type="hidden" name="sucess" ng-init="messages.sucess='<fmt:message key='portfolio.create.sucessMessage' />'" >
<!-- Message showed affter insert -->
<div class="alert alert-info" ng-show="(inforMessage.length > 0)">
{{inforMessage}}
</div>
<!-- properties
portfolio.create.sucessMessage=Portf\u00f3lio {0} criado com sucesso! ID={1}. -->
The result was:
Portfólio 1 criado com sucesso! ID=3.
Best Regards
Just in case someone still struggles with this, I had similar problem when trying to keep track of user session/userid on multipage form
Ive fixed that by adding
.when("/q2/:uid" in the routing:
.when("/q2/:uid", {
templateUrl: "partials/q2.html",
controller: 'formController',
paramExample: uid
})
And added this as a hidden field to pass params between webform pages
<< input type="hidden" required ng-model="formData.userid" ng-init="formData.userid=uid" />
Im new to Angular so not sure its the best possible solution but it seems to work ok for me now
Directly assign the value to model in data-ng-value attribute.
Since Angular interpreter doesn't recognize hidden fields as part of ngModel.
<input type="hidden" name="pfuserid" data-ng-value="newPortfolio.UserId = data.Id"/>
I use a classical javascript to set value to hidden input
$scope.SetPersonValue = function (PersonValue)
{
document.getElementById('TypeOfPerson').value = PersonValue;
if (PersonValue != 'person')
{
document.getElementById('Discount').checked = false;
$scope.isCollapsed = true;
}
else
{
$scope.isCollapsed = false;
}
}
Below Code will work for this IFF it in the same order as its mentionened
make sure you order is type then name, ng-model ng-init, value. thats It.
Here I would like to share my working code :
<input type="text" name="someData" ng-model="data" ng-init="data=2" style="display: none;"/>
OR
<input type="hidden" name="someData" ng-model="data" ng-init="data=2"/>
OR
<input type="hidden" name="someData" ng-init="data=2"/>

Trying to update one input's value to another's using angularjs

I'm fairly new using AngularJS but I've been using for a pet project and I've run in to an issue. What I basically want to do is take the text input from this input field:
<form id="ci_search_form">
<p class="input-append"><label>Search:</label> <input type="text" id="query" ng:model="query" autofocus> <button ng:click="clearSearch()" class="btn"><i class="icon-remove"></i></button></p>
</form>
and update this input field's value with that value:
<div><input type="text" id="ciquery" ng:model="ciquery.Name"></div>
The second input filters some data and I can type in that directly and it works. However this page will have different sets of data, each with their own search input that I want updated by the master input at the top. I can't set value="" or use jQuery to set the value either, it just appears blank unless I explicitly type in that second input field. Can anyone assist with a solution?
EDIT
I thought I should include my app and controller code:
var App = angular.module('TicketAssistApp', []);
App.controller('SearchController', function($scope, $http, $filter){
$scope.query = '';
$http.get('static/ci_list.json').success(function(data){
$scope.ci_list = data;
});
$scope.clearSearch = function(){
$scope.query = '';
}
});
EDIT 2
Made some progress. Created a function that can be called an update ciquery in $scope:
var App = angular.module('TicketAssistApp', []);
App.controller('SearchController', function($scope, $http, $filter){
$scope.query = '';
$scope.ciquery = '';
$http.get('static/ci_list.json').success(function(data){
$scope.ci_list = data;
});
$scope.queryUpdate = function(){
$scope.ciquery = $scope.query;
}
$scope.clearSearch = function(){
$scope.query = '';
$scope.queryUpdate();
}
});
This works great. However, this creates another issue. Before in ciquery I was using ciquery.Name to filter only on the Name attribute. With this new solution I had to change it to this:
<div><input type="hidden" id="ciquery" ng:model="ciquery"></div>
This searches all fields in my data which returns unwanted results. Suggestions?
$scope and ng-model are differents. You should give ng-model's property to ng-click's function. Looks at this -> Ng-model does not update controller value
To update second input's field (here an example -> http://jsfiddle.net/yEvSL/1/)
<div><input type="text" id="ciquery" ng:model="query"></div>

Categories