How to set the required field for md-autocomplete dynamically - javascript

trying to use md-autocomplete inside a custom directory.
I wanted the field to be required based on two conditions. One which is initialized(cond1) and one that changes(cond2)
I've tried to do it like this:
required="{{ cond1 && !cond2 }}"
But for some reason the md-autocomplete always becomes required, even if cond1 is initially undefined or false. It also doesn't respond to changes done to the value of cond2.
The validator also doesn't check if the selected-item has been set. It just checks if there is text in the field.
ng-required doesn't seem do anything.
Any ideas how i can make this work?
EDIT:
Here is my directive's .html
<div ng-form="dummyForm">
<md-autocomplete
md-input-name="{{ctrl.fieldName}}"
md-selected-item="ctrl.dummyItem"
md-selected-item-change="ctrl.setSelectedItem()"
md-search-text="ctrl.searchText"
md-items="item in ctrl.querySearch(ctrl.searchText)"
md-item-text="item.display || item"
md-min-length="0"
placeholder="{{ ctrl.placeholder }}"
ng-disabled="ctrl.selectAll || ctrl.isDisabled"
required="{{ ctrl.isRequired && !ctrl.selectAll }}">
<md-item-template>
<span>{{ item.display || item }}</span>
</md-item-template>
<md-not-found>
No results.
</md-not-found>
</md-autocomplete>
</div>
<div class="label-wrapper">
<label
class="checkbox-label"
ng-if="ctrl.selectAll !== null">
<input
type="checkbox"
ng-model="ctrl.selectAll"
ng-change="ctrl.checkSelectAll()">
{{ ctrl.checkboxName ? ctrl.checkboxName : 'Select all' }}
</label>
</div>
It is basically just a custom input field that lets the user either select a value from the list OR click the checkbox to "select all" items. When the checkbox is selected, i don't want the md-autocomplete field to be required anymore.

It does work fine with ng-required as you can see in this demo plnkr.
View
<form name="myForm">
<md-autocomplete
md-autoselect=true
placeholder="What is your favorite place?"
md-item-text="item.display"
md-items="item in ctrl.items"
md-menu-class="autocomplete-custom-template"
md-min-length="2"
md-delay="ctrl.throttle"
md-search-text="ctrl.searchText"
md-search-text-change="ctrl.searchTextChange(ctrl.searchText)"
md-select-on-match=true
md-match-case-insensitive=true
md-selected-item-change="ctrl.selectedItemChange(item)"
ng-required="ctrl.cond1 && !ctrl.cond2"
md-selected-item="ctrl.selectedItem">
{{ myForm.$error }}
<div>
<md-button type="submit">Submit</md-button>
</div>
</form>
AngularJS controller
myApp.controller('MyCtrl', function ($scope) {
var vm = this;
this.cond1 = true;
this.cond2 = false;
});

Related

How do I show angularjs material validation for another model property?

