I have a project where I'm currently trying to refactor an old system that was hinged on jquery from the ground up with angular 1.x. However, there are a lot of old HTML forms that I'd like to reuse the bulk of so I don't want to recreate them. I'd love it if there was a way to keep it purely angular, but I'm honestly at a loss of how I'd do that (or whether or not I can). I'm fairly new to angular so there are a lot of inner workings to it that I'm still not privy to.
I've searched around on google and other places including here and I can't really even find other people talking about it. That tells me that either I'm searching badly or it's something that I should probably not be working towards.
All the html pages have identically id'd fields so I feel I can reliably base things on that. For example: all forms with first name text boxes have an id of "cl_fname".
Is there anyway that I can accomplish: getting the form, adding an ng-model="cl_fname" or something to the relevant tag and then display the form? I've gotten to the point where I can get the html page, hold it in the scope and then display using ng-bind-html, but figuring out how to add angular attributes to specific elements I can't figure out.
You can achieve this with jQuery and the attr() method.
I created a plunker here that demonstrates adding angular to an existing "plain" html form.
In the example, I'm using id selectors, but you could use any combination of selectors to ensure you get the right elements.
The below is a quick code snippet from my Plunker example:
HTML:
<div ng-app="myApp">
<form id='myForm1' data-test="test2">
<span>First Name:</span>
<input type="text" id="myForm1_firstName" />
<input type="submit" id="myForm1_Submit" value="Go!" />
</form>
</div>
JS:
// set up angular
var myApp = angular.module('myApp', []);
myApp.controller('MyForm1Controller', ['$scope', function($scope) {
$scope.firstName = 'Angular Working!';
}]);
// use jQuery to add the relevent attributes to our form
var jqMyForm1 = $('form#myForm1');
var jqTxtFirstName = jqMyForm1.find('input[type="text"]#myForm1_firstName');
//add controller to form
jqMyForm1.attr('ng-controller', 'MyForm1Controller');
//bind the textbox to the angular 'firstName' variable
jqTxtFirstName.attr('ng-model', "firstName");
EDIT:
just realised you want to load the html form dynamically.
Version 2 of the plunker (here) will now dynamically load a HTML form from an external resource (separate html page), inject it into the current page, add the angular bindings to it, and then get angular to recognise it.
The key to getting angular to recognise the form is the use of the $compile object (angular $compile documentation).
Again, quick snippets of the code in use:
HTML (main page):
<div ng-app="myApp" ng-controller="LoadingController"></div>
HTML (myForm1.html):
<form id='myForm1' data-test="test2">
<span>First Name:</span>
<input type="text" id="myForm1_firstName" />
<input type="submit" id="myForm1_Submit" value="Go!" />
</form>
JS:
// set up angular
var myApp = angular.module('myApp', []);
// main controller for loading the dynamic form
myApp.controller('LoadingController', ['$scope','$http','$compile', function($scope,$http,$compile) {
$scope.loadHtmlForm = function(formURL) {
$http.get(formURL).then(function successCallback(response){
var jqForm = $(response.data);
var jqTxtFirstName = jqForm.find('input[type="text"]#myForm1_firstName');
//add controller to form
jqForm.attr('ng-controller', 'MyForm1Controller');
//bind the textbox to the angular 'firstName' variable
jqTxtFirstName.attr('ng-model', "firstName");
$('div').append(jqForm);
$compile(jqForm[0])($scope);
});
}
$scope.loadHtmlForm('myForm1.html');
}]);
// form controller for managing the data
myApp.controller('MyForm1Controller', ['$scope', function($scope) {
$scope.firstName = 'Angular Working!';
}]);
There must be a simple answer to this, but I'm not getting it. I'm using the following input element to filter the results in a table through AngularJS. If I manually type "foo" into the input field, the table will filter for "foo". But my hard-coded value of "foo" (in the input's value attribute) will neither show up in the browser nor filter the results upon page load. How can I make this input field provide both a default filter and a user-defined filter? Thanks in advance.
<input ng-model="search" id="search" type="text" placeholder="Search" value="foo">
<div ng-controller="eventsController")>
<table>
<tr ng-repeat="event in events | filter:search">
<td><span ng-bind="event.title"></span></td>
<td><span ng-bind="event.date_start"></span></td>
</tr>
</table>
</div>
<script>
function EventsController($scope, $http) {
$http.get('/api/all-events').success(function(events) {
$scope.events = events;
});
}
</script>
If you are using ng-model then you shouldn't set your value in html. Instead you should be setting you model value in Angular. So get rid of you value="foo" and do this.
function EventsController($scope, $http) {
$http.get('/api/all-events').success(function(events) {
$scope.events = events;
$scope.search = "foo"; //This will set the models search to foo
});
}
Edited, initial response is an option but not recommended.
According to the documentation for ng-init
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.
The link is a working example of the "proper" way to go about it.
http://plnkr.co/edit/EyV1R4WUyu2TEMTue3jT
I'm using a form to add elements to list that is displayed on the side of the form.
Markup is:
<form name="thingForm">
<input required type="text" ng-model="thing.name"/>
<input required type="text" ng-model="thing.value"/>
<input type="submit" ng-click="addThing(thing)"/>
</form>
<ul>
<li ng-repeat="thing in things">{{thing.name}} with value of {{thing.value}}</li>
</ul>
And in a controller I have:
$scope.things = [];
$scope.addThing = function(thing) {
$scope.things.push(thing);
$scope.thing = {};
};
Working jsfiddle: http://jsfiddle.net/cXU2H/1/
Now as you can see, I can empty the form by emptying the model, however since the inputs have the required tag the browser still displays an error message (at least Chrome does).
I looked at the similar questions and:
I've also looked at this answer: https://stackoverflow.com/a/16296941/545925 however the jsfiddle behaves exactly the same as in my example: after the input is cleared it still has an ng-invalid-required class remaining (and it also triggers a HTML5 error message)
since I'm not on the 1.1.x branch $setPristine() is not available for me $setPristine() behaves the same way
I can of course write a function that iterates through the elements of a form and removes every ng-invalid-required and ng-invalid class, but that is not the way I would like to solve this. That is what I would do with jQuery.
Are you using $setPristine right? You can easily see in your fiddle that if you add it, it works. http://jsfiddle.net/X6brs/
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.things = [];
$scope.addThing = function(thing) {
$scope.things.push(thing);
$scope.thing = {};
$scope.thingForm.$setPristine(true);
};
}
$scope.thingForm.$setPristine();
$scope.thingForm.$setUntouched();
will do the trick.
The two way binding in AngularJs is great in updating the view anytime the model changes. I was wondering if there was some way to pass the model to a function defined in the controller before being displayed. And not with a button click but live.
So for example, the p element would be updated automatically
<input data-ng-model='myModel'>
<p>{{myModel}}</p>
Is there any way to do the following?
<div data-ng-controller='myController'>
<input data-ng-model='myModel'>
<p>{{increment(myModel)}}</p>
</div>
where increment is a function defined in myController
Most definitely you can. Just define the function in the same controller. For instance
Controller:
app.controller('myCtrl', function($scope) {
$scope.increment = function() {
return $scope.myModel;
}
});
HTML
<div data-ng-controller='myController'>
<input data-ng-model='myModel'>
<p>{{increment()}}</p>
</div>
That returns the exact same thing as {{myModel}}
Using AngularJS if I set a simple input text box value to something like "bob" below. The value does not display if the ng-model attribute is added.
<input type="text"
id="rootFolder"
ng-model="rootFolders"
disabled="disabled"
value="Bob"
size="40"/>
Anyone know of a simple work around to default this input to something and keep the ng-model? I tried to use a ng-bind with the default value but that seems not to work either.
That's desired behavior, you should define the model in the controller, not in the view.
<div ng-controller="Main">
<input type="text" ng-model="rootFolders">
</div>
function Main($scope) {
$scope.rootFolders = 'bob';
}
Vojta described the "Angular way", but if you really need to make this work, #urbanek recently posted a workaround using ng-init:
<input type="text" ng-model="rootFolders" ng-init="rootFolders='Bob'" value="Bob">
https://groups.google.com/d/msg/angular/Hn3eztNHFXw/wk3HyOl9fhcJ
Overriding the input directive does seem to do the job. I made some minor alterations to Dan Hunsaker's code:
Added a check for ngModel before trying to use $parse().assign() on fields without a ngModel attributes.
Corrected the assign() function param order.
app.directive('input', function ($parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, element, attrs) {
if (attrs.ngModel && attrs.value) {
$parse(attrs.ngModel).assign(scope, attrs.value);
}
}
};
});
The Angular way
The correct Angular way to do this is to write a single page app, AJAX in the form template, then populate it dynamically from the model. The model is not populated from the form by default because the model is the single source of truth. Instead Angular will go the other way and try to populate the form from the model.
If however, you don't have time to start over from scratch
If you have an app written, this might involve some fairly hefty architectural changes. If you're trying to use Angular to enhance an existing form, rather than constructing an entire single page app from scratch, you can pull the value from the form and store it in the scope at link time using a directive. Angular will then bind the value in the scope back to the form and keep it in sync.
Using a directive
You can use a relatively simple directive to pull the value from the form and load it in to the current scope. Here I've defined an initFromForm directive.
var myApp = angular.module("myApp", ['initFromForm']);
angular.module('initFromForm', [])
.directive("initFromForm", function ($parse) {
return {
link: function (scope, element, attrs) {
var attr = attrs.initFromForm || attrs.ngModel || element.attrs('name'),
val = attrs.value;
if (attrs.type === "number") {val = parseInt(val)}
$parse(attr).assign(scope, val);
}
};
});
You can see I've defined a couple of fallbacks to get a model name. You can use this directive in conjunction with the ngModel directive, or bind to something other than $scope if you prefer.
Use it like this:
<input name="test" ng-model="toaster.test" value="hello" init-from-form />
{{toaster.test}}
Note this will also work with textareas, and select dropdowns.
<textarea name="test" ng-model="toaster.test" init-from-form>hello</textarea>
{{toaster.test}}
Update: My original answer involved having the controller contain DOM-aware code, which breaks Angular conventions in favor of HTML. #dmackerman mentioned directives in a comment on my answer, and I completely missed that until just now. With that input, here's the right way to do this without breaking Angular or HTML conventions:
There's also a way to get both - grab the value of the element and use that to update the model in a directive:
<div ng-controller="Main">
<input type="text" id="rootFolder" ng-model="rootFolders" disabled="disabled" value="Bob" size="40" />
</div>
and then:
app.directive('input', ['$parse', function ($parse) {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, element, attrs) {
if(attrs.value) {
$parse(attrs.ngModel).assign(scope, attrs.value);
}
}
};
}]);
You can of course modify the above directive to do more with the value attribute before setting the model to its value, including using $parse(attrs.value, scope) to treat the value attribute as an Angular expression (though I'd probably use a different [custom] attribute for that, personally, so the standard HTML attributes are consistently treated as constants).
Also, there is a similar question over at Making data templated in available to ng-model which may also be of interest.
If you use AngularJs ngModel directive, remember that the value of value attribute does not bind on ngModel field.You have to init it by yourself and the best way to do it,is
<input type="text"
id="rootFolder"
ng-init="rootFolders = 'Bob'"
ng-model="rootFolders"
disabled="disabled"
value="Bob"
size="40"/>
This is a slight modification to the earlier answers...
There is no need for $parse
angular.directive('input', [function () {
'use strict';
var directiveDefinitionObject = {
restrict: 'E',
require: '?ngModel',
link: function postLink(scope, iElement, iAttrs, ngModelController) {
if (iAttrs.value && ngModelController) {
ngModelController.$setViewValue(iAttrs.value);
}
}
};
return directiveDefinitionObject;
}]);
Hi you can try below methods with initialize of model.
Here you can initialize ng-model of textbox two way
- With use of ng-init
- With use of $scope in js
<!doctype html>
<html >
<head>
<title>Angular js initalize with ng-init and scope</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
</head>
<body ng-app="app" >
<h3>Initialize value with ng-init</h3>
<!-- Initlialize model values with ng-init -->
<div ng-init="user={fullname:'Bhaskar Bhatt',email:'bhatt.bhaskar88#gmail.com',address:'Ahmedabad'};">
Name : <input type="text" ng-model="user.fullname" /><br/>
Email : <input type="text" ng-model="user.email" /><br/>
Address:<input type="text" ng-model="user.address" /><br/>
</div>
<!-- initialize with js controller scope -->
<h3>Initialize with js controller</h3>
<div ng-controller="alpha">
Age:<input type="text" name="age" ng-model="user.age" /><br/>
Experience : <input type="text" name="experience" ng-model="user.exp" /><br/>
Skills : <input type="text" name="skills" ng-model="user.skills" /><br/>
</div>
</body>
<script type="text/javascript">
angular.module("app",[])
.controller("alpha",function($scope){
$scope.user={};
$scope.user.age=27;
$scope.user.exp="4+ years";
$scope.user.skills="Php,javascript,Jquery,Ajax,Mysql";
});
</script>
</html>
The issue is that you have to set the ng-model to the parent element to where you want to set the ng-value/value .
As mentioned by Angular:
It is mainly used on input[radio] and option elements, so that when the element is selected, the ngModel of that element (or its select parent element) is set to the bound value.
Eg:This is an executed code :
<div class="col-xs-12 select-checkbox" >
<label style="width: 18em;" ng-model="vm.settingsObj.MarketPeers">
<input name="radioClick" type="radio" ng-click="vm.setPeerGrp('market');"
ng-value="vm.settingsObj.MarketPeers"
style="position:absolute;margin-left: 9px;">
<div style="margin-left: 35px;color: #717171e8;border-bottom: 0.5px solid #e2e2e2;padding-bottom: 2%;">Hello World</div>
</label>
</div>
Note: In this above case I alreday had the JSON response to the ng-model and the value, I am just adding another property to the JS object as "MarketPeers". So the model and value may depend according to the need, but I think this process will help, to have both ng-model and value but not having them on the same element.
I had similar issue. I was not able to use value="something" to display and edit.
I had to use the below command inside my <input>along withe ng model being declared.
[(ngModel)]=userDataToPass.pinCode
Where I have the list of data in the object userDataToPass and the item that I need to display and edit is pinCode.
For the same , I referred to this YouTube video