toggle component input using only template - angular9

How can I toggle an input to a component using a button, without creating a component property to hold the value?
example:
<button #componentVisibility (click)="componentVisibility.value = !componentVisibility.value" value="false">
show graph
</button>
<my-component [graphVisible]="componentVisibility.value"></my-component>
This one for example is not good because value has to be a string, and I need to toggle a boolean and pass it in.
How can I toggle a local template boolean and pass it in to a component, without creating a component property to hold it?
Note:
Doing the following throws a transpile error:
<button (click)="graphVisible ? graphVisible = false:graphVisible = true;" value="false">
show graph
</button>
<my-component [graphVisible]="graphVisible"></my-component>
Error thrown about graphVisible not being a property on the parent component.

Related

How to add disabled attribute to button in react?

Here is my code
import React from 'react'
import PropTypes from "prop-types";
export default function Button({ htmlType, type, disabled, action, ...props}) {
return (
<button type={htmlType} onClick={action}>
{props.children}
</button>
)
}
Button.propTypes = {
htmlType: PropTypes.string.isRequired,
action: PropTypes.func,
disabled: PropTypes.bool
};
I call Button component by this code
<Button disabled={true}>button title</Button>
I want to add disabled html attribute to button when disabled of props is true, how to do it ?
You could line single line if-else statements like this:
<button disabled={propsDisabled}>button title</button>
Here, propsDisabled is the variable which you can pass through the props, and it is a boolean variable which will either be true or false. I have not used disabled itself to avoid confusion but you can use the variable name as disabled.
When propsDisabled is true, the button will be dissabled, and when propsDisabled is false the button will not be disabled.
Aya, I couldn't exactly understand your question, it looks like you're trying to solve a problem, when you have a second problem in the question in the first place.
Are you using Ant components? use the disabled prop on the <Button /> component itself. (notice the capital B in the component name Button).
<Button disabled={true} />
// or (it's the same but different JSX syntax)
<Button disabled />
This is the same answer answered by the brother #Abdul Qadir.
If you're working with native HTML elements, also, you can call the disabled attribute on the <button /> element (notice the small character b in the element name button) the same way and it should work:
<button disabled={true}>I'm disabled</button>
// or (the same but different syntax)
<button disabled>I'm disabled</button>
So here are the two answers,
If you're working with Ant components:
import { Button } from 'antd';
const CustomButton = ({ disabled, children }) =>{
return <Button disabled={disabled}>{children}</Button>
}
If you're working with native HTML elements:
const CustomButton = ({ disabled, children }) =>{
return <button disabled={disabled}>{children}</button>
}

Angular: Enable button asynchronously

I am fetching data in my angular app from a REST API. I want to enable the button of the search once the user logs in. I have added auth.service.ts which I am importing in my search navbar component. But, the button doesn't get enabled once the user logs in. The data is asynchronous so, it either remains disabled or enabled. I can't figure it out.
Here's my code:
navbar.component.ts
allowSearch: boolean = false;
buttonEnable() // this should get called every time change is detected in the input
{
if(this.authService.isAuthenticated())
{
this.allowSearch = true;
}
}
navbar.component.html
<input type="text" value="" class="form-control" (change) = "buttonEnable()" placeholder="Search..." [(ngModel)] =
"searchElement" name = "searchElement"
style = "background-color: #faf9f9" id = "searchElement" >
<div class="input-group-append" style = "background-color: #faf9f9">
<button type="button" name="button" type = "submit" [disabled] = "!allowSearch" (click) =
"openModal2(); onSearch(searchElement)"><i class="nc-icon nc-zoom-split"></i></button>
</div>
If you call to authService outside of navBar, you can get it if your service has, futhermore a method to login, a Subject. Else simply change the variable in the subscribe function.
e.g. using Subject:
LoginService(){
isLoggin:Subject<boolean>=new Subject<boolean>()
loggin(data){
this.httpClient.get(....).pipe(
tap(res=>{
if (.....)
this.isLoggin.next(true)
})
)
}
loggout(){
this.httpClient.get(....).pipe(
tap(_=>{
this.isLoggin.next(false)
})
)
}
}
Your nav-bar-component inject in constructor the loginService -make it public-
constructor(public loginService:LoginService){}
And your .html can be simple
<button [disabled]="!(loginService.isLoggin|async)">I'm login</button>
(*)tap not change the response, but executed when "someone" subscribe to our observable.
Here 'this.allowSearch' is getting initialized in buttonEnable(). Once you login to the application, You should call the 'buttonEnable()' method.
Same wise, When ever you logout from app, you should invoke the 'buttonEnable()' method.
So 'allowSearch' value will be updated.
Instead of this approach, you should have RXJS observables. have 'allowSearch' property in RXJS store and update based on login and logout actions.
Your component should subscribe for 'allowSearch' from store and update the 'allowSearch' component property.