Using angularjs and material, I want to be able to have a readonly textbox that displays the name for a selected object a user looks up (via a modal popup), but the textbox validation should show as required and fire off if a separate id property is not populated. Here is an example plnkr.
I was originally thinking that I could do this simply by adding a hidden field with an ng-model, name, and required attribute, it would create the associated form property for the field with required validator (which it does), and I would be able to show the validator on the readonly textbox like so:
<form name="myCtrl.myForm" novalidate>
<input type="hidden" ng-model="myCtrl.id" name="id" required />
<div layout="row">
<md-input-container flex="50">
<label>Selected Object</label>
<input ng-model="myCtrl.selectedObject.selectedText" readonly />
<div ng-messages="myCtrl.myForm.id.$error">
<div ng-message="required">Please select an object.</div>
</div>
</md-input-container>
<div>
<md-button class="md-icon-button md-primary" ng-click="myCtrl.select($event)">
<md-tooltip md-direction="top">
Select Object
</md-tooltip>
<md-icon>search</md-icon>
</md-button>
</div>
</div>
<div>
<md-button class="md-raised md-primary" type="submit">Submit</md-button>
</div>
</form>
JS:
vm.select = function(evt) {
// Set the selected Object
vm.selectedObject = { selectedText: "Object id 1 selected", id: 1 };
// Set the associated ID
vm.id = 1;
};
However, the <div ng-message="required">Please select an object.</div> never displays when the form is submitted and validation fires. Any idea how I can accomplish this?
While I was typing up this question I had an idea - perhaps I should be creating a custom validator that I can apply to this input which references a separate property. That appeared to do what I needed. Here's the plnkr and here's the directive:
angular.module('MyApp', ['ngMessages', 'ngMaterial'])
.directive('requiredOther', RequiredOther);
function RequiredOther() {
return {
require: "ngModel",
scope: {
requiredOtherValue: "=requiredOther"
},
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.requiredOther = function(modelValue) {
return scope.requiredOtherValue !== undefined && scope.requiredOtherValue !== null && scope.requiredOtherValue !== '';
};
scope.$watch("requiredOtherValue", function() {
ngModel.$validate();
});
}
};
}
This is the updated HTML:
<form name="myCtrl.myForm" novalidate>
<input type="hidden" ng-model="myCtrl.id" />
<div layout="row">
<md-input-container flex="50">
<label>Selected Object</label>
<input name="id" ng-model="myCtrl.selectedObject.selectedText" readonly required-other="myCtrl.id" />
<div ng-messages="myCtrl.myForm.id.$error">
<div ng-message="requiredOther">Please select an object.</div>
</div>
</md-input-container>
<div>
<md-button class="md-icon-button md-primary" ng-click="myCtrl.select($event)">
<md-tooltip md-direction="top">
Select Object
</md-tooltip>
<md-icon>search</md-icon>
</md-button>
</div>
</div>
<div>
<md-button class="md-raised md-primary" type="submit">Submit</md-button>
</div>
</form>
The required-other="myCtrl.id" directive references the id property and watches for changes and fires off validation on change:
I guess I don't really need the hidden input field anymore either.

Using v-for and v-if in the same element

Hello i'm using vuejs and i need your help to get the best practice to do this:
langs : is an object of languages :
langs: {'1':'fr', '2':'en', '3':'ar'},
has_lang : equal to 1 for the case which i need enter a value for each lang
and equal to 0 for case which i need only enter one value for all languages
What i do now :
<md-layout md-gutter>
<md-input-container v-if="has_langs" v-for="lang in langs">
<label>#{{ attribute.attribute }} #{{ lang }}</label>
<md-input v-model="attValues"></md-input>
</md-input-container>
<md-input-container v-if="has_langs == 0">
<label>#{{ attribute.attribute }} #{{ lang }}</label>
<md-input v-model="inputa"></md-input>
</md-input-container>
</md-layout>
What i need is not duplicate the input * two times
this input * :
<md-input-container>
<label>#{{ attribute.attribute }} #{{ lang }}</label>
<md-input v-model="inputa"></md-input>
</md-input-container>
There is a way to set v-for and v-if in the same element or something else that can do this?
You can't really use v-if and v-for on the same element. Best to put the v-if on a parent element.
You can move all checks for has_langs and langs to code.
computed: {
__langs(){
return this.has_langs === 1? this.langs : {'0': 'Params for all'};
}
},
methods: {
manipulateWithLangs(){
if (this.has_langs === 1){
//do
} else {
// do something else
}
}
}
<md-layout md-gutter>
<md-input-container v-for="lang in __langs">
<label>#{{ attribute.attribute }} #{{ lang }}</label>
<md-input v-model="attValues"></md-input>
</md-input-container>
</md-layout>
You could do the v-if with a v-else on the md-layout the component level and get the result you want.
<md-layout v-if="has_langs" md-gutter>
<md-input-container v-for="lang in langs">
...
</md-input-container>
</md-layout>
<md-layout v-else md-gutter>
<md-input-container>
...
</md-input-container>
</md-layout>
Besides the option to create an additional filtered computed (effectively eliminating the need to use v-for and v-if on the same element), you also have a template level way of dealing with such edge-cases: the tag.
The tag allows you to use arbitrary template logic without actually rendering an extra element. Just remember that, because it doesn't render any element, you have to place the keys from the v-for on the actual elements, like this:
<template v-for="(guide, index) in guides"> <article v-if="isGuideVisible(guide)"
:key="index"
class="post-item post-guide"
:class="[guide.categories.toString().replace(/,/g, ' ')]"><header>
<h1 v-text="guide.title.rendered" /></header>

