I am receiving from the server a date in UTC format (as a string) = "2020-04-01T00:00:00Z".
When my users are visiting the calendar here in the UK (gmt) they can see it fine. However my users in the USA of course see the calendar pre-selecting 31st March.
How can I make my users in the USA also see the calendar pre-selecting 1st April?
I'm aware that PrimeNG removed the UTC="true" attribute.
See if this works:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
dates: UTCDate[] = [];
days: UTCDate[] = [];
constructor() {
for (var i = 0; i < 24; i+= 3) {
this.dates.push(new UTCDate(new Date(Date.UTC(2018, 0, 1, i))));
}
for (var j = 1; j < 28; j+= 3) {
this.days.push(new UTCDate(new Date(Date.UTC(2018, 0, j))));
}
}
}
class UTCDate {
private boundDateBacker: Date;
constructor(private dateInstance: Date) {
this.boundDateBacker = this.utcToLocal(dateInstance);
}
public get boundDate(): Date {
return this.boundDateBacker;
}
public set boundDate(value: Date) {
if (value) {
this.dateInstance.setTime(this.localToUtc(value).getTime());
const newTime = value.getTime();
if (newTime !== this.boundDateBacker.getTime()) {
this.boundDateBacker.setTime(newTime);
}
}
}
private localToUtc(date: Date): Date {
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
}
private utcToLocal(date: Date): Date {
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
}
}
I didn't write it, I found it while looking for a solution to the utc issue. I haven't tested it because I don't need to implement timezone support yet, but thought you could have a look at this solution and maybe find what you are looking for.
I'm thinking the logic there could be used inside a directive, instead of a separate class.
StackBlitz
Related
After googling lots of related answers and tries, I seemly have to seek help here.
I have an angular application, one of its components named stock-subscribe, which is used to display the feeTypes that a customer subscribed. Within this component, I created another component, stock-addsubs, used to display the feeTypes that are not yet subscribed by this customer.
enter image description here
Obviously, the two feeType lists can compose one whole list. From stock-subscribe, I can get an array, subscribedFeeIds, which holds all the ids of those subscribed feeTypes.
My requirement is to pass this array, subscribedFeeIds, to the stock-addsubs component so that I can filter out those yet unsubscribed feeTypes based on those ids of the array.
To my best understanding, the data passing from one component to its sub component should be a simple process and neither of two component html templates should be involved for my case. Of the many googled solutions, using #Output and #Input seems the simpler than event emitting. However, none can successfully pass the elements of the array in the sub component.
I can get the expected id list (subscribedFeeIds[]) from the stock-subscribe component, and all the rest code work fine so long as the array passed to the sub component is not EMPTY.
1) stock-subscribe.component.ts
#Component({
selector: 'app-stock-subscribe',
templateUrl: './stock-subscribe.component.html',
styleUrls: ['./stock-subscribe.component.scss']
})
export class StockSubscribeComponent implements OnInit {
userSubscribe: ISubscribe;
#Output() subscribedFeeIds: any = [];
listData: MatTableDataSource<any>;
constructor(private accountService: AccountService,
private stockService: StockService) { }
ngOnInit(): void {
this.createSubsList();
}
createSubsList() {
this.accountService.getUserAccount()
.subscribe(account => {
let userId = account.id.toString();
this.stockService.getUserSubscribes(userId).subscribe((data: ISubscribe[]) => {
// get the id of subscribed fee type and thenpublish to other component
for (var i = 0; i < data.length; i++)
{
if (data[i].status)
this.subscribedFeeIds.push(data[i].fees[0].id);
}
console.log(this.subscribedFeeIds);
// prepare the list source for display
this.listData = new MatTableDataSource(data);
this.listData.sort = this.sort;
}, error => {
console.log(error);
});
}, error => {
console.log(error);
});
}
}
2) stock-addsubs.component.ts
#Component({
selector: 'app-stock-addsubs',
templateUrl: './stock-addsubs.component.html',
styleUrls: ['./stock-addsubs.component.scss']
})
export class StockAddsubsComponent implements OnInit {
listData: MatTableDataSource<any>;
#Input() subscribedFeeIds: any [];
constructor(private feesService: FeesService) { }
ngOnInit(): void {
this.createFeeList();
}
createFeeList() {
this.feesService.getFeeList().subscribe((data: IFee[]) => {
// filter those already subscribed
for (var i = 0; i < this.subscribedFeeIds.length; i++)
{
for (var j = 0; j < data.length; j++)
{
data = data.filter(f => f.id != this.subscribedFeeIds[i]);
}
}
// prepare data to display
this.listData = new MatTableDataSource(data);
}, error => {
console.log(error);
});
}
}
You can implement either one of these methods:
1.) Enhance your #Input and #Output based on your code above:
stock-subscribe.component.ts
#Component({...})
export class StockSubscribeComponent implements OnInit {
#Output() subscribedFeeIds: EventEmitter<any> = new EventEmitter<any>();
list: any[] = []; // To store your ids, do not use the #Output variable above to push things inside a loop, you may not want to continuously emit data for an nth number of the sequence.
...
createSubsList() {
...
for (var i = 0; i < data.length; i++) {
...
if (data[i].status)
this.list.push(data[i].fees[0].id); // Push IDs on this.list not on this.subscribedFeeIds
}
this.subscribedFeeIds.emit(this.list); // After the loop finishes, emit the final list value
// Do not use .push() on #Output variable, this is an emission variable so use .emit()
}
}
or you could also do any of these methods when handling your array list:
// METHOD #1
this.filteredIds = [];
for(const { status, fees } of FEES_DATA) {
if (status) filteredIds.push(fees[0].id);
}
OR
// METHOD #2
this.filteredIds = FEES_DATA
.filter(({ status }) => status)
.map(({ fees }) => fees[0].id);
// Then emit the filtered ids
this.list.emit(this.filteredIds);
stock-addsubs.component.ts
#Component({...})
export class StockAddsubsComponent implements OnInit {
// Since you are waiting for emission from StockSubscribeComponent which the
// emission also waits till the loop is finished based on the code above, better to
// implement the #Input like this so as to avoid processing a an empty list
#Input() set subscribedFeeIds(list: any[]) {
if (list && list.length) { // If the list now has items, call createFeeList() function
this.createFeeList();
}
}
...
}
____
or
#Component({...})
export class StockAddsubsComponent implements OnInit, OnChanges { // Add OnChanges
#Input() subscribedFeeIds: any [];
ngOnChanges({ subscribedFeeIds }: SimpleChanges) { // SimpleChanges from #angular/core
if (subscribedFeeIds && subscribedFeeIds.currentValue && subscribedFeeIds.currentValue.length) {
this.createFeeList();
}
// You can also console the subscribedFeeIds and check it's value or changes
console.log(subscribedFeeIds);
}
}
Have created a Stackblitz Demo for your reference
2.) Use RxJS Subject or BehaviorSubject to emit data
Use this Stackblitz Demo for your reference.
I am building a calendar, it has 53 weeks from (12-30-2019 -> 03-01-2021). How when the app first loads it display current date.
// the function display dates
export default function RenderDates(props) {
const dates_ = [];
const startDate = moment('12-29-2019', 'MM-DD-YYYY');
// display date in week
for(let i = 1; i <= 53*7; i++) {
dates_.push(
<Date>
<ContentDate>
<ShortDate>{moment.weekdaysShort()[i%7]}</ShortDate>
<span>{startDate.add(1,'days').get('Date')}</span>
</ContentDate>
</Date>
)
}
return dates_;
}
demo: https://codesandbox.io/s/github/Kalipts/scroll_calendar?file=/src/components/RenderDates.js
You can assign unique id to every date box and then focus today's box
https://codesandbox.io/s/quirky-leavitt-w2x3w
export default function RenderDates(props) {
const dates_ = [];
const startDate = moment("12-29-2019", "MM-DD-YYYY");
useEffect(() => {
const today = moment().format("YYYY-MM-DD");
console.log('today', today);
const node = document.getElementById(today);
if (node) {
node.setAttribute("tabindex", "-1");
node.focus();
node.removeAttribute("tabindex");
}
}, []);
for (let i = 1; i <= 53 * 7; i++) {
const date = startDate.add(1, "days");
dates_.push(
<Date id={date.format("YYYY-MM-DD")}>
<ContentDate>
<ShortDate>{moment.weekdaysShort()[i % 7]}</ShortDate>
<span>{date.get("Date")}</span>
</ContentDate>
</Date>
);
}
return dates_;
}
I just changed a bit your codesandbox to make it work and here is the link: https://codesandbox.io/s/vibrant-worker-b2xhq?file=/src/App.js
Basically what I did was:
On your RenderDates component I check for the current date and added an id to the Date component if that date was the current one.
On App component (It could be on RenderDates component) I added a useEffect to run once the component is mounted that getElementById using the id on date and scrollIntoView.
It is very simple and works well! :)
I have a directive for JQuery Date picker which injects date picker into input HTML control. This was developed by a previous developer and I am pretty new to Angular at this moment.
My question is that is there any way to prevent showing auto complete on all the date pickers that we inject via this directive?
export class DanialDatePickerDirective implements ControlValueAccessor {
constructor(protected el: ElementRef, private renderer: Renderer) { }
#Input() dateformat: string = "DD-MMM-YY";
#Input() ngModel: any;
#Input() setDefaultDate: boolean;
onModelChange: Function = () => { };
onModelTouched: Function = () => { };
writeValue(value: any) {
if (value) {
var ff = new Date(value);
$(this.el.nativeElement).datepicker("setDate", ff);
}
else {
$(this.el.nativeElement).datepicker("setDate", "");
}
}
registerOnChange(fn: Function): void {
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
onBlur() {
this.onModelTouched();
}
ngAfterViewInit() {
var self = this;
$(this.el.nativeElement).datepicker({
dateFormat: 'dd-M-y',
changeMonth: true,
changeYear: true,
showOtherMonths: true,
selectOtherMonths: true
});
if (this.setDefaultDate) {
var ff = new Date(self.ngModel);
setTimeout(function () {
$(self.el.nativeElement).datepicker("setDate", ff);
}, 200);
}
$(this.el.nativeElement).on('change', (e: any) => {
var model = e.target.value;
var date = null;
var monthstring = '';
if (model.indexOf("-") > 0){
monthstring = model.substring(model.indexOf("-") + 1, 5);
}
if (isNaN(parseInt(monthstring))) {
var tt = moment(model, "DD-MMM-YY").format('YYYY-MM-DD');
date = tt;
model = moment(model, "DD-MMM-YYYY").format('MM-DD-YYYY')
}
else {
date = moment(model, "DD-MM-YYYY").format('YYYY-MM-DD');
model = moment(model, "DD-MM-YYYY").format('MM-DD-YYYY')
}
$(".ui-datepicker a").removeAttr("href");
self.onModelChange(date);
self.writeValue(date.toString());
});
}
}
The only approach who works for me:
First, make sure to set autocomplete="off" on both, the input element itself and the parent form.
Second, make sure to assign an unique name to your input field always.
This can be achieved by simply generating a random number and using this number in the name of the field.
private getUniqueName() {
return Math.floor(Math.random() * Date.now());
}
Explanation:
In the past, many developers would add autocomplete="off" to their
form fields to prevent the browser from performing any kind of
autocomplete functionality. While Chrome will still respect this tag
for autocomplete data, it will not respect it for autofill data.
https://developers.google.com/web/updates/2015/06/checkout-faster-with-autofill.
So autocomplete="off" solves the autocomplete issue. But to solve the autofill you need to play dirty with the browser by changing the name of the input over an over again, that way the browser will never know how to autofill ;)
I'm trying to display a list of dates for the current week in an Angular app. I want to allow users to view previous weeks at the click of a button, so I'm using an Observable to update the array of dates, and attempting to display the updated array.
All items are updated in the view, except for the first item in the array. Plunker example here
I've tried using *ngFor and the async pipe, as well as explicitly creating elements for each item in the array (like below). Both have the same issue. I'm struggling to find a solution.
//our root app component
import {Component, NgModule, VERSION} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
#Component({
selector: 'my-app',
template: `
<button (click)="previousWeek()">Prev Week</button>
<div>{{dates[0]}}</div>
<div>{{dates[1]}}</div>
<div>{{dates[2]}}</div>
`,
})
export class App {
name:string;
dates: Date[];
public $datesSource: Observable<Date[]>;
private datesSource: Subject<Date[]>;
constructor() {
this.datesSource = new Subject<Date[]>();
this.datesSource$ = this.getDatesWithObservable();
this.datesSource$.subscribe((dates) => {
console.log(dates);
this.dates = dates;
})
this.setDates(new Date());
}
setMonday(date: Date): Date {
const day = date.getDay() || 7;
if (day !== 1) {
date.setHours(-24 * (day - 1));
}
return date;
}
setDates(date: Date): void {
const dates = [
new Date(),
new Date(),
new Date(),
new Date(),
new Date(),
new Date(),
new Date()
];
const monday = this.setMonday(date);
dates[0] = monday;
const mondayDate = monday.getTime();
dates.forEach((date, idx) => {
console.log(idx);
date.setTime(monday.getTime() + (idx * 24 * 60 * 60 * 1000));
});
this.addDates(dates);
}
addDates(dates: Date[]): void {
this.datesSource.next(dates);
}
getDatesWithObservable(): Observable<Date[]> {
return this.datesSource.asObservable();
}
previousWeek(): void {
const day = this.dates[0].getDay() || 7;
const lastWeek = this.dates[0];
const days = 7;
lastWeek.setTime(lastWeek.getTime() - (days * 24 * 60 * 60 * 1000));
this.setDates(lastWeek);
}
}
try this , i commented the line in the middle and it's working, can you check:
const monday = this.setMonday(date);
//dates[0] = monday;
const mondayDate = monday.getTime();
I'm trying to build an event date filter by passing in a time range. I was able to filter for events that are the same date as today but need help to filter events from last week, last month, etc...
$scope.eventDateFilter = function(column) {
if(column === 'today') {
$scope.dateRange = $scope.dateToday;
} else if (column === 'pastWeek') {
//need logic
} else if (column === 'pastMonth') {
//need logic
} else if (column === 'future') {
//need logic
} else {
$scope.dateRange = "";
}
}
Here's my fiddle:
http://jsfiddle.net/c6BfQ/3/
Your help is greatly appreciated.
I would use a custom filter. Here is one I used to filter things created in the last two days, it should give you an idea of how to do yours.
.filter('dateFilter', function() {
return function (objects) {
var filtered_list = [];
for (var i = 0; i < objects.length; i++) {
var two_days_ago = new Date().getTime() - 2*24*60*60*1000;
var last_modified = new Date(objects[i].date_created).getTime();
if (two_days_ago <= last_modified) {
filtered_list.push(objects[i]);
}
}
return filtered_list;
}
});