Unable to control html elements using form state - html

I am trying to create a form in angular
<form name="vm.mechanicalForm">
<div layout="column" layout-padding>
<div layout="row" layout-align="end center">
<md-button class="aq-btn md-accent" type="submit"
ng-show="vm.Auth.check({access: 'EDIT'})"
ng-click="vm.save()"
ng-disabled="vm.mechanicalForm.$invalid">
Save Button
</md-button>
</div>
<div>
<md-input-container class="md-block"
ng-if="vm.building.mainSourceOfCooling === 'CHILLERS'">
<label>Cooler</label>
<input name="numberOfChillers" class="form-control"
type="number"
ng-model="vm.building.numberOfChillers"
required min="0">
<div ng-messages="vm.mechanicalForm.numberOfChillers.$error"
ng-hide="vm.building.numberOfChillers">
<div ng-message="min">
<span class="red">Number of Chillers must be a positive number</span>
</div>
</div>
</md-input-container>
</div>
</div>
</form>
I cannot get any of the states connected to the form, for example vm.mechanicalForm.$invalid/$valid to work. I want to be able to disable my save button when there is a negative number of Coolers entered. Even though the error Number of chillers must be positive is displayed the save button still works. Additionally, I wanted to change the ng-hide on the ng-messages div to hide the div when the form is valid, but that seems to break functionality too. What can I do to correct this error? I have looked around on stack overflow and the answers I have seen seem to suggest I am using the form state correctly.
EDIT:
Here is my controller code:
namespace properties {
export class MechanicalCtrl {
constructor(
public Messages,
public building,
public BuildingService,
private allEnums,
private Auth
) {}
public save() {
this.BuildingService.updateBuilding(this.building)
.then(() => {
this.Messages.success('Successfully updated mechanical information');
})
.catch(() => {
this.Messages.error('Unable to update mechanical information');
});
}
}
angular
.module('properties')
.controller('MechanicalCtrl', MechanicalCtrl);
}
According to the answer here I should be able to reference vm.mechanicalForm in my MechanicalCtrl as this.mechanicalForm (unless I misunderstood that answer). Another thing I have noticed is that I am not able to do this. I had tried debugging this by trying to console.log properties on the form but it is returned as undefined if I do console.log(this.mechanicalForm). Have I done something wrong?
EDIT 2:
I am setting up my template and controller as
.state(...) {
templateUrl: 'my/template/url',
controller: 'MechanicalCtrl as vm',
...
}

Related

How to instantiate a variable in Nuxt

