I am using Angular 4 and I am trying to toggle contenteditable="true" / contenteditable="false"
I've got this:
<h1 (dblclick)="edit($event)" contentEditable="true">Double-click Here to edit</h1>
or I could also have a checkbox if its easier to toggle edit mode?
<input type="checkbox" name="" value="" (onchange)="edit($event)">Toggle Edit mode
and on the .ts file:
edit(event) {
// code here needed
}
Create a one way binding from the contentEditable property to a field in your Component, then add an event binding for the click event on the header.
In your component, create a boolean:
private isEditable = false;
Then, in your html, toggle the value of this boolean with a click event, and bind the contentEditable property to this boolean as well:
<h1 (dblclick)="isEditable = !isEditable" [contentEditable]="isEditable">
You could also put the code of the (dblclick) binding inside a method on your component, if you'd rather have some method like toggleIsEditable(), with additional logic.
What you need is the following.
HTML
<h4 (dblclick)="contentEditable=true; toto.focus()"
*ngIf="!contentEditable">
{{myText}}
</h4>
<input #toto
autofocus
*ngIf="contentEditable"
[value]="myText"
(keyup.enter)="contentEditable=false; save(toto.value)"
(blur)="contentEditable=false; save(toto.value)"
type="text">
TypeScript
myText = "Double-click Here to edit";
With some css you can get to have one in place of the other.
Here is the plunker
Related
I am working for the first time with reactive forms in Angular(v7)
I want to make my submit button enabled if any of the fields has changed. So I am working with the dirty value of the form.
For a simple scenarios its working. I change a input type text and the button became enable.
But now I got a problem with an element (<span id="locationUpdated">) that the value inside of it is being changed by the result of some other javascript functions.
So I decided to listening the change of an hidden input that get the value the same way as the span element
<form [formGroup]="model.form" >
....
<input id="nameInput" type="text"
[(ngModel)]="model.user.name"
formControlName="name" />
...
<span [textContent]="model.user.location.label" id="locationUpdated"></span>
<input type="hidden" [value]="model.user.location.label" formControlName="location" />
....
<button [ngClass]="{'disabled': !model.form.dirty}></button>
</form>
--- Component ---
private buildFormUpdated() {
this.model.form = this.formBuilder.group({
name: [this.model.user.name, [Validators.required]],
location: [this.model.user.location.label]
});
}
I replace the hidden to text so I can see the value change and it is working fine.
However the property dirty continues false. If I change manually I get the dirty:true
What am I missing?
Thanks
What am I missing?
The dirty flag is not set to true when the data is changed programatically.
It is set to true only if the user blurs the control component, or changes the value (through the UI)
Please ref official docs - https://angular.io/guide/form-validation#why-check-dirty-and-touched
you can explicit marks the control as dirty by doing this after your logic
this.model.form.markAsDirty();
JSFiddle here: http://jsfiddle.net/c6tzj6Lf/4/
I am dynamically creating forms and buttons and want to disable the buttons if the required form inputs are not completed.
HTML:
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
JavaScript:
angular.module('choicesApp', ['ngSanitize'])
.controller('ChoicesCtrl', ['$scope', '$sce', function($scope, $sce) {
$scope.custom = "Required Input: <input required type='text'>";
$scope.trustCustom = function() {
return $sce.trustAsHtml($scope.custom);
};
$scope.buttons = [
{text:'Submit 1'},
{text:'Submit 2'}];
}]);
choicesForm.$invalid is false and does not change when entering text into the input field.
Solution:
I ended up using the angular-bind-html-compile directive from here: https://github.com/incuna/angular-bind-html-compile
Here is the relevant bit of working code:
<ng-form name="choicesForm">
<div ng-if="choices" bind-html-compile="choices"></div>
<button ng-click="submitForm()" ng-disabled="choicesForm.$invalid">
Submit
</button>
</ng-form>
And choices might be a snippit of HTML like this:
<div><strong>What is your sex?</strong></div>
<div>
<input type="radio" name="gender" ng-model="gender" value="female" required>
<label for="female"> Female</label><br>
<input type="radio" name="gender" ng-model="gender" value="male" required>
<label for="male"> Male</label>
</div>
The main problem is that ngBindHtml doesn't compile the html - it inserts the html as it is. You can even inspect the dynamic input and see that it doesn't have the ngModel's CSS classes (ng-pristine, ng-untouched, etc) which is a major red flag.
In your case, the form simply doesn't know that you've added another input or anything has changed for that matter. Its state ($pristine, $valid, etc) isn't determined by its HTML but by the registered NgModelControllers. These controllers are added automatically when an ngModel is linked.
For example this <input required type='text'> won't affect the form's validity, even if it's required, since it doesn't have ngModel assigned to it.
But this <div ng-model="myDiv" required></div> will affect it since it's required and has ngModel assigned to it.
The ngDisabled directive on your buttons works as expected since it depends on the form's $invalid property.
See this fiddle which showcases how ngModel registers its controller. Note that the html containing the dynamic input gets compiled after 750ms just to show how NgModelControllers can be added after FormController has been instantiated.
There are a few solutions in your case:
use a custom directive to bind and compile html - like this one
use ngInclude which does compile the html
use $compile to compile the newly added HTML but this is a bit tricky as you won't know exactly when to perform this action
This is an answer yet imcomplete because i cannot do the code at the moment.
I think your html will be included, not compiled. So the inputs are not bind to angular and are not part of the angular form object.
The only way i see is to use a directive that will compile the passed html and add it to your form. This may be quite tricky though, if you want to go on this way i suggest to edit your question to ask for the said directive.
However i'm not really familiar with $compile so i don't know if it'll work to just add $compile around $sce.trustAsHtml()
You can write a method as ng-disabled does not work with booleans, it works with 'checked' string instead:
So on your controller place a method :
$scope.buttonDisabled = function(invalid){
return invalid ? "checked" : "";
};
And on your view use it on angular expression :
<button ng-repeat="button in buttons" ng-disabled="buttonDisabled(choicesForm.$invalid)">
Here is a working fiddle
Working DEMO
This is the solution you are looking for. You need a custom directive. In my example I have used a directive named compile-template and incorporated it in div element.
<div ng-bind-html="trustCustom()" compile-template></div>
Directive Code:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
I found the directive in this fiddle.
I believe what is really happening though due to jsfiddle I'm unable to dissect the actual scopes being created here.
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
The first div is your top level scope, your form is the first child scope. Adding the div using a function creates the dynamically added input field as a child of the first child, a grandchild of the top level scope. Therefore your form is not aware of the elements you're adding dynamically causing only the static field to be required for valid form entry.
A better solution would be to use ng-inclue for additional form fields or if your form isn't to large then simply put them on the page or template you're using.
I have generated button using ng-repeat directive in angular js. Now I want to generate a textfield inside a div tag on click of that button.
Example buttons -
Example textfields added on click of it -
I am doing this using innerHTML attribute of div tag, like below -
var txt = document.getElementById("paidby").innerHTML;
document.getElementById("paidby").innerHTML=txt+ "<div class='row' style='padding:2px'><div class='col-sm-4'><input type='number' placeholder="+str+"></div></div>";
But I want to know if there is any other better way using angularJS or javascript to do the same so that if I need to remove one or all of the textfields later on, it can be done easily. By removing means deleting and NOT hiding.
(becuase if I want to remove for example textfield 'two' now, I have no idea how I remove it)
You don't want to manipulate the DOM within your controller. Often times, there are better ways to do this within the framework that Angular provides.
You can do this by having another ng-repeat which loops over an array you declare within your controller. For example:
In your view:
<section id="paidby" ng-repeat="textfield in textfields">
<div class='row' style='padding:2px'>
<div class='col-sm-4'>
<input type='number' placeholder="{{textField.str}}" ng-model="textField.value">
</div>
</div>
</section>
In your controller, within your button ng-click logic:
// To add:
$scope.textFields.push({ str: someVariable, value: someValue });
// To remove:
var index = $scope.textFields.map(function(t) { return t.value; }).indexOf(someValue);
if (index !== -1) {
$scope.textFields.splice(index, 1);
}
Try hiding the inputs to start with, then show them if the appropriate button is clicked:
<script src= "http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<div ng-app="" ng-init="array=['one','two','three','four','five']; show=[false,false,false,false,false]">
<button ng-repeat="item in array" ng-click="show[$index] = !show[$index]">{{item}}</button>
<br>
<input type="text" ng-repeat="item in array" placeholder="{{item}}" ng-if="show[$index]" />
</div>
I'm a big fan of angularjs, I started lately to use it in all of my 'coding for fun' projects.
I have a big curiosity:
I have a two inputs, one disabled by a ng-disabled directive and the other disabled with an html tag (A better illustration in this link):
//...
<input type="text" disabled value="This is an html input text disabled" />
<input type="text" ng-disabled="true" value="disabled with angular js directive" />
//...
Using the browser ability I can right click on the input and remove the disabled and ng-disabled tags but only the one with the disabled tag would be editable, the other one will still be tracked by angular even when ng-disabled directives has been removed.
So, When and Why should I prefer using ng directives over native html tags? Which could be the impact of letting angular track all these actions? is it really worth to use it everywhere?
Use the native html 'disabled' if the element should always be disabled. (static, for example if you want to provide an input with text and never let the user change it)
Use angular if it should change based on variables value in the scope.
For example, say a button should change the state of an input.
<input type="button" ng-click="inpDisabled = true" >Disable Input</input>
<input type="text" ng-disabled="inpDisabled" />
live example
No harm will come if you still use ng-disabled="true" but it's redundant.
If you want to make directive static, you shoud use native html
<your-tag disable><your-tag>
against
<your-tag ng-disabled="true"><your-tag>
But AngularJS does not work this way, you shoud initialize your application and controller, then pass a variable as parameter to your directive:
JS:
$scope.isDisabled = true;
HTML:
<your-tag ng-disabled="isDisabled"><your-tag>
You shoud read more tutorials to make things clear
I've found a bizarre quirk in AngularJS/Firefox where the selectors use grab different elements. I've put it in a Plunker to demonstrate it's effect:
http://plnkr.co/edit/H7stCpQE59i0aUlQ865j?p=preview
Open it in Chrome and click the button. You're actually selecting a hidden <input> element, then Angular passes it's event/parent along, grabs the parent <button> and adds the class .active to it, like so:
$scope.selectTag = function($event){
var elem = angular.element($event.srcElement).parent();
if(elem.hasClass('active')){
elem.removeClass( "active" );
}else{
elem.addClass('active');
}
}
In Firefox, though, it selects the <input> element and adds .active to that rather than the <button> that is its parent.
Any ideas on why this is happening?
No need for using jQuery. Just use ng-class. Following requires zero code in controller to accomplish what your code will end up doing. Also controllers shouldn't have any DOM manipulation code in them
<label class="btn btn-default" ng-class="{active:btn_active}" >
<input class="" ng-click="btn_active=!btn_active" type="checkbox" />
Button Text
</label>
Learn to look for angular approaches first before using jQuery methodology!
DEMO
As in the comment by Arun P Johny, use $event.target rather than srcElement. But, you shouldn't be manipulating the DOM like that when using Angular JS. Instead, you could do this with ng-class.
<label class="btn btn-default" ng-class="foo">
<input class="" ng-click="foo=(foo==='active') ? '' : 'active'" type="checkbox" />
Button Text
</label>