validation with angular checkbox-model

I'm using checklist-model in my angular/electron application and have following three checkboxes.
Both
USA
Canada
To start with, by default 'USA' is checked, however if the user clicks on 'Both' or 'Canada' , I need to check for a specific flag, and if the flag is false, I need to alert the user and avoid 'Both' or 'Canada' being clicked.
<div style="height:115px;background-color:#FBF9EC" class="form-control" >
<label ng-repeat="item in locations" style="width:80%">
<input type="checkbox" checklist-model="locationData.item" checklist-value="item" checklist-before-change="checkListvalidate(item)" ng-change="checkBoxClickFunc(item)" ng-click="locationClick()">
{{item}}
</label>
</div>
In the controller:
$scope.locationClick = function(){
if(!myFlag){
return false;
}
};
The spec for checklist-before-change says
An angular expression evaluated each time before the checklist-model has changed. If it evaluates to 'false' then the model will not change anymore.
What is it that I'm not doing correctly ....
You have many listeners in the checkedbox input :
checklist-before-change="checkListvalidate(item)"
ng-change="checkBoxClickFunc(item)"
ng-click="locationClick()"
I think these are too much to address your need and besides, it complicates the understand of the logic and it may create side-effects.
Why not use only checklist-before-change directive and make the call to locationClick() inside it ? I think it should be enough.
Here an example :
<body ng-controller="MainCtrl">
<div style="height:115px;background-color:#FBF9EC" class="form-control" >
<label ng-repeat="item in locations" style="width:80%">
<input type="checkbox" checklist-model="locationData.item"
checklist-value="item" checklist-before-change="checkListvalidate(item)" >
{{item}}
</label>
</div>
</body>

textbox validation for number and required in repeating mode angular js

