How to make if statement rerender component svelte - javascript

Here is what I have for my script :
function changeCart(subCat)
{
let addedCat = {id: subCat._id,name: subCat.name, name_categorie: subCat.name_categorie}
let exist = $cart.some(element => {
if(element.id === addedCat.id)
{
return true
}
})
if(exist)
{
$cart.some(element => {
if(element.id === addedCat.id)
{
$cart.splice($cart.indexOf(element, 1))
return true
}
})
}
else
{
$cart = [...$cart, addedCat]
}
console.log($cart)
}
and here is my html :
{#each subCategories as subCategories}
{#if ($cart.indexOf(subCategories.name) > -1)}
<div class="media ali list-group-item list-group-item-action clique selected" on:click={() => changeCart(subCategories)}>
<Management/>
<div class="media-body" >
<h4 class="media-heading">{subCategories.name}</h4>
</div>
</div>
{:else}
<div class="media ali list-group-item list-group-item-action clique" on:click={() => changeCart(subCategories)}>
<Management/>
<div class="media-body" >
<h4 class="media-heading">{subCategories.name}</h4>
</div>
</div>
{/if}
{/each}
I want to change this line :
{#if ($cart.indexOf(subCategories.name) > -1)}
The goal is to check if an object is already in $cart like the script part already do but I don't know how to make the modification for the if statement in my html

You can simply use the class:<name> Svelte directive. To add better readability, you could also make use of the new-ish #const block directive:
{#each subCategories as subCategory}
{#const selected = $cart.some(element => element.id === subCategory.id)}
<div class:selected class="media ali list-group-item list-group-item-action clique" on:click={() => changeCart(subCategory)}>
<Management/>
<div class="media-body" >
<h4 class="media-heading">{subCategory.name}</h4>
</div>
</div>
{/each}
Note: also changed the name of the currently iterated value to subCategory (don't name the iterator the same as the iteratee).

I found the solution : this is what I've done :
Script part :
function changeCart(subCat)
{
let addedCat = {id: subCat._id,name: subCat.name, name_categorie: subCat.name_categorie}
let exist = checkIfExist(subCat)
if(exist >= 0)
{
$cart = $cart.filter(item => item.id !== subCat._id)
}
else
{
$cart = [...$cart, addedCat]
}
console.log($cart)
}
function checkIfExist(cat)
{
for( let i = 0; i < $cart.length; i++)
{
if($cart[i].id == cat._id){
return i
}
}
return -1
}
and the html part :
{#key $cart}
{#each subCategories as subCategory}
<div class={`media ali list-group-item list-group-item-action ${checkIfExist(subCategory) >= 0 ? "selected" :""} clique`} on:click={() => {changeCart(subCategory)}}>
<Management/>
<div class="media-body">
<h4 class="media-heading">{subCategory.name}</h4>
</div>
</div>
{/each}
{/key}

Related

Ng If with a boolean - to change the title of a page

In my page Stock I have several fields with a title named Title 1.
If I do a console.log I am on my first page !
ngOnInit() {
this.currentPortfolio = this.shrd.getData('currentPortfolio');
this.subPage = 'stocks';
this.sectionTitle = "";
this.pageTitle = "Options";
for (var i = 0; i < this.optionsDropdownFilters.length; i++) {
this.issuers.actionsGrouped[this.optionsDropdownFilters[i]] = [];
}
console.log("First page => Tile 1 : " + JSON.stringify(this.subPage = 'stocks'));
this.initiate();
}
In Google Chrome I have this:
Now, I click on the button Search in my page Stock...
I want to change Title1 to Title2.
If I do a console.log, I am on my second page
launchSearchSelected(t) {
this.searched = true;
this.dateNotValid = false;
this.dateDoesntExist = false;
if (this.search.equityDate.length == 0) {
this.dateDoesntExist = true
}
if (t == 'A' && this.search.equityOptionCode > 0 && this.search.equityDate.length > 0) {
this.launchSearchResult(t, this.search.equityOptionCode, this.search.equityDate);
} else if (t == 'I' && this.search.indexOptionCode.length > 0 && this.search.indexDate.length > 0) {
this.launchSearchResult(t, this.search.indexOptionCode, this.search.indexDate);
}
console.log("Second Page => Title 2 " + JSON.stringify(this.searched = true));
}
In my HTML, I don't understand how can I switch from one title to another with the boolean?
<ng-container *ngIf="subPage === 'stocks' ">
<div class="breadcrumb d-flex justify-content-between">
<h1>Title 1 </h1>
</div>
</ng-container>
It's hard to tell what the best way to proceed is without more information, but if you just want to change the title based on the this.subPage variable having the value "stocks" or not (like it seems you are attempting), then you can do this:
<ng-container>
<div class="breadcrumb d-flex justify-content-between">
<h1 *ngIf="subPage == 'stocks'">Title 1</h1>
<h1 *ngIf="subPage != 'stocks'">Title 2</h1>
</div>
</ng-container>
I have the solution:
<ng-container *ngIf="subPage === 'stocks' ">
<ng-container *ngIf="spinners.search === true ">
<div class="breadcrumb d-flex justify-content-between">
<h1>Title 1 </h1>
</div>
</ng-container>
</ng-container>
<ng-container *ngIf="subPage === 'stocks' ">
<ng-container *ngIf="spinners.search === false ">
<div class="breadcrumb d-flex justify-content-between">
<h1>Title 2 </h1>
<button (click)="goBack()" class="btn btn-primary"> Back
</button>
</div>
</ng-container>
</ng-container>

OnClick() handler in map function is changing the state of all elements instead of only clicked element

I am creating a Next js site mcqs and answer.
Initially only questions are rendered with the title and options.
There is a button to show the answer of given question but when button is clicked it is changing the state of all elements in the map function instead of element that is clicked.
export default function Chapter({ chapter }) {
const [right, setRight] = useState(false)
function handleOnClick() {
setRight(!right)
}
<main className='main_q'>
<h1>{chapter.items[0].name}</h1>
{chapter.items[0].questions.items.map((question, index) => {
return (
<div key={question.id} className='question_container'>
<div className='question_q_t'>
<div className='question_q'>Q</div>
<div className='question_t'>
<Markdown>{question.title}</Markdown>
</div>
</div>
<div className='option_container'>
<div className='option_a_o'>
<div className='option_a'>A</div>
<div className='option_o'>
<Markdown>{question.optionA}</Markdown>
</div>
</div>
<div className='option_a_o'>
<div className='option_a'>B</div>
<div className='option_o'>
<Markdown>{question.optionB}</Markdown>
</div>
</div>
<div className='option_a_o'>
<div className='option_a'>C</div>
<div className='option_o'>
<Markdown>{question.optionC}</Markdown>
</div>
</div>
<div className='option_a_o'>
<div className='option_a'>D</div>
<div className='option_o'>
<Markdown>{question.optionD}</Markdown>
</div>
</div>
</div>
<a
className='solution_link'
target='_blank'
href={`/${question.chapter.subject.category.slug}/${question.chapter.subject.slug}/${question.chapter.slug}/${question.id}`}
>
See Solution
</a>
<button onClick={handleOnClick}>Answer</button>
{right && <div>{question.rightAnswer}</div>}
</div>
)
})}
</main>
}
Put each question div into its own component, and make a right state in that component:
const Question = ({ question }) => {
const [right, setRight] = useState(false)
return (
<div key={question.id} className='question_container'>
// etc
Or make an array of right states in the parent:
export default function Chapter({ chapter }) {
const qs = chapter.items[0].questions.items;
const [rights, setRights] = useState(qs.map(q => false));
const makeHandleClick = (i) => {
setRights(
rights.map((r, j) => j === i ? r : !r)
);
};
// ...
<button onClick={makeHandleClick(i)}>Answer</button>
{rights[i] && <div>{question.rightAnswer}</div>}

How to filter an Array with Checkboxes items?

I want to filter my checkboxes I search it on internet there was information but I couldn't work it with my code.
This is the webpage
I want when you click on the checkbox it must be same as the category.
This is some code of the checkbox:
<div>
<input class="form-check-input checkboxMargin" type="checkbox" value="All" v-model="selectedCategory">
<p class="form-check-label checkboxMargin">All</p>
</div>
This is my grey box layout:
<div class="col-sm-12 col-md-7">
<div class="card rounded-circle mt-5" v-for="item of items" :key="item['.key']">
<div>
<div class="card-body defaultGrey">
<h5 class="card-title font-weight-bold">{{ item.name }}</h5>
<div class="row mb-2">
<div class="col-sm ">
<div class="row ml-0"><h6 class="font-weight-bold">Job:</h6><h6 class="ml-1">{{ item.job }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Category:</h6><h6 class="ml-1">{{ item.categories }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Location:</h6><h6 class="ml-1">{{ item.location }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Niveau:</h6><h6 class="ml-1">{{ item.niveau }}</h6></div>
<div class="row ml-0"><h6 class="font-weight-bold">Availability:</h6><h6 class="ml-1">{{ item.availability }}</h6></div>
<h6>{{ item.info }}</h6>
<div class="row ml-0"><h6 class="font-weight-bold">Posted:</h6><h6 class="ml-1">{{ item.user }}</h6></div>
</div>
</div>
<div class="row">
<div class="col-xs-1 ml-3" v-if="isLoggedIn">
<router-link :to="{ name: 'InternshipDetails', params: {id: item['.key']} }" class="btn bg-info editbtn">
Details
</router-link>
</div>
<div class="col-xs-1 ml-3 mr-3" v-if="isLoggedIn && item.user == currentUser">
<router-link :to="{ name: 'Edit', params: {id: item['.key']} }" class="btn btn-warning editbtn">
Edit
</router-link>
</div>
<div class="col-xs-1" v-if="isLoggedIn && item.user == currentUser">
<button #click="deleteItem(item['.key'])" class="btn btn-danger dltbtn">Delete</button>
</div>
</div>
</div>
</div>
</div>
</div>
And I have my object here but how can I filter my grey boxes with category:
selectedCategory: []
Use computed properties:
computed: {
filteredItems(){
if(this.selectedCategory.length == 0) return this.items;
return this.items.filter(el => {
for(let i = 0; i < this.selectedCategory.length; i++){
if(el.categories == this.selectedCategory[i]) return el;
}
});
}
}
Then you go for v-for="item of filteredItems"
I didnt tested it. If you provide me more code i could help you more
You need to create one-way binding between your CategoryCheckBox component and your ListCard component.
Because you provided separated code when I can not reproduce it to give you a solution based on your own, I suggest this example to explain my solution.
Step One:
You have many ways to CRUD your items using a Plugins or Vuex or global instance, in my example I used global instance in main.js
Vue.prototype.$myArray = ["Books", "Magazines", "Newspaper"];
I assume you created your items data in the ListCards component
Step Two:
You need to add #change event in your checkbox to handle checked and unchecked states. I used static data (Book) for value.
<label>
<input type="checkbox" value="Books" #change="handleCategory($event)" /> Books
Now, let's implement handleCategory method, but before that, as we know that Checkbox and ListCards are independents which means we need to define a bus to create event communication between them and this is your issue so we define the bus inside the main.js
Vue.prototype.$bus = new Vue({});
Now we define handleCategory like this :
methods: {
handleCategory(e) {
this.$bus.$emit("checkCategory", e);
}
}
Step Three:
How can our ListCards listen to this event ?
By call $bus.$on(..) when the component is created ( Hope you know what Vue lifecycle methods mean )
created() {
this.$bus.$on("checkCategory", e => this.checkCategory(e));
},
When the user click the check box the component runs handleCategory then runs checkCategory inside ListCard.
Inside my ListCard, I have categories and originalCategories as data
data() {
return {
categories: this.$myArray,
originalCategories: this.$myArray
};
},
and a template :
<ul v-for="(category,index) in categories" :key="index">
<CategoryItem :categoryName="category" />
</ul>
and a created method ( lifecycle )
created() {
// $bus is a global object to communicate between components
this.$bus.$on("checkCategory", e => this.checkCategory(e));
},
and our filtering methods :
methods: {
checkCategory(e) {
let target = e.target;
let value = e.target.value;
target.checked
? this.filterCatergories(value)
: (this.categories = this.originalCategories);
},
filterCatergories(value) {
this.categories = this.categories.filter(val => val === value);
}
}
What's important for you is :
this.categories.filter(val => val === value); //will not do nothing
const categories=this.categories.filter(val => val === value); //will update the view.
And you can change the code or make it better and simple.
#Update
You can also use computed properties but because we have a parameter here which is the category name we need get and set.
computed: {
filteredItems: {
get: function() {
if (this.selectedCategory.length == 0) return this.items;
return this.items.filter(value => {
for (const category of this.selectedCategory) {
if (value == category) return value;
}
});
},
set: function(category) {
this.selectedCategory.push(category);
}
}
},
methods: {
checkCategory(e) {
let target = e.target;
let value = e.target.value;
target.checked
? (this.filteredItems = value)
: this.selectedCategory.pop(value);
}
}

Adding a selected list entry to a new list entry

How can I move the selected list entry by clicking button and move it to a new list entry
<pre className="reverse-list">
{datas.map((data, i) =>
<li key={i} className="list-group-item text-capitalize d-flex
justify-content-between my-2">
<div className="todo-icon">
<span>
<button></button>
</span>
</div>
<h6 className="mt-2">{data.input}</h6>
<div className="todo-icon">
<span className="mx-2 text-success">
<i className="fas fa-pen" onClick={()=>this.fEdit(i)</i>
</span>
<span className="mx-2 text-danger">
<i className="fas fa-trash" onClick={()=>this.fRemove(i)</i>
</span>
</div>
</li>
)}
</pre>
Methods used
constructor(props){
super(props);
this.state={
title: 'Todo List',
act: 0,
index: '',
datas: []
}
}
componentDidMount(){
this.refs.input.focus();
}
What should i add in the fcomplete method to get the required output?
fSubmit = (e) =>{
e.preventDefault();
console.log('try');
let datas = this.state.datas;
let input = this.refs.input.value;
if(this.state.act === 0){ //new
let data = {
input
}
datas.push(data);
}else{ //update
let index = this.state.index;
datas[index].input = input;
}
this.setState({
datas: datas,
act: 0
});
this.refs.myForm.reset();
this.refs.input.focus();
}
fComplete = (i) => {
}
fRemove = (i) => {
let datas = this.state.datas;
datas.splice(i,1);
this.setState({
datas: datas
});
this.refs.myForm.reset();
this.refs.input.focus();
}
fEdit = (i) => {
let data = this.state.datas[i];
this.refs.input.value = data.input;
this.setState({
act: 1,
index: i
});
this.refs.input.focus();
}
I have tried using methods in fcomplete(). But I was not able to get the task done.I have added the complete code.
csccscsccs
csscscsccssc
If I understand you correctly, you want to display your list in such a way that the items marked as completed are displayed at the bottom of the list. My below answer is based on this assumption only.
Like I mentioned in the comments, you have to store your list in a different way:
fSubmit = (e) =>{
e.preventDefault();
console.log('try');
let datas = this.state.datas;
let input = this.refs.input.value;
if(this.state.act === 0){ //new
let data = {
id: (Math.floor(Math.random() * 100) + 1), // for generating random ids
value: input,
isComplete: false // you have to make this change here while creating the data item
}
datas.push(data);
}else{ //update
let index = this.state.index;
datas[index].input = input;
}
this.setState({
datas: datas,
act: 0
});
this.refs.myForm.reset();
this.refs.input.focus();
}
fComplete = (id) => {
// use id to update the list
// use this in fEdit & fRemove
const updatedDataList = this.state.datas.map(data => {
if(data.id === id) {
data.isComplete = true
}
return data;
});
this.setState({
datas: updatedDataList
});
}
render() {
const incompleteList = datas.filter(data => data.isComplete === false);
const completeList = datas.filter(data => data.isComplete === true);
...
...
...
}
Display the two lists separately. (I don't understand your css classes, that you can see according to your design.) The idea is to display them in two separate lists to make it simple. Or otherwise you have to always modify your "datas" list and keep the incomplete list on top and append the completed data items at the end.
<pre className="reverse-list">
{incompleteList.map((data, i) =>
<li key={i} className="list-group-item text-capitalize d-flex
justify-content-between my-2">
<div className="todo-icon">
<span>
<button onClick={() => this.fComplete({data.id})}></button>
</span>
</div>
<h6 className="mt-2">{data.input}</h6>
<div className="todo-icon">
<span className="mx-2 text-success">
<i className="fas fa-pen" onClick={()=>this.fEdit({data.id})</i>
</span>
<span className="mx-2 text-danger">
<i className="fas fa-trash" onClick={()=>this.fRemove({data.id})</i>
</span>
</div>
</li>
)}
{completeList.map((data, i) =>
<li key={i} className="list-group-item text-capitalize d-flex
justify-content-between my-2">
<div className="todo-icon">
<span>
<button></button>
</span>
</div>
<h6 className="mt-2">{data.input}</h6>
<div className="todo-icon">
<span className="mx-2 text-success">
<i className="fas fa-pen" onClick={()=>this.fEdit(i)</i>
</span>
<span className="mx-2 text-danger">
<i className="fas fa-trash" onClick={()=>this.fRemove(i)</i>
</span>
</div>
</li>
)}
</pre>

How to remove the child when removing the parent?

I am making angular application with angular dynamic form where i am using ng-select library.
The HTML with select:
<div *ngFor="let question of questions" class="form-row {{question.class}}">
<ng-container *ngIf="question.children">
<div [formArrayName]="question.key" class="w-100">
<div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i" class="row mt-1">
<div *ngFor="let item of question.children" class="{{item.class}} align-middle">
<div class="w-100">
<dynamic-form-builder [question]="item" [index]="i" [form]="form.get(question.key).at(i)"></dynamic-form-builder>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-6 col-sm-12 col-lg-6 col-md-6">
<div class="form-label-group" *ngIf="showTemplateDropdown">
<ngi-select placeholder="Select Template" [required]="true" [hideSelected]="false" [multiple]="true" [items]="templateList"
dropdownPosition="down" bindLabel="name" bindValue="id" (add)="getTemplateValues($event)" (remove)="onRemove($event)">
</ngi-select>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-6 col-sm-12 col-lg-6 col-md-6">
</div>
<div class="col-6 col-sm-12 col-lg-6 col-md-6 text-right">
<div class="btn-group float-right">
<button class="btn btn-primary btn-round btn-fab mat-raised-button" mat-min-fab="" mat-raised-button="" type="button"
(click)="addControls('template_properties')">
<span class="mat-button-wrapper"><i class="material-icons mt-2">add</i></span>
<div class="mat-button-ripple mat-ripple" matripple=""></div>
<div class="mat-button-focus-overlay"></div>
</button>
<button class="btn btn-primary btn-round btn-fab mat-raised-button" mat-min-fab="" mat-raised-button="" type="button"
(click)="removeControls('template_properties')">
<span class="mat-button-wrapper"><i class="material-icons mt-2">remove</i></span>
<div class="mat-button-ripple mat-ripple" matripple=""></div>
<div class="mat-button-focus-overlay"></div>
</button>
</div>
</div>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="!question.children">
<div class="w-100">
<dynamic-form-builder [question]="question" [form]="form"></dynamic-form-builder>
</div>
</ng-container>
</div>
Here the [items]="templateList" has the following,
[{"id":"5bebba2c20ccc52871509d56","name":"Template One"},
{"id":"5bebba5720ccc52871509d57","name":"Template Two"},
{"id":"5bebba8d20ccc52871509d5d","name":"Template Three"}]
I am having (change)="getTemplateValues($event)" event for detecting each change happen when we select an item from dropdown.
The change event function Edited,
getTemplateValues(e) {
this.dynamicFormService.getRest("url" + '/' + e.id").subscribe(res => {
try {
if (res.status === "true") {
res.data.template_properties.forEach(element => {
this.templateArray.push(element);
});
this.form = this.qcs.toFormGroup(this.questions);
for (let i = 0; i < this.templateArray.length; i++) {
this.addControls('template_properties');
}
let propertiesArray = [];
this.templateArray.forEach(element => {
propertiesArray.push(element);
});
this.form.patchValue({
'template_properties': propertiesArray
});
} else {
}
}
catch (error) {
}
})
}
console.log(this.propertiesArray) gives the following,
[{"property_name":"Property one","property_type":4,"property_required":true,"property_origin":1},{"property_name":"Property one","property_type":5,"property_required":true,"property_origin":1}]
In the below image i have deleted template three but the template three properties still showing in it..
Here first i am filtering the data first and ignoring the duplicates and each time i am sending the newly selected values alone to the service and fetching the data related to the id element.id.
And using this.addControls('template_properties') to make open the number of rows, and elements will get patched to the form template_properties.
this.form.patchValue({
'template_properties': propertiesArray
});
As of now everything working fine..
The problem actually arise from here:
If we delete a selected list from dropdown, (say i have selected all three template and i have deleted the template two then that particular template's template_properties needs to get deleted..
I have tried with (remove)="onRemove($event)" but its not working because while remove data, the (change) function also calls..
How can i remove the template_properties with this.removeControls('template_properties'); of particular deleted template name in the change event or remove event..
Remove Function:
onRemove(e) {
console.log(e);
this.dynamicFormService.getRest("url" + '/' + e.value.id").subscribe(res => {
try {
if (res.status === "true") {
for (let i = 0; i < res.data.template_properties.length; i++) {
this.removeControls('template_properties');
}
} else {
}
}
catch (error) {
}
})
}
Remove Control:
removeControls(control: string) {
let array = this.form.get(control) as FormArray;
console.log(array)
array.removeAt(array.length);
}
console.log(array) gives,
It should be pretty easy fix. Use (add) instead of (change) in ngi-select.
onRemove(e) {
console.log(e);
this.dynamicFormService.getRest("url" + '/' + e.value.id").subscribe(res => {
try {
if (res.status === "true") {
this.form = this.qcs.toFormGroup(this.questions);
// Issue is here, you should remove only specific record
// which is being passed from function `e`
for (let i = 0; i < res.data.template_properties.length; i++) {
this.removeControls('template_properties');
}
let propertiesArray = [];
this.templateArray.forEach(element => {
propertiesArray.push(element);
});
this.form.patchValue({
'template_properties': propertiesArray
});
} else {
}
}
catch (error) {
}
})
}
Pass the index in removeControls where you want to remove the element from.
removeControls(control: string, index:number) {
let array = this.form.get(control) as FormArray;
console.log(array)
array.removeAt(index);
}
console.log(array) gives,

Categories