How to manipulate the HTML controls using directives - html

I have a scenario, like i want to use a single HTML file for displaying the form(to get inputs) and use the same HTML for displaying the form (to display the filled value).
Scenario 1 (Form input):
<input ng-model="a"/>
I want the above line to be like in scenario 2
Scenario 2 (Form display):
<label>{{a}}</label>
If i pass a value into a function(or directive) like "Form" then scenario 1 should come otherwise scenario 2 will come.
Any help will be highly appreciated

Solution using ng-switch: demo
<div ng-app>
<div ng-controller="ctrl">
<select ng-model="selection" ng-options="item for item in items">
</select>
<div ng-switch on="selection">
<input type="text" ng-model='a.val' ng-switch-when="one" />
<label ng-switch-when="two">{{a.val}}</label>
</div>
</div>
</div>
<script>
function ctrl($scope) {
$scope.items = ['one','two'];
$scope.selection = 'one';
$scope.a = {val:''};
}
</script>
Note that ng-switch creates new scopes, so in order to use the model from the controller I had to change a to an object, of which a reference is passed to the child scopes (which doesn't happen for primitives).

Related

Inputfield unfocus after typing 1 character

I can't seem to write down a full word without being unfocused after each character I type in the inputfield. Trying to understand why that is.
AngularJS
var module = angular.module("myModule", []);
module.controller("myController", function($scope) {
$scope.prop = {};
});
HTML
<div ng-app="myModule">
<div ng-controller="myController">
<button ng-show="!prop.dropdownType"
ng-click="prop.dropdownType = ['']">Init</button>
<div ng-hide="!prop.dropdownType" ng-repeat="(key, value) in prop.dropdownType">
<input type="text" ng-model="prop.dropdownType[key]">
</div>
<button ng-hide="!prop.dropdownType"
ng-click="prop.dropdownType.push('')" >Add options</button>
</div>
</div>
EDIT: created a quick code where you can see what i mean. Just run the code, initialize the inputfield and try to type a word: https://jsfiddle.net/wk173q0a/
I was able to fix your code by making the following change:
<div ng-hide="!prop.dropdownType">
<input ng-repeat="type in prop.dropdownType track by $index" type="text" ng-model="type">
</div>
The problem is that you are updating the key for the values in which you are iterating over. This is kicking off a digest cycle and you are losing focus. Also, the add button did not work because you were adding identical objects with no tracking.
Lastly, you will want to iterate over an array of objects to be able to maintain the reference in ng-model. Otherwise, all the changes will be lost once you add a new value to your array.
This is a great read on understanding the digest cycle:
https://www.thinkful.com/projects/understanding-the-digest-cycle-528/
This is happening because you are updating the list/object that controls your ng-repeat.
prop.dropdownType may start as [''], but as soon as you type into your input, you are updating the prop.dropdownType object. AngularJS sees that you have changed the prop.dropdownType and it refreshes the dom with the new input. If you typed the character A, the prop.dropdownType will now have a key of A (and a value of null?) and the input you see is now a different object.
If you change your ng-model to be a separate array or some other property, this issue should go away.
This is happening because of ng-repeat in tag. So, in this case try below 2 methods to solve the input element focus issue.
1) Use track by $index
<div ng-hide="!prop.dropdownType" ng-repeat="(key, value) in prop.dropdownType track by $index">
<input type="text" ng-model="prop.dropdownType[$index]">
</div>
2) Wrap your strings into objects. E.g. prop.dropdownType = [{value: 'string1'}, {value: 'string2'}, ...]:
<div ng-hide="!prop.dropdownType" ng-repeat="(key, value) in prop.dropdownType">
<input type="text" ng-model="prop.dropdownType[$index].value">
</div>

append String to ngModel to get an expression

I have a Business Object Car{id:2,name:ford,modelNo:123} I also have a MetaData class for CarMetaData{columnName:string,type:text,required:true}
each attribute has it's own CarMetaData object
how can I display Car data in a form in such a way using an Array of CarMetaData objects
In the template.html file
<form #carForm='ngForm'>
<div class="form-group" *ngFor="let metaData of metadata" >
<input [(ngModel)]="car[metaData.columnName]" name="metaData.columnName"
type="text">
</div>
</form>
In the component file
car:Car;
metadata:CarMetaData[];
The above method isn't working because of the [] in car[meta.columnName] in the
ngModel is there a way an expression can be worked out in *ngFor or [(ngModle)] to calculate columName
Very stupid mistake. Please try this:
<div *ngFor="let meta of metaData;">
<input [(ngModel)]="car[meta.columnName]" name="{{meta.columnName}}" />
{{meta.columnName}}
</div>
Old answer:
But first - your loop (meta in metaData) is wrong as you loop array, not object. So you better do let meta of metaData, let i = index.
And the interesting part is how ngModel sets the value of inputs in ngFor. If you try this:
<div *ngFor="let meta of metaData; let i = index">
<input [(ngModel)]="car[meta.columnName]" name="meta.columnName" />
{{meta.columnName}}
</div>
You will see that input value is the same of all inputs (it was picked from the first ngFor iteration and repeated to all inputs). However, {{meta.columnName}} prints correct values. So there is some scoping issue. Other strange thing - its definitely related with ngForm and input's name property. If you move that outside of the form - all happens as expected. And if you:
<div *ngFor="let meta of metaData; let i = index">
<input [(ngModel)]="car[meta.columnName]" name="{{metaData[i].columnName}}" />
{{meta.columnName}}
</div>
Inside the form - again, all works well. So that might be your workaround.
Here is a DEMO. Hopefully someone will explain it further.
I'd suggest to put both objects in a wrapper-object. e.g.
export class CarWrapper {
constructor(
car: Car,
carMetaData: CarMetaData
) {}
}
Then build a method that joins both objects in this wrapper
private carWrapper: Array<CarWrapper> = [];
private fillCarWrapper(): void {
// your code here
}
And in your template you act like this
<form #carForm='ngForm'>
<div class="form-group" *ngFor="let wrapper of carWrapper">
<input [(ngModel)]="wrapper.car" name="wrapper.carMetaData.columName" type="text">
</div>
</form>