Please refer below link
https://plnkr.co/edit/9HbLMBUw0Q6mj7oyCahP?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.NDCarray = [{val: ''}];
$scope.NDCadd = function() {
$scope.NDCarray.unshift(
{val: ''}
);
};
$scope.data = angular.copy($scope.NDCarray);
$scope.NDCcancel=function(){debugger
$scope.NDCarray=$scope.data;
}
$scope.NDCdelete = function(index) {
if(index != $scope.NDCarray.length -1){
$scope.NDCarray.splice(index, 1);
}
};
});
It contains the textbox with add button. I have added validation for number and required field, it is working fine. but when i click add button it will create another textbox with entered value that time it showing the validation message for all the textboxes , i don't want to show validation message for all the textboxes. need to show validation for corresponding textbox only. that means when i enter something wrong in second textbox it is showing message to that textbox only.refer below screenshot.
validation message displaying for all textboxes.that should display for only one textbox.
Working plnkr : https://plnkr.co/edit/f4kAdZSIsxWECd0i8LDT?p=preview
Your problem is in your HTML, to get independant fields you must :
Move outside the form of the ng-repeat
Provide a dynamic name using $index on your fields, because name is what make each fields independant on the validation.
Here is the final HTML from the plnkr i didn't touch at all the javascript :
<body ng-controller="MainCtrl">
<form name="myForm">
<div ng-repeat ="ndc in NDCarray">
<div class="col-sm-4 type7" style="font-size:14px;">
<div style="margin-bottom:5px;">NDC9</div>
<label>Number:
<input type="number" ng-model="ndc.value"
min="0" max="99" name="{{'input_'+$index}}" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.input.$dirty && myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.number">
Not valid number!</span>
</div>
<tt>value = {{example.value}}</tt><br/>
<tt>myForm['input_{{$index}}'].$valid = {{myForm['input_'+$index].$valid}}</tt><br/>
<tt>myForm['input_{{$index}}'].$error = {{myForm['input_'+$index].$error}}</tt><br/>
</div>
<div class="col-sm-4 type7 " style="font-size:14px;">
<div style="padding-top:20px; display:block">
<span class="red" id="delete" ng-class="{'disabled' : 'true'}" ng-click="NDCdelete($index)">Delete</span>
<span>Cancel </span>
<span id="addRow" style="cursor:pointer" ng-click="NDCadd()">Add </span>
</div>
</div>
</div>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</body>
Couple of changes:
If you add "track by $index" to your ng-repeat it will make each group of elements unique so that you don't have to worry about deriving unique names for elements.
Your validation on the number (myForm.ndcValue.$error.number) didn't work so I changed it to myForm.ndcValue.$error.max || myForm.ndcValue.$error.min
Also, you can throw an ng-form attribute directly on the div with your ng-repeat.
Like this:
<div ng-repeat="ndc in NDCarray track by $index" ng-form="myForm">
<div class="col-sm-4 type7" style="font-size:14px;">
<div style="margin-bottom:5px;">NDC9</div>
<label>Number:
<input type="number" ng-model="ndc.value" min="0" max="99" name="ndcValue" required>
</label>
<div role="alert">
<span class="error" ng-show="myForm.ndcValue.$dirty && myForm.ndcValue.$error.required">
Required!</span>
<span class="error" ng-show="myForm.ndcValue.$error.max || myForm.ndcValue.$error.min">
Not valid number!</span>
</div>
<tt>value = {{example.value}}</tt>
<br/>
<tt>myForm.ndcValue.$valid = {{myForm.ndcValue.$valid}}</tt>
<br/>
<tt>myForm.ndcValue.$error = {{myForm.ndcValue.$error}}</tt>
<br/>
</div>
Here's the working plunker.
I changed the input element name from "input" to "ndcValue" to be less confusing.

Angularjs: Add placeholder on input with active class

I have a list of inputs created via ng-repeat. Initially all inputs are disabled except the first one. First input also have an active class (it's red in color because of active class) Planker
When I focus on the first input field 2nd input field becomes enabled with same active class. Same for others
So what I am trying to do is, if inputs have active class there will be a "placeholder text" on it. Without active class there should be no placeholder.
How I can add a placeholder dynamically on the input with active class ?
Code:
<div class="col-md-2-4 voffset3" ng-repeat="item in csTagGrp">
<ul class="list-unstyled cs-tag-item-grp" ng-init="$parentIndex = $index">
<li class="clearfix" ng-repeat="value in item.csTags">
<div class="pull-left cs-tag-item-list">
<input ng-focus="focusItem($index, $parentIndex)" ng-disabled="!value.active" ng-class='{active: value.active && !value.old}' type="text" class="form-control input-sm">
</div>
</li>
</ul>
</div>
Thanks in advance.
You could set a placeholder on the input conditionaly, and If its true set to some value from the scope for example:
<input placeholder="{{value.active && !value.old ? placeholder : ''}}" ng-focus="focusItem($index, $parentIndex)" ng-disabled="!value.active" ng-class='{active: value.active && !value.old}' type="text" class="form-control input-sm">
// controller
$scope.placeholder = "something";
See this plunker.
You can add a function to the $scope and check for the active = true on your data model.
//controller
$scope.getPlaceholder = function (item) {
if(item.active){
return item.tags;
}
}
//view
<input ng-focus="focusItem($index, $parentIndex)"
placeholder="{{getPlaceholder(value)}}"
ng-disabled="!value.active"
ng-class='{active: value.active && !value.old}'
type="text" class="form-control input-sm">
I forked your Plink

Categories