What solution would you implement for an Angular "form", where you wanted it to submit on an enter key press, but you can't rearrange the elements to fit in a <form> element. For instance something like this:
<div>
<input/>
<input/>
//This might act as a kind of custom built select
<ul>
<li></li>
</ul>
<form></form> //Dropzone form for instance.
<button type="submit" ng-click="submitFunction()">Submit</button>
</div>
I want the user to be able to hit enter while the focus is on any of these elements(Not just the true form elements), and have the form submit(validation passing of course). The solution should be purely Angualr(if possible). I'd rather it didn't use complex CSS positioning, I don't want to have to rearrange and muddle the html structure.
I saw a solution online for a custom "ngEnter" type directive that I thought had promise but I don't think everyone on the team is gonna like that solution. I'd like to know how else one might approach this.
See this article for more info.
Here's a snippet showing how to conditionally log to the console using ng-keypress:
angular.module('myExample', [])
.controller('myController', ['$scope', function($scope) {
$scope.myFunction = function() {
console.log('hi!');
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myExample">
<div ng-controller="myController">
<input placeholder="type something" ng-keypress="$event.which === 13 && myFunction()">
</div>
</div>
Related
hey this is an edited version of my first post:
Pretty much what I want to do is have a text input box that stores its contents in a javascript variable. The catch is I want it to happen live, so the variable will update automatically say, ever .2 seconds without the user needing to press a submit button. Thanks
Are you looking for the two way data binding? Something like this: https://codepen.io/manishiitg/embed/ZYOmbB?
<html>
<head>
<script src="https://code.angularjs.org/1.3.8/angular.min.js"></script>
<script>
var mainMod = angular.module('MainApp', []);
mainMod.controller('MainCtrl', function ($scope) {
$scope.text = '';
});
</script>
</head>
<body ng-app='MainApp'>
<div ng-controller='MainCtrl'>
<div>
Change Text Here:
<input type='text' ng-model='text' />
</div>
<div>
<p>Text: {{text}}</p>
</div>
</div>
</body>
<html>
You can achieve that with Angular, React and Vue.js framework, even with jQuery. Perhaps you should try one of the frameworks that solves your needs quickly.
If you choose Angular, I recommend you to make the tutorial steps, specially this part: https://angular.io/tutorial/toh-pt1#two-way-binding
JSFiddle here: http://jsfiddle.net/c6tzj6Lf/4/
I am dynamically creating forms and buttons and want to disable the buttons if the required form inputs are not completed.
HTML:
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
JavaScript:
angular.module('choicesApp', ['ngSanitize'])
.controller('ChoicesCtrl', ['$scope', '$sce', function($scope, $sce) {
$scope.custom = "Required Input: <input required type='text'>";
$scope.trustCustom = function() {
return $sce.trustAsHtml($scope.custom);
};
$scope.buttons = [
{text:'Submit 1'},
{text:'Submit 2'}];
}]);
choicesForm.$invalid is false and does not change when entering text into the input field.
Solution:
I ended up using the angular-bind-html-compile directive from here: https://github.com/incuna/angular-bind-html-compile
Here is the relevant bit of working code:
<ng-form name="choicesForm">
<div ng-if="choices" bind-html-compile="choices"></div>
<button ng-click="submitForm()" ng-disabled="choicesForm.$invalid">
Submit
</button>
</ng-form>
And choices might be a snippit of HTML like this:
<div><strong>What is your sex?</strong></div>
<div>
<input type="radio" name="gender" ng-model="gender" value="female" required>
<label for="female"> Female</label><br>
<input type="radio" name="gender" ng-model="gender" value="male" required>
<label for="male"> Male</label>
</div>
The main problem is that ngBindHtml doesn't compile the html - it inserts the html as it is. You can even inspect the dynamic input and see that it doesn't have the ngModel's CSS classes (ng-pristine, ng-untouched, etc) which is a major red flag.
In your case, the form simply doesn't know that you've added another input or anything has changed for that matter. Its state ($pristine, $valid, etc) isn't determined by its HTML but by the registered NgModelControllers. These controllers are added automatically when an ngModel is linked.
For example this <input required type='text'> won't affect the form's validity, even if it's required, since it doesn't have ngModel assigned to it.
But this <div ng-model="myDiv" required></div> will affect it since it's required and has ngModel assigned to it.
The ngDisabled directive on your buttons works as expected since it depends on the form's $invalid property.
See this fiddle which showcases how ngModel registers its controller. Note that the html containing the dynamic input gets compiled after 750ms just to show how NgModelControllers can be added after FormController has been instantiated.
There are a few solutions in your case:
use a custom directive to bind and compile html - like this one
use ngInclude which does compile the html
use $compile to compile the newly added HTML but this is a bit tricky as you won't know exactly when to perform this action
This is an answer yet imcomplete because i cannot do the code at the moment.
I think your html will be included, not compiled. So the inputs are not bind to angular and are not part of the angular form object.
The only way i see is to use a directive that will compile the passed html and add it to your form. This may be quite tricky though, if you want to go on this way i suggest to edit your question to ask for the said directive.
However i'm not really familiar with $compile so i don't know if it'll work to just add $compile around $sce.trustAsHtml()
You can write a method as ng-disabled does not work with booleans, it works with 'checked' string instead:
So on your controller place a method :
$scope.buttonDisabled = function(invalid){
return invalid ? "checked" : "";
};
And on your view use it on angular expression :
<button ng-repeat="button in buttons" ng-disabled="buttonDisabled(choicesForm.$invalid)">
Here is a working fiddle
Working DEMO
This is the solution you are looking for. You need a custom directive. In my example I have used a directive named compile-template and incorporated it in div element.
<div ng-bind-html="trustCustom()" compile-template></div>
Directive Code:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
I found the directive in this fiddle.
I believe what is really happening though due to jsfiddle I'm unable to dissect the actual scopes being created here.
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
The first div is your top level scope, your form is the first child scope. Adding the div using a function creates the dynamically added input field as a child of the first child, a grandchild of the top level scope. Therefore your form is not aware of the elements you're adding dynamically causing only the static field to be required for valid form entry.
A better solution would be to use ng-inclue for additional form fields or if your form isn't to large then simply put them on the page or template you're using.
I got this example from the W3Schools tutorial on AngularJS. I made a small change from binding the value of the checkbox span to using an expression. I figured that the todo list wouldn't update any more. But it still does. What causes the ng-repeat to fire just because I have added a todo item?
http://plnkr.co/edit/Kojz2ODWDS8dFDNzjYR5?p=preview
<!DOCTYPE html>
<html>
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<body ng-app="myApp" ng-controller="todoCtrl">
<h2>My Todo List</h2>
<form ng-submit="todoAdd()">
<input type="text" ng-model="todoInput" size="50" placeholder="Add New">
<input type="submit" value="Add New">
</form>
<br>
<div ng-repeat="x in todoList">
<input type="checkbox" ng-model="x.done"> <span>{{x.todoText}}</span>
</div>
<p><button ng-click="remove()">Remove marked</button></p>
<script>
var app = angular.module('myApp', []);
app.controller('todoCtrl', function($scope) {
$scope.todoList = [{todoText:'Clean House', done:false}];
$scope.todoAdd = function() {
$scope.todoList.push({todoText:$scope.todoInput, done:false});
$scope.todoInput = "";
};
$scope.remove = function() {
var oldList = $scope.todoList;
$scope.todoList = [];
angular.forEach(oldList, function(x) {
if (!x.done) $scope.todoList.push(x);
});
};
});
</script>
</body>
</html>
Clicking the Add New Button submits the corresponding form and by using ng-submit="todoAdd()" it will call this function. This in turn adds an entry to the todoList in your scope. As this array has been modified the angular digest cycle is triggered and the list is updated.
Some suggestions for your questions: First of all, you mean W3Schools, not the W3C (which is a standardization organization and normally is not doing tutorials, which is why I got curios - Also, you will find lots of reasons why not to use W3Schools when goolgin around or looking at meta). Also, if you compare to some other code, you should include it or at least link to it.
I found it by googling and it seems your only change is using <span>{{x.todoText}}</span> instead of <span ng-bind="x.todoText"></span>. There really is no difference in terms of the digest cycle here. The only difference is that by using {{}} it might at first be rendered as curly brackets in the browser window, before the variable is actually replaced. Thus, it is usually better to use ng-bind.
Let say I have the following short input form:
<form ng-controller="parentController" action="testing.php" method="post">
<input name="parentInputField" ng-model="x">
<div ng-controller="childController">
<div ng-repeat="item in itemList">
<input name="childInputField[]" ng-model="x">
</div>
</div>
</form>
As you may already know, the model x in childController will follow the value of of model x in parentController until the user type in something in the childController input field, so the user can simply change the value in parent for bulk input and then fine tune in child.
Now, after the user have submitted the form, I want to call the same form for editing function. To keep the bulk input function on new items, is there a way I can ng-init model x in childController only when there is a previous value?
Haven't tried but I believe you can achieve with:
<div ng-init="ctrl.value && (ctrl.value=1)">
BUT if you want an advice, avoid both ng-init and nesting controllers like this: it would be a pain to maintain this program. Prefer to use controllerAs syntax (https://docs.angularjs.org/api/ng/directive/ngController) and put init logic on controller/service.
I have a dilemma about what is the best (and correct) approach if I want to disable form controls (or at least make them unavailable for user interaction) during a period of time when user clicks sort of "Save" or "Submit" button and data travelling over the wire. I don't want to use JQuery (which is evil!!!) and query all elements as array (by class or attribute marker)
The ideas I had so far are:
Mark all elements with cm-form-control custom directive which will subscribe for 2 notifications: "data-sent" and "data-processed". Then custom code is responsible for pushing second notification or resolve a promise.
Use promiseTracker that (unfortunatelly!) enforces to produce extremely stupid code like ng-show="loadingTracker.active()". Obviously not all elements have ng-disabled and I don't want to user ng-hide/show to avoid "dancing" buttons.
Bite a bullet and still use JQuery
Does any one have a better idea?
UPDATED:
The fieldset idea DOES work. Here is a simple fiddle for those who still want to do the same http://jsfiddle.net/YoMan78/pnQFQ/13/
HTML:
<div ng-app="myApp">
<ng-form ng-controller="myCtrl">
Saving: {{isSaving}}
<fieldset ng-disabled="isSaving">
<input type="text" ng-model="btnVal"/>
<input type="button" ng-model="btnVal" value="{{btnVal}}"/>
<button ng-click="save()">Save Me Maybe</button>
</fieldset>
</ng-form>
</div>
and JS:
var angModule = angular.module("myApp", []);
angModule.controller("myCtrl", function ($scope, $filter, $window, $timeout) {
$scope.isSaving = undefined;
$scope.btnVal = 'Yes';
$scope.save = function()
{
$scope.isSaving = true;
$timeout( function()
{
$scope.isSaving = false;
alert( 'done');
}, 10000);
};
});
Wrap all your fields in fieldset and use ngDisabled directive like this:
<fieldset ng-disabled="isSaving"> ... inputs ...</fieldset>
It will automatically disable all inputs inside the fieldset.
Then in controller set $scope.isSaving to true before http call and to false after.
There is an simple solution in modern browsers:
define a css class
.disabled {
pointer-events: none;
... ...
}
add this class to ng-form
<ng-form data-ng-class="{ 'disabled': isSaving }"> ... inputs ... </ng-form>
Here is the pointer-events support chart.
Note: even if you set pointer-events: none, you can still tab to input element with your keyboard.