Angular nested ng-form causes $modelValue to not be set - javascript

I'm having a little issue with an Angular project. Firstly the $digest value in a form wasn't set to true, in certain conditions. I fixed it via this post.
So in order to fix that problem I had to put a ng-form around some parts in my form. Making it look like the following:
<div class="fim-sub-input">
<ng-form name="gender" novalidate>
<label class="fim-radio" fim-radio ng-repeat="gender in genders">
<input type="radio" name="gender" value="{{ gender.id }}" ng-model="card.gender">
<span class="fim-sub-label">{{ gender.name }}</span>
</label>
</ng-form>
</div>
This fixed my $digest problem. However now in the form, the gender property never has a $modelValue property? All other form properties still do, except the gender property, which is in a nested form doesn't have it. Is this expected behaviour?
I currently fixed it by setting the form.gender.$modelValue to the value which is in the scope's model, before I process the form. It works, but it's ugly :)
Any ideas?

not sure what you are trying, but ng-model should be gender.gender instead of card.gender.
You might still eventually get some issues since you are using ng-repeat which creates a child scope (unless you use ng-model = $parent.gender.gender)

Related

To disable all radio buttons if anyone of them is clicked in angularjs ng-repeat

what i am trying to do here is, i have an ng-repeat in a form and if i click anyone of those input buttons corresponding all buttons get disabled
<div ng-repeat="question in sinSurCtrl.singleSurvey" ng-show="!$first">
<hr>
<p>Q. {{question.questionText}}</p>
<form >
<div ng-repeat=" option in question.questionOptions track by $index">
<label>
<input name="options" type="radio" value="$index" ng-click="sinSurCtrl.questionId=question.questionId; sinSurCtrl.answer=$index+1; sinSurCtrl.createAnswer()" onclick="this.disabled = true">
<span> {{option}} {{$index+1}} {{question.questionId}} </span>
</label>
</div>
</form>
</div>
here is the view-
as you can see if i select anyone of those option it is getting disabled but what i am trying to do is if i attempt anyone option then options corresponding to the same question get disabled.
for example-
in Q3. which is a better orator ?
if i choose option (a) then it get selected and after that automatically bot options (a) and (b) get disabled.
Note- please try to keep solution completely in angularjs or if you want use affordable amount of javascript other then that please avoid using external libraries like jQuery(i know nobody in his senses will handle trouble of using jQuery and angular together but for the sake of example i have included its name)
Proposed solution with some suggested refactoring...
First change the ng-click directive to point to a new onOptionButtonClicked function on sinSurCtrl which takes in the two parameters question and index (which it needs to carry out it's work):
<div ng-repeat="question in sinSurCtrl.singleSurvey" ng-show="!$first">
<hr>
<p>Q. {{question.questionText}}</p>
<form>
<div ng-repeat="option in question.questionOptions track by $index">
<label>
<input
name="options"
type="radio"
value="$index"
ng-click="onOptionButtonClicked(question, $index)"
ng-disabled="question.disabled">
<span> {{option}} {{$index+1}} {{question.questionId}} </span>
</label>
</div>
</form>
</div>
Also take note of the newly added ng-disabled="question.disabled" directive. This is part of the mechanism that will enable/disable the question's controls.
Now move the variable assignments to the new onOptionButtonClicked function. The controller is generally a better place (than the view) for variable assignments, especially if there are several of them on the same directive.
sinSurCtrl.onOptionButtonClicked = onOptionButtonClicked;
function onOptionButtonClicked(question, index){
sinSurCtrl.questionId=question.questionId;
sinSurCtrl.answer=index;
sinSurCtrl.createAnswer();
question.disabled = true; // <--- This is what disables the option buttons
}
This is where an answered question object gets it's disabled property set to true. This in combination with the ng-disabled directive mentioned previously is what disables the option buttons.
On your controller, create a function that checks if a given questionId has been answered and return truthy if it has. Call that function in ng-disabled in the input tag:
<input type="radio" ng-disabled="sinSurCtrl.questionHasAnswer(question.questionId)" ... />

Angular1: How to reflect ng-model errors on another element?

I have a directive with the following template
<div>
<span class="label">My Label</span>
<input ng-model="name" required>
</div>
I want the label to be painted red when the input field is invalid.
How can I do that?
Currently I have another directive to sync all the errors from ngModelCtrl to the wrapping div
<div add-all-errors>
...
</div>
And the directive's link function does something like this:
const ngmodel = $element.find('[ng-model]').controller('ngModel');
$scope.$watch(()=>ngmodel.$error, addAllClasses, true);
Where addAllClasses simply makes sure the correct classes appear on the element..
I also tried just adding the same ng-model
<div ng-model="name">
...
</div>
But did not see the classes there..
any better way to do this?
This is why we use the angularjs form... I'm really not sure why people are against using a very handy feature.
I've made a plunker for you.
https://plnkr.co/edit/bGOcQjWzlRq2aTYZUYNm?p=preview
<form name="form">
<span ng-class="{red: form.name.$invalid}">Name:</span>
<input name="name" ng-model="name" required>
</form>
A little more insight of what's going on. form is added to the scope auto magically by angularjs by it's name. In this case, I named it form, however it can be any name.
Now form is an ngForm Object and adds all input field into it by their name attributes. This way we can do form.name to get another object similar to the ngForm Object. We can then use $invalid or $valid properties with ng-class.
ngForm is pretty powerful and is loaded with many cool properties and methods. Just call console.log(scope.form); You will need to put in a method and add it to ng-change to see updates.

Using conditional operators in v-model?

I have a vue component that shows a form populated with items from a selected item to edit. Now I don't want to have to use a second form for creating a new item. At the moment I auto populate and update the item with v-model which obviously updates the object. Am I not able to use conditional operators in this like so?
<form #submit.prevent>
<div class="field">
<label class="label">Job Title</label>
<p class="control">
<input type="text" class="input" placeholder="Job title" v-model="experiences[editIndex].title ? experiences[editIndex].title : ''" />
</p>
</div>
</form>
You can use conditional operators with v-model, but you can't give v-model a string like you're attempting in your example.
I wouldn't use the same form for editing and creating (might be preference). I would make the form its own component and then make two additional form components for editing and creating.
However, if you really want to handle the logic in each input's v-model directive, you would need to give it a variable in the last part of the ternary operator. Something like this:
v-model="experiences[i].title ? experiences[i].title : newExperience.title"
If you use eslint-plugin-vue it will complain about ternary in v-model.
ESLint: 'v-model' directives require the attribute value which is
valid as LHS. (vue/valid-v-model)
So I'd rather explicitly use a pair of :value and #input props.
Like that:
<input
type="text"
class="input"
placeholder="Job title"
:value="experiences[editIndex].title ? experiences[editIndex].title : ''"
#input="experiences[editIndex].title = $event.target.value"
/>
Also, you can use some function for #input, which will check property existence and add it if necessary.

Angular validators and ng-maxlength use

I've got the following div, which I want to add the bootstrap's class "has-error" if the input length is over 50 characters. This is the HTML:
<div class="form-group" ng-class="{has-error:[formData.titulo.$error]}">
<label for="inputTitulo">Título</label>
<input type="titulo" class="form-control" id="inputTitulo"
maxlength="50" ng-maxlength="50" ng-model="formData.titulo">
</div>
How can I make this work? I guess when you reach 50 characters, ng-maxlength throws a error, like the $error object, but I have no clue on what object is, how to access it, and if I have to do some more work in the controller or directive.
Any help here? I can't find any "easy" info regarding this issue and Angular validators.
edit 1:
I've seen all your responses, learned something new thanks to you, but this is still somehow not working. It currently is this way:
<div class="form-group" ng-class="{'has-error': formData.titulo.$error.maxlength}">
<label for="inputTitulo">Título</label>
<input type="titulo" class="form-control" id="inputTitulo" maxlength="50" ng-maxlength="50" ng-model="formData.titulo">
</div>
Also tested checking the length directly, as one of you suggested. But none of these solutions seem to work: it never adds the has-error class. Why?
To have the errors published on the scope, a form directive is required.
<div ng-form="form1" ng-class="{'has-error': form1.text1.$error.maxlength}">
<input name="text1" ng-model="formData.foo" ng-maxlength="50">
</div>
(Notice that the above uses the name attribute of the input to publish the form data - really, the ngModelController - on the scope)
So, the above works, and it's preferable if you do form validation. But, if you just need to check the length of some input, you don't have to use form validation - you could just check the model directly:
<div ng-class="{'has-error': formData.foo.length > 50}>
<input ng-model="formData.foo">
</div>
as you are using ng-model to make validations ,, this class ng-invalid will be added to your input
docs : https://docs.angularjs.org/api/ng/directive/ngModel
to use $error you need to access it using forms and names not ng-model ,, and the ng-class should be bound to the $error.maxlength not $error only
tutorial : https://scotch.io/tutorials/angularjs-form-validation
If you use the maxlength, a user will never be able to enter more characters than that, so you will never get the ng-maxlength error. It doesn't make sense to use maxlength and ngMaxlength together IMHO.
See the example on docs.angularjs.org/api/ng/directive/ngMaxlength (open the example in plunker and add maxlength attribute)

Is using ng-init for bind once valid in angular js?

i have a collection of values whose structure lets assume to be
var a = [{id:1, value:12, name="one"}, {id:2, value:34, name="two"},...]
i wanted to display this in a series of controls so that user can change the values. but with that i also wanted to display original values which obviously shoudn't change.
i found out a way that is working and my code is something like this using ng-init
<div ng-repeat="p in a">
<div class="control-group" ng-if="p.value>0">
<label class="control-label" ng-bind="p.name"></label>
<div class="controls controls-row" ng-init="v=p.value">
<input class="span1" value="{{v}}"/>
<input type="number" ng-model="p.value" class="span2" />
</div>
</div>
</div>
being a complete newbie in angularjs i dont know what implications this might have as i have very little experience in thinking about $watch and performance.
Is it ok to do so?
but with that i also wanted to display original values which obviously shoudn't change.
Use angular.copy(/* array */). It will create new copy (instance) of old array.
BTW a collection must be defined as $scope.a
Demo Fiddle

Categories

Resources