Buttons not disabled when initializing component

I'm using the [disabled] property to decide if button elements should be or not disabled, depending if an array.length returns 0 or not.
This is working fine after the user interacts with the component, however the buttons aren't disabled when the component is initialized and the array is empty.
typescript
constructor() {
this.macrosSelected = [];
}
html
<button
type="button"
class="btn btn-secondary"
[disabled]="!macrosSelected.length"
>
As I said, this code is enabling the button when the array has an object inside it, and disabling it once the array returns to an empty state.
As you have assigned macrosSelected = [] in your constructor then this will be the initial value of your variable. If you are assigning this variable using #Input() decorator or by a service, those will update this variable after the constructor call means second assignment. So you can simply check the length of macrosSelected variable like below.
Your template code must be like:
<button type="button" class="btn btn-secondary" [disabled]="macrosSelected.length === 0"></button>

how to bind component variable to form object instance property

I am not quite sure how to bind a variable from my component class to a property value in my form object. The form needs to display this value and add it to the ngModel so that it can become part of the object instance.
I am struggling with the syntax and keep getting the errorNo value accessor for form control with name: 'per_print'
Error: No value accessor for form control with name: I think I need to use the [(ngModel)]="myObject.property" syntax, but I am not getting this from an input into the form, but from a binded variable on the class.
<div class="form-group">
<label class="label-text" >Per Print</label>
<div class="input-group" style="width:150px;">
<h4 [(ngModel)]="job_entry.per_print" name="per_print"
value='pricePerPrint' class="form-control"
>
{{pricePerPrint | currency:'GBP':true:'1.2-2'}}
</h4>
</div>
</div>
job_entry is my object which properties I am setting through the form. pricePerPrint is a variable on the class. I want to set this variable to one of the form instance properties. How to do this? I thought I could do it through value, as in the value of the <h4> tag, but this error persists.
You could use [hidden] input field with the value you want, so that this value will be added to your form. This means though, that you need to use pricePerPrint as the ngModel. But ngModel for your job_entry is possibly not needed. You could build the form as such, so that the object you get from the form can be assigned directly to job_entry:
onSubmit(obj) {
this.job_entry = obj;
}
Also check the Demo for that.
So your code would look like this:
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm.value)">
<input [hidden]="isHidden" name="per_print"
[(ngModel)]="pricePerPrint" [value]="pricePerPrint"/>
<h4>Price: {{pricePerPrint}}</h4>
<button type="submit">Submit</button>
</form>
where isHidden is set to true.
Other option I see, if you want to use [(ngModel)]="job_entry.per_print, you need to assign whatever value you have in pricePerPrint to job_entry.per_print.

AngularJS - bind ng-model with button click

