Reputation: 1345
I have a input element which I am trying to name dynamically, but I had no luck so far using angular js.
neither
<input type="text" name="resource.Name" ng-model="resource.Value" ng-required="resource.IsRequired">
nor
<input type="text" name="{{resource.Name}}" ng-model="resource.Value" ng-required="resource.IsRequired">
works for me. When I try to access name attribute it always comes back as resource.Name instead of the value it contains. The main reason I am trying to name this input control is validation, if user does not enter text in the field, I would like to tell them which textbox is required. If there is any angular directive I can use for that purpose I am ready to use them as well.
Can anyone tell me how to achieve this.
My validation function is as follows:
window.resourcesValidation = function ($scope, $rootScope, $alert) {
var subscriptions = $scope.scopeData.Subscriptions;
var isValidated = true;
if ($scope.resourceDataForm && $scope.resourceDataForm.$error && $scope.resourceDataForm.$error.required) {
var missingFields = [];
angular.forEach($scope.resourceDataForm.$error.required, function (value, key) {
isValidated = false;
missingFields.push("-" + value.$name);
});
$alert.error("Please fill in the all required fields.\r\n" + missingFields.join("\r\n"));
}
if (isValidated)
$scope.saveChanges(subscriptions);
return isValidated;
};
Upvotes: 3
Views: 6562
Reputation: 15912
I could not find the answer that satisfied some or all of these needs. This is what I came up with.
There may be a better way, so please share your thoughts.
I am using Angularjs 1.3.0-beta.8
I have a form with multi-nested directives that all contain input(s), select(s), etc... These elements are all enclosed in ng-repeats, and dynamic string values.
This is how to use the directive:
<form name="myFormName">
<nested directives of many levels>
ex: <input ng-repeat=(index, variable) in variables" type="text"
my-name="{{ variable.name + '/' + 'myFormName' }}"
ng-model="variable.name" required />
ex: <select ng-model="variable.name" ng-options="label in label in {{ variable.options }}"
my-name="{{ variable.name + '/' + 'myFormName' }}"
</select>
</form>
Note: you can add and index to the string concatenation if you need to serialize perhaps a table of inputs; which is what I did. However, dynamic name inputs means you may not know the name of the form input, so how would you call $scope.formName.??????. You could iterate of the $scope.formName object to get keys that match a certain value. That means string concatenation like this:
my-name="{{ dynamicString + hello + '/' + 'myFormName' }}"
Then in $scope.myFormName you would find any form input name by just iterating over the object and gathering any keys that included 'hello'.
app.directive('rsName', function(){
var rsNameError = "rsName directive error: "
return {
restrict:'A', // Declares an Attributes Directive.
require: 'ngModel', // ngModelController.
link: function( scope, elem, attrs, ngModel ){
if( !ngModel ){ return } // no ngModel exists for this element
// check rsName input for proper formatting ex. something/something
checkInputFormat(attrs);
var inputName = attrs.rsName.match('^\\w+').pop(); // match upto '/'
assignInputNameToInputModel(inputName, ngModel);
var formName = attrs.rsName.match('\\w+$').pop(); // match after '/'
findForm(formName, ngModel, scope);
} // end link
} // end return
function checkInputFormat(attrs){
if( !/\w\/\w/.test(attrs.rsName )){
throw rsNameError + "Formatting should be \"inputName/formName\" but is " + attrs.rsName
}
}
function assignInputNameToInputModel(inputName, ngModel){
ngModel.$name = inputName
}
function addInputNameToForm(formName, ngModel, scope){
scope[formName][ngModel.$name] = ngModel; return
}
function findForm(formName, ngModel, scope){
if( !scope ){ // ran out of scope before finding scope[formName]
throw rsNameError + "<Form> element named " + formName + " could not be found."
}
if( formName in scope){ // found scope[formName]
addInputNameToForm(formName, ngModel, scope)
return
}
findForm(formName, ngModel, scope.$parent) // recursively search through $parent scopes
}
});
This should handle many situations where you just don't know where the form will be. Or perhaps you have nested forms, but for some reason you want to attach this input name to two forms up? Well, just pass in the form name you want to attach the input name to.
What I wanted, was a way to assign dynamic values to inputs that I will never know, and then just call $scope.myFormName.$valid.
This may be an overkill, and a better solution exists in 1.3+. I couldn't find it in the time I had. This works for me now.
Good luck! Hope this helps someone!!!!
Upvotes: 0
Reputation: 5290
Try this:
<input type="text" ng-attr-name="{{resource.Name}}" ng-model="resource.Value" ng-required="resource.IsRequired">
From the docs:
If an attribute with a binding is prefixed with the ngAttr prefix (denormalized as ng-attr-) then during the binding will be applied to the corresponding unprefixed attribute.
Although name="{{resource.Name}}"
should work in this case as well.
I found a solution here: Dynamic validation and name in a form with AngularJS
Here's a plunker that I made.
Sounds like this issue may be fixed in Angular 1.3.
Upvotes: 2
Reputation: 48211
Quoting another answer on a very similar issue:
This happens because the control's name (the one with which it is registered on its parent form) is retrieved during the
ngModelController
's instantiation, which according to the docs takes place before the pre-linking phase* (so no interpolation yet).
In other words, validation relies on forms.$error
object, which is bound to the controls that are registered with that form.
A control's ngModelController
is responsible for registering a control with its parent form and it is instantiated at the pre-linking phase (when the name-expression is not interpolated yet).
This can be solved by a custom directive that manually registers the control with its parent form, but only after it's actual name is determined (after insterpolating the name-expression).
You can find the whole answer here.
Look for the UPDATE 2 section for a working solution.
(This is the link to the working demo.)
Upvotes: 1
Reputation: 1039
<input ng-model="resource.model" name="{{resource.name}}" ng-required="{{resource.required}}"/>
seems to be working for me. Please see this plnkr .
Upvotes: 0
Reputation: 19772
Can you show the rest of your code? (HTML, relevant controllers, etc.)
Sometimes the issue can center around asynchrony. For example, the HTML might be compiled by Angular's compiler before your $scope.resource
object gets its data (perhaps, with an AJAX call).
Two-way data binding sometimes doesn't always work how you would expect. Therefore, after "putting" your data into $scope.resource
with whatever function, you might need to call $scope.$apply()
.
Upvotes: 0