I've tried a couple of different ways to instantiate a variable in Nuxt but neither way seems to work. I have read around the subject and suspect that perhaps what I'm trying to do is not compatible with Webpack but I'm not sure how.
Here is a jsFiddle of the code: jsfiddle.net/tutmoses/z2365g49/4
First in the script section I export dataSize:
<script>
export default {
data(){
return {
page_name: "Run Model",
dataSize: 1296
}
}
</script>
Then in the HTML above I'm trying to import it but nothing renders:
<div class="setting">
<span class="setting-label">Training Size:</span>
<input id="trainingSize" :value="dataSize"></input>
</div>
I've also tried this:
<div class="setting">
<span class="setting-label">Training Size:</span>
<input id="trainingSize" :value= {{ dataSize }}></input>
</div>
...but the value instantiates as
{{
I've tried both of the above options without binding the value but that didn't work either.
Another way I've tried is this in a separate file:
export const nnSettings = {
dataSize: 1296
}
And then importing it with this:
import nnSettings from '~/components/testindex.js'
Again, zip.
The reason why I'm importing the value is because other values will be calculated from it. What would be the standard, best way to do it?
Nuxt (Vue) uses v-model to bind to form input. Have a look here for more info on form bindings
<div class="setting">
<span class="setting-label">Training Size:</span>
<input id="trainingSize" v-model="dataSize"></input>
</div>

Angular Instantiate components more times

I'm using angular 6 and i can't instantiate a components more than one time, I have tryed to see similar question on stack overflow but I didn't find answer
here is html-div-component.html
<div [style.opacity]="opacity" class='div-component' [style.margin- top.px]="marginTop">
and html-div.component.ts
#Component({
selector: 'app-html-div',
templateUrl: './html-div.component.html',
styleUrls: ['./html-div.component.css']
})
export class HtmlDivComponent implements OnInit {
#Input() name;
#ViewChildren('div-component') elements: QueryList<HtmlDivComponent>;
...
constructor() { }
ngOnInit() {
console.log('initializing');
window.addEventListener('scroll', this.scrollHandler, true);
window.onload = (event) => {
...
};
}
...
}
Here the parent component .html
<app-html-div id="get-started-div" name="blue">
<div class="charat-list default-panel mui-panel">
<h2 class="forstyle-h2">/h2>
<p>
<input type="button" value="1">
</p>
<p>
<input type="button" value="2">
</p>
</div>
</app-html-div>
<app-html-div name="green">
<div class="charat-list default-panel mui-panel">
<h2 class="forstyle-h2"></h2>
<p>
<input type="button" value="1">
</p>
<p>
<input type="button" value="2">
</p>
</div>
in my log console i see this
initializing
Thank you for your help
As discussed over comments, console log in ngOnInit will be consoled in browser developer tools as many times as the component used
codesample -
https://stackblitz.com/edit/angular-qwae9v?file=src/app/app.component.html
https://stackblitz.com/edit/angular-dvaleu?file=src/app/hello.component.ts
In your case, it is not issue with the application but the browser developer tools is designed to stack repeated console messages to one and show the number for the repetition
Check this link for reference - https://developers.google.com/web/tools/chrome-devtools/console/ as per Chrome documentation
Message stacking
If a message is consecutively repeated, rather than printing out each instance of the message on a new line, the Console "stacks" the messages and shows a number in the left margin instead. The number indicates how many times the message has repeated.
Just for testing and understanding , try this simple example
for (i = 0; i < 4; i++) {
alert("test")
console.log("test")
}
//ouput you will see in console as 3 test, you will see three alert windows
Is the second instance of <app-html-div> rendering?
It is possible that your first instance of HtmlDivComponent initializes, runs, and fails. If there was a problem in the first instance of the component, that may prevent the rest of the application from loading.

Working with custom components for input generates "No value accessor for form control with path X->0->Y"

I have a working form taking the following HTML markup. No errors or warnings.
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
I transformed it into a custom component, which also works, as shown below.
<app-input-text [info]="'Title'"
formControlName="targetField"
ngDefaultControl></app-input-text>
In my next view, I need to use FormArray as follows - still working code.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<div class="input-element">
<div class="input-caption">Title</div>
<input type="text"
formControlName="targetField"
class="form-control">
</div>
</div>
</div>
Now, I expected that combining both (i.e. being able to use custom input component and being able to form array for components) would post no problem. However, the sample below doesn't work.
<div formArrayName="stuff">
<div *ngFor="let thing of form.controls.stuff.controls; let i = index;"
[formGroupName]=i>
<app-input-text [info]="'Title'"
formControlName="targetField"
class="col-sm-6"></app-input-text>
</div>
</div>
It generates the following error.
No value accessor for form control with path: 'stuff -> 0 -> targetField'
The custom component is design like this (although given that it works in the explicit markup example, I'm not sure if it's relevant information). The only (wild) guess I have might be that value isn't jacked into the form array field somehow.
export class InputTextComponent implements OnInit {
constructor() { this.value = new EventEmitter<string>(); }
#Input() info: string;
#Output() value: EventEmitter<string>;
onEdit(value: any): void { this.value.emit(value); }
}
The group and array creating in the current view is done like this (not sure if this is of any relevance neither, as it works for the explicit HTML markup case).
this.form = builder.group({
id: "",
stuff: builder.array([
builder.group({ targetField: "aaa" }),
builder.group({ targetField: "bbbb" }),
builder.group({ targetField: "cc" })
])
});
Is there a limitation in Angular in this regard that I'm not aware of? I'm rather sure there's not and that I'm just doing something fairly clever simply missing a tiny detail.
I do understand the error but I can't see how it relates to the code. The form can't find the 0th element in the array or that element has no field of that name. Since I do get to see a few rows, I know there must be a 0th element. Since I specified the name of the field, I know there is indeed such. What else am I missing?

AngularJS ng-modal do not return latest value from form input

I am still new towards AngularJS, I made a simple textarea to handle user input using angular model binding like below code (noted that my ng-app and ng-controller are being injected somewhere else but it is within the entire <div></div>):
HTML:
<div ng-controller="StatusCtrl">
//some other HTML
<div class="sPTabs-holder">
<tabset>
<tab heading="Status">
<div>
<form class="statusPost" enctype="multipart/form-data">
<div class="form-group no-margin">
<div class="col-md-12 col-sm-12 no-pad">
<textarea type="text" ng-model="inputStatus" class="statusPostBox" placeholder="what's new on your mind?"></textarea>
</div>
</div>
<div class="form-group no-margin">
<div class="col-md-12 col-sm-12 no-pad">
<button style="width: 12%;" ng-click="postStatus()" class="btn btn-primary btn-sm" type="button">Share</button>
</div>
</div>
</form>
</div>
</tab>
<tab heading="Image">Image</tab>
</tabset>
</div>
</div>
JS:
'use strict';
var Status = angular.module('Status',['ui.bootstrap','ngResource','ngSanitize'])
Status.controller('StatusCtrl', ['StatusService','$resource','$scope','$http', '$timeout', '$sce',
function StatusCtrl(StatusService, $resource, $scope, $http, $timeout, $sce) {
//Usable models
$scope.inputStatus;
//Html-bind
$scope.makeTrust = function(html){
return $sce.trustAsHtml(html);
}
$scope.postStatus = function(){
if ($scope.inputStatus == null){
console.log('Blank post alert');
alert('You cannot post with blank statuses!');
}else{
console.log($scope.inputStatus);
}
}
}]);
My problem is whenever I click on the submit button angular will always pop me with the empty input error even though I have input in the textarea. At first I thought that I made a mistake in my model binding so I have tried out to echo the value in html using {{inputStatus}}, things appeared as it was typed and also when I try to define a default value in $scope.inputStatus = 'default value', the console does indeed echoed 'default value', but the problem is it doesn't store anything that is being typed in the form. What have i done wrong in my code?
Noted that I am not so familiar on how to setup AngularJS in JSFiddle. I apologize in advance if you would like to see the working demo.
**Update 1 - I have narrow down the problem, apparently the problem only occur when I am using angular tabs by Angular Bootstrap. So what happen is if you revise the HTML code, there is this <tabset> section. When declaring the ng-controller after the <tabset> section and everything works like a charm but if you declare it before the <tabset> section, that is where everything mess up.
You should initialize $scope.inputStatus in your controller, otherwise it will pop out an alert windows if you haven't input anything in the textarea (which will initialize or update $scope.inputStatus).
So you change your controller to
$scope.inputStatus = "";
Then everything will work, here is a working demo.
update
If you are using <tabset>, then you are facing child scope problem. <tabset> will create a child scope inside your controller, which means, the scope bind to tabset is the child of scope bind to StatusCtrl.
There are two ways to fix this problem. The first one is accessing the parent scope directly by changing your ngModel to below
<textarea type="text" ng-model="$parent.inputStatus" class="statusPostBox" placeholder="what's new on your mind?"></textarea>
The second one is easier but may looks like a trick, use Dot notation like #lcycook mentioned. In your controller StatusCtrl, declare a dictionary called data
$scope.data = {
inputStatus: ""
};
Then you can access the inputStatus by data.inputStatus anywhere inside the controller scope and you don't need to care about the child scope.
While there is no direct evidence, I suspect your text area is masked inside a child scope. This is common for new AngularJS developers.
While you are learning which directive creates a child scope (e.g. ng-if, ng-repeat), you can avoid this problem with "Dot notation". Which is, wrapping the model inside an object.
You can do this by initializing your ng-model or at least the wrapper object in your controller.
$scope.data = {};
// OR
$scope.data = {inputStatus=''};
Then in your template
<textarea type="text" ng-model="data.inputStatus" class="statusPostBox" placeholder="what's new on your mind?"></textarea>
Process it in your controller by referring it as $scope.data.inputStatus.
Some people even argue you are doing it wrong if you don't do this for any ng-model, but I find thinking wrapper object name is hard so I still use "dotless" one if I know the there is no child scope.

How do I reset a form including removing all validation errors?

I have an Angular form. The fields are validated using the ng-pattern attribute. I also have a reset button. I'm using the Ui.Utils Event Binder to handle the reset event like so:
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
As you can see, when the form is reset it calls the reset method on the $scope. Here's what the entire controller looks like:
angular.module('app').controller('mainController', function($scope) {
$scope.resetCount = 0;
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
$scope.search = function() {
alert('Searching');
};
});
I'm calling form.$setPristine() and form.$setUntouched, following the advice from another question here on Stack Overflow. The only reason I added the counter was to prove that the code is being called (which it is).
The problem is that even after reseting the form, the validation messages don't go away. You can see the full code on Plunker. Here's a screenshot showing that the errors don't go away:
I started with the comment from #Brett and built upon it. I actually have multiple forms and each form has many fields (more than just the two shown). So I wanted a general solution.
I noticed that the Angular form object has a property for each control (input, select, textarea, etc) as well as some other Angular properties. Each of the Angular properties, though, begins with a dollar sign ($). So I ended up doing this (including the comment for the benefit of other programmers):
$scope.reset = function(form) {
// Each control (input, select, textarea, etc) gets added as a property of the form.
// The form has other built-in properties as well. However it's easy to filter those out,
// because the Angular team has chosen to prefix each one with a dollar sign.
// So, we just avoid those properties that begin with a dollar sign.
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
// Set each control back to undefined. This is the only way to clear validation messages.
// Calling `form.$setPristine()` won't do it (even though you wish it would).
for (let name of controlNames) {
let control = form[name];
control.$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
};
$scope.search = {areaCode: xxxx, phoneNumber: yyyy}
Structure all models in your form in one place like above, so you can clear it like this:
$scope.search = angular.copy({});
After that you can just call this for reset the validation:
$scope.search_form.$setPristine();
$scope.search_form.$setUntouched();
$scope.search_form.$rollbackViewValue();
There doesn't seem to be an easy way to reset the $errors in angular. The best way would probably be to reload the current page to start with a new form. Alternatively you have to remove all $error manually with this script:
form.$setPristine(true);
form.$setUntouched(true);
// iterate over all from properties
angular.forEach(form, function(ctrl, name) {
// ignore angular fields and functions
if (name.indexOf('$') != 0) {
// iterate over all $errors for each field
angular.forEach(ctrl.$error, function(value, name) {
// reset validity
ctrl.$setValidity(name, null);
});
}
});
$scope.resetCount++;
You can add a validation flag and show or hide errors according to its value with ng-if or ng-show in your HTML. The form has a $valid flag you can send to your controller.
ng-if will remove or recreate the element to the DOM, while ng-show will add it but won't show it (depending on the flag value).
EDIT: As pointed by Michael, if form is disabled, the way I pointed won't work because the form is never submitted. Updated the code accordingly.
HTML
<form name="searchForm" id="searchForm" ui-event="{reset: 'reset(searchForm)'}" ng-submit="search()">
<div>
<label>
Area Code
<input type="tel" name="areaCode" ng-model="areaCode" ng-pattern="/^([0-9]{3})?$/">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.areaCode.$dirty">The area code must be three digits</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" name="phoneNumber" ng-model="phoneNumber" ng-pattern="/^([0-9]{7})?$/">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern" ng-if="searchForm.phoneNumber.$dirty">The phone number must be seven digits</div>
</div>
</div>
<br>
<div>
<button type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
JS
$scope.search = function() {
alert('Searching');
};
$scope.reset = function(form) {
form.$setPristine();
form.$setUntouched();
$scope.resetCount++;
};
Codepen with working solution: http://codepen.io/anon/pen/zGPZoB
It looks like I got to do the right behavior at reset. Unfortunately, using the standard reset failed. I also do not include the library ui-event. So my code is a little different from yours, but it does what you need.
<form name="searchForm" id="searchForm" ng-submit="search()">
pristine = {{searchForm.$pristine}} valid ={{searchForm.$valid}}
<div>
<label>
Area Code
<input type="tel" required name="areaCode" ng-model="obj.areaCode" ng-pattern="/^([0-9]{3})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.areaCode.$error">
<div class="error" ng-message="pattern">The area code must be three digits</div>
<div class="error" ng-message="required">The area code is required</div>
</div>
</div>
<div>
<label>
Phone Number
<input type="tel" required name="phoneNumber" ng-model="obj.phoneNumber" ng-pattern="/^([0-9]{7})?$/" ng-model-options="{ allowInvalid: true }">
</label>
<div ng-messages="searchForm.phoneNumber.$error">
<div class="error" ng-message="pattern">The phone number must be seven digits</div>
<div class="error" ng-message="required">The phone number is required</div>
</div>
</div>
<br>
<div>
<button ng-click="reset(searchForm)" type="reset">Reset</button>
<button type="submit" ng-disabled="searchForm.$invalid">Search</button>
</div>
</form>
And JS:
$scope.resetCount = 0;
$scope.obj = {};
$scope.reset = function(form_) {
$scope.resetCount++;
$scope.obj = {};
form_.$setPristine();
form_.$setUntouched();
console.log($scope.resetCount);
};
$scope.search = function() {
alert('Searching');
};
Live example on jsfiddle.
Note the directive ng-model-options="{allowinvalid: true}". Use it necessarily, or until the entry field will not be valid, the model value is not recorded. Therefore, the reset will not operate.
P.S. Put value (areaCode, phoneNumber) on the object simplifies purification.
Following worked for me
let form = this.$scope.myForm;
let controlNames = Object.keys(form).filter(key => key.indexOf('$') !== 0);
for (let name of controlNames) {
let control = form [name];
control.$error = {};
}
In Short: to get rid of ng-messages errors you need to clear out the $error object for each form item.
further to #battmanz 's answer, but without using any ES6 syntax to support older browsers.
$scope.resetForm = function (form) {
try {
var controlNames = Object.keys(form).filter(function (key) { return key.indexOf('$') !== 0 });
console.log(controlNames);
for (var x = 0; x < controlNames.length; x++) {
form[controlNames[x]].$setViewValue(undefined);
}
form.$setPristine();
form.$setUntouched();
} catch (e) {
console.log('Error in Reset');
console.log(e);
}
};
I had the same problem and tried to do battmanz solution (accepted answer).
I'm pretty sure his answer is really good, but however for me it wasn't working.
I am using ng-model to bind data, and angular material library for the inputs and ng-message directives for error message , so maybe what I will say will be useful only for people using the same configuration.
I took a lot of look at the formController object in javascript, in fact there is a lot of $ angular function as battmanz noted, and there is in addition, your fields names, which are object with some functions in its fields.
So what is clearing your form ?
Usually I see a form as a json object, and all the fields are binded to a key of this json object.
//lets call here this json vm.form
vm.form = {};
//you should have something as ng-model = "vm.form.name" in your view
So at first to clear the form I just did callback of submiting form :
vm.form = {};
And as explained in this question, ng-messages won't disappear with that, that's really bad.
When I used battmanz solution as he wrote it, the messages didn't appear anymore, but the fields were not empty anymore after submiting, even if I wrote
vm.form = {};
And I found out it was normal, because using his solution actually remove the model binding from the form, because it sets all the fields to undefined.
So the text was still in the view because somehow there wan't any binding anymore and it decided to stay in the HTML.
So what did I do ?
Actually I just clear the field (setting the binding to {}), and used just
form.$setPristine();
form.$setUntouched();
Actually it seems logical, since the binding is still here, the values in the form are now empty, and angular ng-messages directive is triggering only if the form is not untouched, so I think it's normal after all.
Final (very simple) code is that :
function reset(form) {
form.$setPristine();
form.$setUntouched();
};
A big problem I encountered with that :
Only once, the callback seems to have fucked up somewhere, and somehow the fields weren't empty (it was like I didn't click on the submit button).
When I clicked again, the date sent was empty. That even more weird because my submit button is supposed to be disabled when a required field is not filled with the good pattern, and empty is certainly not a good one.
I don't know if my way of doing is the best or even correct, if you have any critic/suggestion or any though about the problem I encountered, please let me know, I always love to step up in angularJS.
Hope this will help someone and sorry for the bad english.
You can pass your loginForm object into the function ng-click="userCtrl.login(loginForm)
and in the function call
this.login = function (loginForm){
loginForm.$setPristine();
loginForm.$setUntouched();
}
So none of the answers were completely working for me. Esp, clearing the view value, so I combined all the answers clearing view value, clearing errors and clearing the selection with j query(provided the fields are input and name same as model name)
var modelNames = Object.keys($scope.form).filter(key => key.indexOf('$') !== 0);
modelNames.forEach(function(name){
var model = $scope.form[name];
model.$setViewValue(undefined);
jq('input[name='+name+']').val('');
angular.forEach(model.$error, function(value, name) {
// reset validity
model.$setValidity(name, null);
});
});
$scope.form.$setPristine();
$scope.form.$setUntouched();