I've got an irritating problem with data binding using ng-model and button.
The principle of operation of my site:
My HTML site displays a list of projects (loaded from external .json file).
Each row has a button named Edit which displays a modal containing some <input type="text" filled with relevant data about project (like project.name, project.date etc.)
Initial value of input is equal to object data (text-input called Name will contain project.name etc.)
Object is modified only if you click Save button and confirm the operation (confirm(sometext) is okay).
Closing the modal, not clicking the button or pressing cancel on confirmation box should prevent data from being updated.
Editing input (let's say that project.name is "Project2" and I modify it by adding 3 numbers resulting in "Project2137"), closing modal and opening it again should result in "Project2" text inside input (because object wasn't modified, only input)
So far I understand that single text input should look like this
<input type="text" id="editName" class="form-control" ng-model = "project.name">
Using ng-model means that they are binded. That's what I know. However editing input means that object is updated as soon as I enter some data.
I tried to fiddle with ng-model-options but I didn't find any possible solutions.
I tried to do it programmatically as well using
<input type="text" id="editName" class="form-control" value = {{project.name}}>
....
<button type="button" class="btn pull-right btn-primary btn-md" ng-click="edit(project)" data-dismiss="modal" >Save</button>
And function:
$rootScope.edit = function(project)
{
if(confirm("Are you sure to save changes?"))
{
project.name = angular.element(document.getElementById('editName')).val();
// ...and so on with other properties
This solution is kinda close to what I wanted to achieve (object is updated only on confirm), but I faced another problem: input loads data from object only once at the beginning instead of each time the modal is opened which is against rule #5
Is there any way to fix this using either ng-model bind or custom function? Or maybe there is some other, easier way?
--EDIT--
Here I don't have any problem with saving the data using a button, everything works well and clicking Save is reflected in a projects list. (well until I hit a F5 key).
The problem is that input text is not properly binded to project and that's what I want to fix.
Sample data (pseudocode)
project1.name = "Proj1"
project2.name = "Proj2"
I click an Edit button on row #1
Text input displays "Proj1". Everything is fine.
I change input by adding some random characters like "Proj1pezxde1"
Text input is now "Proj1pezxde1"
I do not click Save button.
I close the modal.
Project summary still displays "Proj1". Okay.
I click an edit button on first row
10. Text input is "Proj1pezxde1" even though I didn't modify an object.
Text input should read data from object again (each time I open this modal) and thus display "Proj1"
That's the problem I want to fix. Sorry for being a little bit inaccurate.
You can create a copy of the project object in modal controller and use this object to bind with the input element of the modal
$scope.copyProj = angular.copy($scope.project);
Assign the copy object properties to project only when save is clicked.
As per my understanding after reading the provided descriptions, you have a list of projects, which is being used as in an repeater and you want to bind each projects data to a Text box and a Button.
Have you tried initializing your Projects object following way?
$scope.projects = [
{ 'name': 'proj1', 'id': '1' },
{ 'name': 'proj2', 'id': '2' }
];
Then you can do something like below to show your data
<div ng-repeat="project in projects">
<div>
<input type="text" class="form-control" ng-model = "project.name">
<button type="button" class="btn pull-right btn-primary btn-md" ng-click="edit(project)" data-dismiss="modal" >Save</button>
</div>
</div>
The simplest way to do this in my opinion is using a second object that is a copy of the project, and after confirmation applying the changes to the original project object.
For example, a simple "pseudo code" of a controller:
function MyCtrl($scope) {
$scope.projects = [...];
$scope.currentProject = null;
$scope.edit = function(project) {
$scope.currentProject = angular.copy(project); // This will create a copy so the changes in $scope.currentProject will not reflect.
// Open dialog with input bound to $scope.currentProject
if (confirm) {
// Assign all properties from currentProject to project
angular.extend(project, $scope.currentProject);
}
}
}
So , as I understand from your question , you need to update the project data only if it is saved. To do that you can maintain a copy of the actual object which get updated only it is saved like below :
Here we are using angular.copy(), which does a deep copy of the source object.
$scope.original = {name : "xyz"};
$scope.project = angular.copy(original);
//Call this when the user confirms to save , here we are replacing the
//original copy with the latest object that needs to be saved.
$scope.save = function () {
$scope.original = angular.copy($scope.project);
}
//Call this when closing the modal or clicking cancel or when losing
//focus, this will reset the changes to the original copy.
$scope.reset = function () {
$scope.project = angular.copy(original);
}