Working with a set of column components with id input in my template:
<div class="panel-body" *ngIf="columns">
<div class="col-md-4">
<column [id]=columns[current_left_column].Id></column>
</div>
<div class="col-md-4">
<column [id]=columns[current_middle_column].Id></column>
</div>
<div class="col-md-4">
<column [id]=columns[current_right_column].Id></column>
</div>
</div>
I have buttons that increment and decrement the columns by changing the values of current_left_column, current_middle_column, and current_right_column. When I click those buttons I log the values of the three column id's and they're representative of where they should be however the template doesn't reload.
I did attempt to use ApplicationRef.tick() to trigger change detection, but the fact that it didn't change makes me think it's a binding issue, but I'm thus far unable to find anything that matches my case as 2-way binding seems to necessitate a more traditional input element and it currently is one way bound.
I think you need to change the name of the input. I have seen it several times that it collides with the id property every HTML element has.
I really appreciate the comments and info given, it helped me get a better handle on what I was actually trying to do.
I ended up taking an example from a different answer and using ngModel with an input to show the individual column id's. Then since I could prove my id's were switching but it wasn't updating even with the tick() I realized I was working on one component level too high and had to be initiating the change from the Column level. So I took the ngOnInit method that populates the columns with data and put it in a ngOnChanges() method, cleared the OnInit() because it was doubling up, and it started moving fine.
Then I had to deal with the information from the columns being added on top of the prior column, but that was relatively minor.
ngOnChanges(){
this._columnService.GetSingleColumn(this.id).subscribe(column => { this.columnData = column; });
this._cardService.GetCardsByColumnId(this.id).subscribe(cards => { this._cardColumnService.LoadCards(cards); console.log(this.cards); });
if (this.columnData == undefined) {
this.LoadDummyData();
}
}
Related
Good, I have a service that I call from my .ts component which I go through an array with court names, and when I click on an event that I command to call every time I click on a next or back arrow, a counter is added that starts at 0 which index 0 is field 1 and so on.
What happens is that I have a condition that shows me the hours of each day of the week in a calendar and that the view is updated according to the name of the field, this condition equals the array and the counter, my problem is that always stays at index 0 it is not updated, instead in my html if the name of the field changes.
I tried to do it only in the html all the conditions that I have, but there are so many that the code was distorted since I had many ng-containers.
HTML code and where I call the click event:
<div class="col-1 text-end" id="prev">
<span>◀</span>
</div>
<div class="col-2" id="nomCancha">
<p>{{ canchas[contador] }}</p>
</div>
<div class="col-1 text-start" id="next">
<span (click)="nextd()">▶</span>
</div>
</div>
Code of the condition and the variable that is not updated:
if(cancha.name === this.canchas[this.contador])
this.cancha is the array of the cancha and this.contador is the variable that contains the counter that goes through the indices this part in 0
event code:
nextd () {
this.contador += 1
if (this.contador === this.canchas.length) {
this.contador = 0
}
}
You are updating the value of contador property on click, that's fine. But this value is never getting rendered in the UI.
Take a look at property binding.
You also need to let the renderer know that the property value is updated, for that you need event binding, check it here
Read the angular doc of two way binding. It's a combination of both property and event binding. There are other possible solutions that you can do but this should solve your issue.
I currently have an accordion with a bunch of options in the form of checkboxes. The user can select the checkboxes as expected however, I want to already have some of those checkboxes checked depending on certain conditions. The issue is sometimes that is determined after the page has loaded. Below are simplified examples to demonstrate my code (my code is for work and would not be able to share due to confidentiality issues).
My HTML looks like this:
<div class="row">
<div "ngFor="let value of filteredPeople" >
<div>
<input type="checkbox" (click)="selectPeople(value)" [checked]="checkedPeople.get(value)">
{{ value }}
</div>
</div>
</div
My Javascript:
public checkPeople() {
this.selectedPeople.forEach(element => {
this.checkedPeople.set(element, true);
});
}
To explain some variables and methods:
Variables:
filterPeople - a string array of all possible people
checkedPeople - a map with a KVP of string (the people) and boolean (whether or not their checkbox is checked)
selectedPeople - a string array of people whose checkboxes I want already checked
Methods:
selectPeople - checks the corresponding checkbox when user clicks on it
checkPeople - a method called when I want the specific checkboxes checked (these specific checkboxes change based on other factors so I cannot hard code it)
For some reason my checkPeople method does not seem to select the expected checkboxes, I am not sure why but I have a feeling it is to do with the fact that I have used "[checked]" in the HTML and that it should be something else. Can someone clarify the issue for me and help me identify a fix? (Note: as the title suggests, I am using Angular)
Edit:
I have just debugged my code and added breakpoints, the checkedPeople map has the correct mapping of people to true for each of the elements in selectedPeople which shows that the checkPeople method is working as expected. Therefore the issue must be with the [checked] attribute if I'm not mistaken. I cannot see why it wouldn't work though.
You should use [ngModel]="checkedPeople.get(value)"
instead of [checked]="checkedPeople.get(value)"
to initialize the checkbox and
(change)="checkUncheckPeople(value, $event)"
to update the value when you check or uncheck it, where
checkUncheckPeople(value, e) {
this.checkedPeople.set(value, e.target.value);
}
So, in conclusion, your HTML input element will be:
<input
type="checkbox"
[ngModel]="checkedPeople.get(value)"
(change)="checkUncheckPeople(value, $event)"
/>
If you choose to use an object instead of a map then you can also directly use
[(ngModel)]="checkedPeople[value]"
to two-way bind the variable
Currently I'm trying to create this design.
It doesn't open and close as expected.
I also created a codesandbox
Three things:
1. I have a onClick, but I'm not sure if my logic is correct to open and close the button.Also should there be a useEffect here to listen to changes?
const showPlatformOptions =()=> {
// let checkboxes = el;
// console.log("ref:",myRef.current)
// if (!expanded) {
// //checkboxes.style.display = "block";
// setExpanded(true);
// } else {
// // checkboxes.style.display = "none";
// setExpanded(false);
// }
}
I have a onChange called selectionOptions that should let me know which platforms are selected, but it currently only shows one platform at a time, why?
Is there another way to create this dropdown and checkbox. Maybe a library using hooks?
Any help is appreciated.
import React, { useState,useEffect, useRef} from "react";
const SearchBar =()=>{
const [query, setQuery] = useState("");
const [expanded,setExpanded] = useState(false);
const [selectionOptions, setSelectionOptions] = useState(["Instagram","LinkedIn","Twitter"]);
const myRef = useRef(null);
const showPlatformOptions =()=> {
// let checkboxes = el;
// console.log("ref:",myRef.current)
// if (!expanded) {
// //checkboxes.style.display = "block";
// setExpanded(true);
// } else {
// // checkboxes.style.display = "none";
// setExpanded(false);
// }
}
const handleQueryChange = event => {
console.log("Event:",event.target.value)
setQuery(event.target.value);
};
return (
<form onSubmit={showPlatformOptions}>
<div className="w-64">
<div className="relative" onClick={showPlatformOptions}>
<h6>PLATFORMS </h6>
<select className="w-full font-semibold" onChange={handleQueryChange}>
{selectionOptions.map((platform,x) => (
<option key={x} value={platform}>
{platform}
</option>
))}
</select>
<div className="absolute left-0 right-0 top-0 bottom-0"></div>
</div>
<div
ref={myRef}
className="checkboxes border-gray-200 border border-solid">
<label htmlFor="one" className="block ">
<input type="checkbox" id="one" onChange={handleQueryChange} className="m-3"/>
Instagram</label>
<label htmlFor="two" className="block">
<input type="checkbox" id="two" onChange={handleQueryChange} className="m-3"/>
LinkedIn</label>
<label htmlFor="three" className="block">
<input type="checkbox" id="three" onChange={handleQueryChange} className="m-3"/>
Twitter</label>
</div>
</div>
</form>
);
}
You're really close with your logic and code so far so well done! I'll answer your questions in order:
I have a onClick, but I'm not sure if my logic is correct to open and close the button.Also should there be a useEffect here to listen to changes?
Yep your logic to toggle the hide and show of the dropdown is spot on. The only thing is you don't have to worry about CSS there. You can use this boolean to display or hide the dropdown when the state changes.
I have a onChange called selectionOptions that should let me know which platforms are selected, but it currently only shows one platform at a time, why?
Good question, the reason for this is because its firing on the change of "each" checkbox. As you change checkboxes you'll see the event fire and just show the value for the new checkbox. The checkbox itself is what the event.target refers to. Not the whole form
Is there another way to create this dropdown and checkbox. Maybe a library using hooks?
I don't think you'll need to in this case. I took your code sandbox and enhanced it with just a couple additional bits of code to show how close you were!
Here it is
I'll point out a couple of changes i made:
Instead of dealing with CSS I used your expanded variable to determine if I should render the dropdown. This conditional logic is on line 50.
Because you want to keep track of all the different queries I changed the query variable to keep track of which queries were selected in an array. The logic for this is on line 26, but basically if the user unticks a checkbox I remove it from the array (if its there) and if they check it I add it to the array (if its not there).
I hope this helps you out!
The answer given by Code Novitiate is really good here to solve the bugs in your existing code 👍 This is a slightly different approach that simplifies the code.
Currently you are trying to rendering a select and separately rendering checkboxes. The select and its contents are never actually used, asides of visually to show the dropdown icon.
Personally, I find the native select tag hard to work with, particularly when you want to select multiple options. Most select libraries seem to create their own component out of divs and style them to look and feel like a select, which I think might be the best approach here as well 😀
Approach
Conditionally render the checkboxes based on whether the dropdown has been expanded
Keep track of selections in local state
Update state whenever one of the checkboxes is clicked
Render selections in the dropdown header
Style dropdown header to look like a select
In my example I added a little triangle in css, but you would use something that better matches the designs you have
Example
I put together this CodePen which is hopefully useful for you
Other suggestions
react-select is a really good library for doing what you would like, take a look at the Multi example.
Given that selectionOptions never changes, there's no reason to hold that as state. You can just declare an array instead. In my example, this is the PLATFORMS constant.
remove <div className="absolute left-0 right-0 top-0 bottom-0" />. It looks like the effect is to create a div that covers the whole area? This gave me troubles because it interferes with some click events 😅
All you want to have a dropdown of with checkbox for Each element. Please refer to Sandbox
to understand the making of this whole dropdown.
the approach you are using is not Right and bit complex and not reusable. In react the whole idea is to create common reusable components but your code is very tightly coupled to your use-case. There is no point in creating a "Select" and then separately creating a list of checkboxes.
Checkboxes should appear as a child to you dropdown and not outside it, making it very easy for you to manage dropdown and its state.
I am attempting to use a checkbox in Angular in order to toggle the contents of an array. I am using property booleans to determine if 3 different boxes are checked and adjust the contents of the array accordingly. The array defaults to containing all 3 subarrays via declaring this.allNouns in ngOnInit(). When I click the first checkbox, I can see in the console that it is running the associated onchange function and setting the associated boolean to its opposite, but the interpolated string in the view does not change. When I click it again, it will change the view but now everything is one step behind. Also when it hits the console logs, it appears to load the prior length and not the length of the newly established this.allNouns. Below are the html and ts snippets associated with it. This is the first of the three checkboxes, but I plan to apply it to all when smoothed out.
TS
preloadedNouns: Boolean = true;
toggleData() {
if(this.preloadedNouns==false){
this.allNouns=this.nouns.concat(this.onlyUserNouns);
console.log('hitting first condition in toggledata', this.allNouns.length);
}
else {
this.allNouns=this.nouns.concat(this.stockNouns).concat(this.onlyUserNouns);
console.log('hitting second condition in toggledata', this.allNouns.length);
}
}
HTML
<div name="allNouns" [(ngModel)]="allNouns" [ngModelOptions]="{standalone: true}" ngDefaultControl>{{allNouns.length}}</div>
<div class="toggleDiv">
<label>Preloaded Nouns</label>
<input id="preloadedNounsCheck"
type="checkbox"
[checked]="preloadedNouns"
(change)="toggleData(); preloadedNouns = !preloadedNouns">
</div>
i don't clear about your nouns logic.
<div class="toggleDiv">
<label>Preloaded Nouns</label>
<input id="preloadedNounsCheck"
type="checkbox"
[checked]="preloadedNouns"
(change)="preloadedNouns = !preloadedNouns;toggleData()">
</div>
but i hope based on my understanding above code will help to you.
just reorder the function call in (change).
Trying to implement a add/remove sub-item entry; increment/decrement buttons add slots into the array and input fields are added/removed automatically:
<div *ngFor="let item of itemsInNewOrder; let i = index">
<input [(ngModel)]="itemsInNewOrder[i]" xtype="text" name="order" title="order" />
</div>
This is working functionally, but every time a letter is entered into the input, the element is deselected and must be clicked again to enter yet one more letter. How can I solve this?
http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
The link above explains change detection in Angular 2. It also describes some other change detection strategies that you might find useful.
Basically since you did a 2 way binding [(ngModel)]="itemsInNewOrder[i]" to the itemsInNewOrder array, Angular is trying to be helpful and update the view for you.
Each keystroke updates your model (itemsInNewOrder)
Angular detects the update
Angular renders the view again with new form controls
Again, these are new form controls so the user hasn't focused one yet. This is why you have to click into the control after every entered character.
You can fix this by removing your 2 way binding and instead listening to the blur event for each input or implementing something described in the link above.
Blur event listening example:
<input (blur)="blurHandler(item,i)" type="text" name="order" title="order" />
Component
blurHandler(val:string,ndx:number) {
this.itemsInNewOrder[ndx] = val;
}
This might help too
Angular 2 change event - model changes
You can also wrap the values into objects. I don't know why you don't lose focus if you do it like this.
Component:
itemsInNewOrder = [{value: 'Soda'}, {value: 'Burger'}, {value: 'Fries'}];
template:
<div *ngFor="let item of itemsInNewOrder; let i = index">
<input
type="text"
name="order"
title="order"
[(ngModel)]="itemsInNewOrder[i].value"/>
</div>
The simplest answer to this was that I needed to learn and implement Angular 2's reactive forms library, which is part of Angular 2. Using this, I ran into none of difficulties I was experiencing.