ANGULARJS: Function I have in ng-checked directive runs infinitely

Trying to do something I thought is pretty simple but it's turning out to be pretty annoying. I'm just trying to have a function that runs when you click on a checkbox using the ng-checked directive.
This is the HTML:
<div class="form-group">
<label class="col-sm-2 control-label">Make Payment Optional</label>
<div class="col-sm-4 center-checkbox">
<input type="checkbox"
class="center-checkbox"
ng-model="formData.optionalPayment"
ng-checked="optionalPaymentCheckbox();"
validate-servererror="featured"/>
</div>
</div>
And this is the Angular:
if($scope.formData.optionalPayment === undefined) {
$scope.formData.optionalPayment = TournamentConst.PAYMENT.OPTIONAL;
}
(This check is just for when I load the page for the first time.)
$scope.optionalPaymentCheckbox = function () {
if($scope.formData.optionalPayment === TournamentConst.PAYMENT.OPTIONAL) {
$scope.formData.optionalPayment = TournamentConst.PAYMENT.MANDATORY;
} else {
$scope.formData.optionalPayment = TournamentConst.PAYMENT.OPTIONAL;
}
};
When I load the page, this ng-checked function runs infinitely. Is there something about the ng-checked directive I don't know, or some minor detail or forgot? Thanks in advance.
You are misunderstanding the intention of ng-checked. What you think it does is "execute this expression when the checkbox is checked" - an event handler directive.
What it actually does is set the checked property based on the expression. This means it sets up a watch on the expression and evaluates it every digest. If the value changes, it sets or unsets the checked property accordingly.
In fact, the documentation for ng-checked says this:
Note that this directive should not be used together with ngModel, as this can lead to unexpected behavior.
As #JB Nizet correctly pointed out, you can achieve the desired effect in your particular case by using ng-true-value and ng-false-value and removing ng-checked altogether.
So your HTML becomes:
<div class="form-group">
<label class="col-sm-2 control-label">Make Payment Optional</label>
<div class="col-sm-4 center-checkbox">
<input type="checkbox"
class="center-checkbox"
ng-model="formData.optionalPayment"
ng-true-value="TournamentConst.PAYMENT.MANDATORY"
ng-false-value="TournamentConst.PAYMENT.OPTIONAL"
validate-servererror="featured"/>
</div>
</div>
Then, in your controller, populate your TournamentConst object in the scope, so the template can see it:
$scope.TournamentConst = TournamentConst;
(or you can just populate the bits you need)
Finally, get rid of the whole $scope.optionalPaymentCheckbox function. You will still need the code to set the default value, though.
One last thing: It is confusing that the property is called optionalPayment, when it is really more like paymentType, but that is not related to the current problem.

appear an html element by clicking on a button with angularjs

I have an html form ( ) , I want that it is displayed when I click on a button.
the declaration of the form is the following :
<div id = "formulaire" class="gl" >
and the button is :
Edit
I use angularjs in my code . Please help me.
It better to use a simple variable than a function in this case. I would also recommend using controller scope when setting variables instead of the application scope so you don't run into issues with the variables when your application becomes large.
I also picked data-ng-click over ng-click because it will allow the html to validate correctly (which can be checked using the W3's validator).
Try this...
"use strict";
angular.module('myApp', [])
.controller("myController", function() {
this.edit = false;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<div data-ng-app="myApp" data-ng-controller="myController as ctrl">
Edit
<div id="formulaire" class="gl" data-ng-show="ctrl.edit">
<form>
<fieldset>
<label>Field:</label>
<input type="text" />
</fieldset>
</form>
</div>
</div>
Have you looked into the ngShow directive? It ables you to show or hide a DOM element depending on whether the attribute expression resolves to a truthey or falsey value.
Add model change on click
Edit
And then display the form if model is true
<div id = "formulaire" class="gl" ng-if="show">

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.