Manipulate CSS from TypeScript in Angular - css

I'm working with Angular and have a calendar which is filled dynamically. Also I have a range of dates and I want to change the background for several days within this range. I'm using moment for handeling dates and moment-range for the date-range.
In TypeScript I have managed to check if a specific day is in the range.
const a = new Date(2023,0,8);
const start = new Date(2023, 0, 9);
const end = new Date(2023, 4, 23);
const range = moment.range(start, end);
console.log(range.contains(a));
But in the html I have only one 'day' which changes constantly. I have a css-class class="background" which changes my background when I write it in html but I can't access the specific days.
How can I only highlight the days which are in the range?
ts file
export class CalendarComponent implements OnInit {
week: any = [
"Mo",
"Tu",
"We",
"Th",
"Fr",
"Sa",
"Su"
];
monthSelect: any[] | undefined;
dateSelect: any;
title: string | undefined;
range: any;
constructor() {
}
ngOnInit(): void {
this.getDaysFromDate(1, 2023)
}
getDaysFromDate(month: number, year: number) {
const startDate = moment(`${year}/${month}/01`)
const endDate = startDate.clone().endOf('month')
this.dateSelect = startDate;
const diffDays = endDate.diff(startDate, 'days', true)
const numberDays = Math.round(diffDays);
this.monthSelect = Object.keys([...Array(numberDays)]).map((a: any) => {
a = parseInt(a) + 1;
const dayObject = moment(`${year}-${month}-${a}`);
return {
name: dayObject.format("dddd"),
value: a,
indexWeek: dayObject.isoWeekday()
};
});
}
changeMonth(flag: number) {
if (flag < 0) {
const prevDate = this.dateSelect.clone().subtract(1, "month");
this.getDaysFromDate(prevDate.format("MM"), prevDate.format("YYYY"));
} else {
const nextDate = this.dateSelect.clone().add(1, "month");
this.getDaysFromDate(nextDate.format("MM"), nextDate.format("YYYY"));
}
}
html file
<div class="wrapper-calendar">
<div class="header-calendar">
<div>
<button (click)="changeMonth(-1)" class="btn-prev"><img src="back.png" alt="back"></button>
</div>
<h1>{{dateSelect | date:'MMMM yyyy'}}</h1>
<div>
<button (click)="changeMonth(1)" class="btn-next"><img src="forward.png" alt="forward"></button>
</div>
</div>
<ol>
<li *ngFor="let day of week" class="day-name">{{day | slice:0:3}}</li>
<li [style.gridColumnStart]="first ? day?.indexWeek : 'auto'"
*ngFor="let day of monthSelect; let first = first">
<span> {{day?.value}}</span>
</li>
</ol>
<br>
</div>

A possible solution will be to add additional property to the day entires, something like the following
...
getDaysFromDate(month: number, year: number) {
...
this.monthSelect = Object.keys([...Array(numberDays)]).map((a: any) => {
a = parseInt(a) + 1;
const dayObject = moment(`${year}-${month}-${a}`);
return {
name: dayObject.format("dddd"),
value: a,
indexWeek: dayObject.isoWeekday(),
selected: someFunctionThatReturnsBooleanBasedOnTheSelectionRange(a)
};
});
}
...
The someFunctionThatReturnsBooleanBasedOnTheSelectionRange should determine if the day is in the selected range, latter in the HTML you should add the following
<li [style.gridColumnStart]="first ? day?.indexWeek : 'auto'"
[ngClass]="{'selected-day-backround': day.selected}"
*ngFor="let day of monthSelect; let first = first">
<span> {{day?.value}}</span>
</li>
By using the ngClass directive you can add the custom background only to HTML elements that are part of the range.

Related

Vue 3 ElTreeSelect Element Plus

I'm working on a personal project using Vue3, Nuxt3 and Element Plus. I'm a beginner with JS/TS and Vue3 and I'm having a problem with the Element Plus TreeSelect Component. I am trying to have the TreeSelect Components selected value be an object but it's always the label field of the object that is selected. Here is my components code.
<template>
<div>
<ClientOnly>
<el-select v-model="selectedEntityDefinitionRef">
<el-option v-for="entityDefinition in scheme.schemeDefinition.entityDefinitions"
:key="entityDefinition.label" :label="entityDefinition.label" :value="entityDefinition">
</el-option>
</el-select>
<el-tree-select check-strictly :render-after-expand="false" show-checkbox check-on-click-node
value-key="label" :data="schemeEntitiesRef" v-model="selectedParentEntityRef"/>
</ClientOnly>
<el-button style="margin-top: 12px" #click="printValues">Add</el-button>
</div>
</template>
<script lang="ts" setup>
import { ElTreeSelect, ElSelect, ElOption, ElButton } from 'element-plus';
import { ref, reactive } from 'vue';
class SchemeDefinition {
label: string
entityDefinitions: Array<EntityDefinition> = new Array<EntityDefinition>()
constructor(label: string) {
this.label = label
}
}
class EntityDefinition {
label: string
constructor(label: string) {
this.label = label
}
}
class Scheme {
schemeDefinition: SchemeDefinition
entities: Array<Entity> = new Array<Entity>()
}
class Entity {
label: string
parent: Entity
children: Array<Entity> = new Array<Entity>()
constructor(label: string) {
this.label = label;
}
}
const schemeDefinition: SchemeDefinition = new SchemeDefinition('Scheme Definition');
const fondsEntityDefinition: EntityDefinition = new EntityDefinition('Entity Definition')
schemeDefinition.entityDefinitions.push(fondsEntityDefinition)
const scheme: Scheme = new Scheme()
scheme.schemeDefinition = schemeDefinition
scheme.entities.push(new Entity('Entity'))
const selectedEntityDefinitionRef = ref<EntityDefinition>()
const selectedParentEntityRef = ref<Entity>()
const schemeEntitiesRef = reactive(scheme.entities)
const printValues = () => {
console.log(selectedEntityDefinitionRef.value)
console.log(selectedParentEntityRef.value)
}
</script>
My question is if I am doing something fundamentally wrong here or is my issue a limitation of the component itself? As a newbie any advice or guidance would be greatly appreciated. Thanks

How to style each element of ngFor individually based on its value?

I have a console on the screen that I log things to. The component looks like this.
export class AppComponent { logs: string[]; ... }
<div *ngFor="let log of logs" class="log-type-a">{{log}}</div>
I'd like to set a class on each of the DIVs dynamically. At the moment, each of those is log-type-a but I'd prefer it to be log-type-b when the string contains a certain value (e.g. it starts with "b", so that the class would be log-type-first_char_of_log.
I'm not sure what to google for to being with. I've tried horsing around with ngClass but failed due to ignorance and uncertainty on how.
NgFor can have an index, like this one: *ngFor="let item of items; index as i;"
You could use that index in order to set different classes for your items, like class="log-type-{{ i }}".
https://angular.io/api/common/NgForOf
You can use expresion in ngClass like this:
export class AppComponent { logs: string[]; ... }
<div *ngFor="let log of logs" [ngClass]="'log-type-' + log">{{log}}</div>
If your logs array has lets say: red and blue
the class output should be:
log-type-red
log-type-blue
OR
you can use functions like this and make decisions depending on the log value:
<div *ngFor="let log of logs" [ngClass]="'class-test-' + ngClassConvert (log)">
My log value: {{ log }}
Converted in cssClass: {{ ngClassConvert (log)}}
</div>
and the component:
export class AppComponent implements OnInit {
name = 'Angular';
logs: string[];
ngClassConvert(value):string{
let returnValue = '';
switch (value){
case 'a': {
returnValue = 'Apple';
break;
}
case 'b': {
returnValue = 'Banana';
break;
}
case 'c': {
returnValue = 'Cherry';
break;
}
case 'd': {
returnValue = 'dddd';
break;
}
default: {
returnValue = 'default';
break;
}
}
return returnValue;
}
ngOnInit(): void {
this.logs = ['a', 'b', 'c', 'd'];
}
}
Also and Demo
Based on the answer of SehaxX: You could parse the list of logs using a setter. This will save you function calls in the template.
private _logs: string[];
public parsedLogs: any[];
set logs(value: string[]) {
this._logs = value;
this.parsedLogs = this.parseLogs(value);
}
private parseLogs(logs: string[]): any[] {
let parsed = [];
for (let i = 0; i < logs.length; i++){
parsed.push({
value: logs[i],
style: this.ngClassConvert(logs[i])
});
}
return parsed;
}
Demo

Picking an item from Firebase on button click

I'm working with Angular 6 and Firebase and what I need is to get all items from the firebase and then display only one at a time till next button click
/*this is the .ts file*/
constructor( public af: AngularFire, public db: AngularFireDatabase) {
this.items = db.list('items').valueChanges();
db.list('/sentences').valueChanges().subscribe(sentences => {
this.sentences = sentences;
console.log(this.sentences);
});
}
<!--this in the html-->
<div>
<ul class="sent">
<li *ngFor="let sentence of sentences"
[class.selected]="sentence === selectedsentence"
(click)="onSelect(sentence)">{{sentence}}
<!-- this is displaying all the items in the firebase; i need only one at a time-->
</li>
</ul>
</div>
//in the ts file
constructor( public db: AngularFireDatabase) {
this.items = db.list('items').valueChanges();
db.list('/sentences').valueChanges().subscribe(sentences => {
this.sentences = sentences;
console.log(this.sentences);
this.labels = this.sentences; //in this.labels we have all the elements *declaration : labels=[]
console.log(this.labels);
});
onSkip($i) {
if (this.i > 13) { //if there are 13 elements in the database
this.i = 0;
}
this.i = this.i + 1;
console.log(this.i);
}
// in the html code
{{labels[i]}}
<button class="centered" (click)="onSkip(i)" > SKIP <a routerLink="skip">

How to update reactivevar dependent on a framerate?

I have problems understanding how exactly meteor's ReactiveVar works. What i think i should do :
Template.template_canvas.onCreated(function() {
this.config = new ReactiveVar([{
name: 'FPS',
value: Engine.mFPS
}, {
name: 'ElapsedTime',
value: Engine.mElapsedTime
}, {
name: 'LagTime',
value: Engine.mLagTime
}, {
name: 'PreviousTime',
value: Engine.mPreviousTime
}]);
});
Template.template_canvas.helpers({
displayConfigs: function() {
return Template.instance().config.get();
}
});
ALL Engine variables are updated every 40ms.
And when i try to display them :
<template name="template_canvas">
<div id='info_log' draggable="true" >
<ul>
{{#each displayConfigs}}
<li> {{name}} {{value}} </li>
{{/each}}
</ul>
</div>
<canvas id='canvas_app'></canvas>
</template>
ONLY the default variables of Engine are displayed.
Which means the variables arent changing at all.
However they are changing according to console.log !
EDIT :
export default class Engine {
static start(application) {
Engine.Application = application;
Engine.mPreviousTime = Date.now();
Engine.mLagTime = 0.0;
Engine.mIsRunning = true;
requestAnimationFrame(function() {
Engine.run.call(Engine.Application);
});
}
static run() {
if (Engine.mIsRunning) {
requestAnimationFrame(
function() {
Engine.run.call(Engine.Application);
}
);
Engine.mCurrentTime = Date.now();
Engine.mElapsedTime = Engine.mCurrentTime - Engine.mPreviousTime;
Engine.mPreviousTime = Engine.mCurrentTime;
Engine.mLagTime += Engine.mElapsedTime;
// LagTime is the sum of the elapsed Time based on the last Frame.
// So if the time passed is bigger than the given time, which bigger than milliseconds per frame
// then we have to update.
while ((Engine.mLagTime >= Engine.mMPF) && Engine.mIsRunning) {
this.update();
Engine.mLagTime -= Engine.mMPF;
}
// Should be called every 25ms, since we have 25ms per Frame.
//
this.draw();
}
}
}
// Frames per second.
//
Engine.mFPS = 40;
// Milliseconds per Frame 25ms/Frame with 40 FPS
Engine.mMPF = 1000 / Engine.mFPS;
Engine.mPreviousTime = 0;
Engine.mLagTime = 0;
Engine.mCurrentTime = 0;
Engine.mElapsedTime = 0;
Engine.mIsRunning = false;
Engine.Application = null;

Knockout js: Add Class to each nth item

Is there any simple way to do something like jquery i.e.
$('#image-gallery li:nth-child(3)').addClass('third-image-child')
but in the context of knockout, I am completely new to knockout but can't seem to find a simple way of doing something which seems like it should be so simple? I thought it might be something to do with finding the 3rd item in the observable array and adding the class but am not sure of the syntax. Help??!
My model is below, it is a simple pagination model, that loads 9 items, and then has next and previous buttons. For now I have added a simple function to generate 100 items just to test it out.
members.DisplayGallery = function(jsondata) {
var viewModel = {
fields: ko.observableArray(jsondata),
pageSize: ko.observable(9),
pageIndex: ko.observable(0),
previousPage: function() {
this.pageIndex(this.pageIndex() - 1);
},
nextPage: function() {
this.pageIndex(this.pageIndex() + 1);
}
};
viewModel.maxPageIndex = ko.dependentObservable(function() {
return Math.ceil(this.fields().length / this.pageSize()) - 1;
}, viewModel);
viewModel.pagedImages = ko.dependentObservable(function() {
var size = this.pageSize();
var start = this.pageIndex() * size;
return this.fields.slice(start, start + size);
}, viewModel);
ko.applyBindings(viewModel, $('#image-gallery')[0]);
};
$(function() {
var data = [];
for (var i = 0; i < 100; i++) {
data.push({
imageLink: "http://sample-image.jpg",
imagePageLink: "http://",
imageTitle: "Title here" + i,
userFullName: "Name" + i,
imageDate: "Description" + i
})
}
members.DisplayGallery(data);
});
Markup:
<ul data-bind="foreach: pagedImages" id="image-gallery">
<li>
<div class="image-thumb" data-bind="style: { backgroundImage: 'url(' + imageLink +')'}">
<a class="image-thumb-link" data-bind="attr: { href: imagePageLink}" href="gallery-single.html"></a>
</div>
<div class="image-text">
<a data-bind="attr: { href: imagePageLink}" href="gallery-single.html"><span class="image-title" data-bind="text: imageTitle"></span></a><br />
<span data-bind="text: userFullName">Username</span><br />
<span data-bind="text: imageDate">Image Date</span>
</div>
</li>
</ul>
If you bind to your observable array using the foreach binding, you can use the $index context property to set the class of every 3rd element, like so:
<li data-bind="css: { 'third-image-child': $index() % 3 == 0 }">
...
</li>

Resources