I have a method that changes the color of some textblocks, can you give the content as a parameter (string) with a RelayCommand?
<Button Command="{Binding ColorCommand}"
Content="Red"
x:Name="Red"/>
<Button Command="{Binding ColorCommand}"
Content="Green"
x:Name="Green"/>
public void getColorForeground(string color)
{
if (color == "Red")
{
RedBackGround = new SolidColorBrush(Colors.Red);
}
else if (color == "Green")
{
RedBackGround = new SolidColorBrush(Colors.Green);
}
}
public RelayCommand ColorCommand
{
get
{
return new RelayCommand>(() =>
{
getColorForeground(**NEED PARAMETER**);
});
}
}
You could use the CommandParameter property to pass a object/value to your command:
<Button Command="{Binding ColorCommand}" CommandParameter="Red"
Content="Red"
x:Name="Red"/>
public RelayCommand ColorCommand
{
get
{
return new RelayCommand>((color) =>
{
getColorForeground(color);
});
}
}
Related
I have a mat-table where each row represents a audio recording for a call. At the end of each row, there is a play button which plays the respective audio file when clicked. However, when I click any play button, the correct audio file plays but all the play buttons change to stop buttons. Here is a snippet of the html:
<!-- audio display -->
<ng-container matColumnDef="isPlaying">
<mat-header-cell *matHeaderCellDef>Playing</mat-header-cell>
<mat-cell *matCellDef="let recording">
<button mat-button (click)="playToggle(recording)" [disabled]="state?.error" *ngIf="!state?.playing">
<mat-icon mat-list-icon>play_arrow</mat-icon>
</button>
<button mat-button (click)="pause()" *ngIf="state?.playing">
<mat-icon mat-list-icon>pause</mat-icon>
</button>
</mat-cell>
</ng-container>
recordings.component.ts
#Component({
selector: 'recordings',
templateUrl: './recordings.component.html',
styleUrls: ['./recordings.component.scss'],
encapsulation: ViewEncapsulation.None,
animations: fuseAnimations
})
export class RecordingsComponent implements OnInit {
// Class Variables
recordingPlaying = false;
buttonValue = 'Play Recording';
displayColumns: string[] = ['date', 'time'];
dataSource: MatTableDataSource < Recording > ;
currentUser: BehaviorSubject < UserModel > ;
dataLoading = true;
recordings: Recording[];
recording: Recording;
state: RecordingStreamState;
currentFile: any = {};
#ViewChild(MatPaginator) paginator: MatPaginator;
#ViewChild(MatSort) sort: MatSort;
#Input()
telephone: string;
// Private
private audio = new Audio();
private _unsubscribeAll: Subject < any > ;
constructor(
private _recordingsService: RecordingsService,
private _usersService: UserService,
private _matSnackBar: MatSnackBar,
private _fuseSidebarService: FuseSidebarService,
private _matDialog: MatDialog
) {
// Set the defaults
this._unsubscribeAll = new Subject();
}
ngOnInit(): void {
this.currentUser = this._usersService.user;
this.loadRecordings();
// listen to stream state
this._recordingsService.getState().subscribe(state => {
this.state = state;
console.log('this.state: ' + JSON.stringify(this.state));
});
}
public loadRecordings(): void {
this._recordingsService.getRecordings(this.telephone).then(data => {
this.recordings = data;
});
this._recordingsService.onRecordingClick
.subscribe((recordings: Recording[]) => this.handleRecordings(recordings),
err => this.handleRecordingsError(err)
);
}
private handleRecordings(recordings: Recording[]): void {
this.dataLoading = false;
this.dataSource = new MatTableDataSource(recordings);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
private handleRecordingsError(err): void {
console.error(err);
// this.alertService.error("Problem loading contacts!");
}
playToggle(recording): void {
// TODO: (remove) if this is the reocrding already playing, pause, else play
if (recording === this.recording) {
this.pause();
this.recording = null;
} else {
this.recording = recording;
console.log('this.recording: ' + JSON.stringify(this.recording));
if (this.recording === undefined) {
console.log('could not find recording.');
} else {
this.playStream(this.recording.url);
}
}
}
playStream(url): void {
// subscribes to playStream in our service and starts listening to media events
// like canplay, playing etc. This should be done in the stateChange object
// but we use this method to start the observable and audio playback
this._recordingsService.playStream(url).subscribe(events => {
});
}
pause(): void {
this._recordingsService.pause();
}
play(): void {
this._recordingsService.play();
}
stop(): void {
this._recordingsService.stop();
}
}
recordings.service.ts
#Injectable()
export class RecordingsService implements Resolve < any > {
// Class variables
routeParams: any;
recordings: Recording[];
onRecordingClick: BehaviorSubject < any > ;
private stop$ = new Subject();
private audioObj = new Audio();
audioEvents = [
'ended',
'error',
'play',
'playing',
'pause',
'timeupdate',
'canplay',
'loadedmetadata',
'loadstart',
];
// gets from the interface we created
private state: RecordingStreamState = {
playing: false,
readableCurrentTime: '',
readableDuration: '',
duration: undefined,
currentTime: undefined,
canplay: false,
error: false,
};
private stateChange: BehaviorSubject < RecordingStreamState > = new BehaviorSubject(
this.state
);
// tslint:disable-next-line:typedef
private streamObservable(url) {
console.log('in streamObservable in service');
// tslint:disable-next-line:no-unused-expression
return new Observable(observer => {
// Play audio
this.audioObj.src = url;
this.audioObj.load();
this.audioObj.play();
// a way for the observer to react to items the observable emits
const handler = (event: Event) => {
this.updateStateEvents(event);
observer.next(event);
};
this.addEvents(this.audioObj, this.audioEvents, handler);
return () => {
// stop playing
this.audioObj.pause();
this.audioObj.currentTime = 0;
// remove event listeners
this.removeEvents(this.audioObj, this.audioEvents, handler);
};
});
}
private addEvents(obj, events, handler): void {
events.forEach(event => {
obj.addEventListener(event, handler);
});
}
private removeEvents(obj, events, handler): void {
events.forEach(event => {
obj.removeEventListener(event, handler);
});
}
// how to handle the different events , recording: Recording
private updateStateEvents(event: Event): void {
console.log('event_type: ' + event.type);
switch (event.type) {
case 'canplay':
this.state.duration = this.audioObj.duration;
this.state.readableDuration = this.formatTime(this.state.duration);
this.state.canplay = true;
break;
case 'playing':
this.state.playing = true;
break;
case 'pause':
this.state.playing = false;
break;
case 'timeupdate':
this.state.currentTime = this.audioObj.currentTime;
this.state.readableCurrentTime = this.formatTime(
this.state.currentTime
);
break;
case 'error':
this.resetState();
this.state.error = true;
break;
}
this.stateChange.next(this.state);
}
private resetState(): void {
this.state = {
playing: false,
readableCurrentTime: '',
readableDuration: '',
duration: undefined,
currentTime: undefined,
canplay: false,
error: false,
};
}
getState(): Observable < RecordingStreamState > {
return this.stateChange.asObservable();
}
playStream(url): Observable < any > {
return this.streamObservable(url).pipe(takeUntil(this.stop$));
}
play(): void {
this.audioObj.play();
}
pause(): void {
this.audioObj.pause();
}
stop(): void {
this.stop$.next();
}
seekTo(seconds): void {
this.audioObj.currentTime = seconds;
}
formatTime(time: number, format: string = 'HH:mm:ss'): string {
const momentTime = time * 1000;
return moment.utc(momentTime).format(format);
}
/**
* Constructor
*
* #param {HttpClient} _httpClient
*/
constructor(
private _httpClient: HttpClient,
) {
// Set the defaults
this.onRecordingClick = new BehaviorSubject([]);
}
}
Looking at your code I can think of a probable solution. I don't know how the Recording object is defined, but if it contains some id it might work. Or you can use some other identifier.
As I said in the comment, state property is shared by all entries within your table. So it's not enough to determine the current playing record. Once it's true for one it's true for all of them. But maybe this can help:
<!-- audio display -->
<ng-container matColumnDef="isPlaying">
<mat-header-cell *matHeaderCellDef>Playing</mat-header-cell>
<mat-cell *matCellDef="let rowRecording">
<button mat-button (click)="playToggle(rowRecording)" [disabled]="state?.error" *ngIf="!state?.playing">
<mat-icon mat-list-icon>play_arrow</mat-icon>
</button>
<button mat-button (click)="pause()" *ngIf="state?.playing && rowRecording.id === recording.id">
<mat-icon mat-list-icon>pause</mat-icon>
</button>
</mat-cell>
</ng-container>
As you can see, I renamed the recording within HTML to rowRecording to differentiate it from the one from the .ts code. Then I'm checking if the state is playing but also if the rowRecording.id equals the ID of assigned recording within .ts. You do it in the playToggle function.
But I would recommend extending the RecordingStreamState object with PlayingRecordId property which would be assigned when you press play. Then you can do someting like this (using your version of HTML)
<button
mat-button
(click)="pause()"
*ngIf="state?.playing && state?.playingRecordId === recording.id"
>
I have setup my app to display events on calendar. However, whilst the correct number of events will display the date and time is always the current date and time rather than what I have input into the SQL db table. Any help with what I am doing wrong would be greatly appreciated. My code is below:
View
#model IEnumerable<wccFacilityBookings.Models.Events>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<div id="calender"></div>
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title"><span id="eventTitle"></span></h4>
</div>
<div class="modal-body">
<p id="pDetails"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<link href="//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.css" rel="stylesheet" />
<link href="//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.print.css" rel="stylesheet" media="print" />
#section Scripts{
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.js"></script>
<script>
$(document).ready(function () {
var events = [];
$.ajax({
type: "GET",
url: "/applications/GetEvents",
success: function (data) {
$.each(data, function (i, v) {
events.push({
title: v.Subject,
description: v.Description,
start: moment(v.Start),
end: v.End != null ? moment(v.End) : null,
color: v.ThemeColor,
allDay : v.IsFullDay
});
})
GenerateCalender(events);
},
error: function (error) {
alert('failed');
}
})
function GenerateCalender(events) {
$('#calender').fullCalendar('destroy');
$('#calender').fullCalendar({
contentHeight: 400,
defaultDate: new Date(),
timeFormat: 'h(:mm)a',
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay,agenda'
},
eventLimit: true,
eventColor: '#378006',
events: events,
eventClick: function (calEvent, jsEvent, view) {
$('#myModal #eventTitle').text(calEvent.title);
var $description = $('<div/>');
$description.append($('<p/>').html('<b>Start:</b>' + calEvent.start.format("DD-
MMM-YYYY HH:mm a")));
if (calEvent.end != null) {
$description.append($('<p/>').html('<b>End:</b>' + calEvent.end.format("DD-
MMM-YYYY HH:mm a")));
}
$description.append($('<p/>').html('<b>Description:</b>' +
calEvent.description));
$('#myModal #pDetails').empty().html($description);
$('#myModal').modal();
}
})
}
})
</script>
}
Controller
// GET: Applications/CalendarView
public IActionResult CalendarView()
{
return View();
}
public JsonResult GetEvents()
{
using (WCCFacilityBookingsContext context = new WCCFacilityBookingsContext())
{
var events =_context.Events.ToList();
return Json(events);
}
}
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace wccFacilityBookings.Models
{
public class Events
{
[Key]
public int EventID { get; set; }
public string Subject { get; set; }
public string Description { get; set; }
public System.DateTime Start { get; set; }
public Nullable<System.DateTime> End { get; set; }
public string ThemeColor { get; set; }
public bool IsFullDay { get; set; }
}
}
Does this have something to do with it being Asp.Net Core?
Yes, in .NET Core 3.x, when you want to pass json from controller to client, it will camel-case all JSON output by default.
To avoid this, you can add the following setting in startup.cs ConfigureServices method:
services.AddMvc()
.AddJsonOptions(opts => opts.JsonSerializerOptions.PropertyNamingPolicy = null);
Since I added this setting before, the problem did not appear when I tested with your code. If I delete it, your problem will be reproduced.
So you have two solutions, change the field name to camel-case in js, or add the above code in startup.
OK, as always #YongquingYu got me on the right track. I am a 'nuffy' when it come to Ajax and Jquery. My issue, for reasons I don't understand was with capitalization, once I made the 'properties' lower case it worked. Does this have something to do with it being Asp.Net Core? Anyway my code (which is working as desired) is below:
#section Scripts{
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.js"></script>
<script>
$(document).ready(function () {
var events = [];
$.ajax({
type: "GET",
url: "/Applications/GetEvents",
success: function (data) {
$.each(data, function (i, v) {
events.push({
title: v.applicantContactName,
description: v.facility,
start: moment(v.start),
end: v.end != null ? moment(v.end) : null,
color: v.themeColor,
allDay: v.isFullDay
});
})
GenerateCalender(events);
},
error: function (error) {
alert('failed');
}
})
function GenerateCalender(events) {
$('#calender').fullCalendar('destroy');
$('#calender').fullCalendar({
contentHeight: 400,
defaultDate: new Date(),
timeFormat: 'h(:mm)a',
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay,agenda'
},
eventLimit: true,
eventColor: '#378006',
events: events,
eventClick: function (calEvent, jsEvent, view) {
$('#myModal #eventTitle').text(calEvent.title);
var $description = $('<div/>');
$description.append($('<p/>').html('<b>Start: </b>' + calEvent.start.format("DD-MMM-YYYY HH:mm a")));
if (calEvent.end != null) {
$description.append($('<p/>').html('<b>End: </b>' + calEvent.end.format("DD-MMM-YYYY HH:mm a")));
}
$description.append($('<p/>').html('<b>Description: </b>' +
calEvent.description));
$('#myModal #pDetails').empty().html($description);
$('#myModal').modal();
}
})
}
})
</script>
}
// GET: Applications/CalendarView
public IActionResult CalendarView()
{
return View();
}
public JsonResult GetEvents()
{
using (WCCFacilityBookingsContext context = new WCCFacilityBookingsContext())
{
var events = context.BookingApplications.ToList();
return Json(events);
}
}
I have a Suspend Toggle(ON/OFF) in Update form which is used for suspending and un-suspending the Row value of a Table as shown below in image.
On click of Suspend Toggle, It should call Service method and suspend/unsuspend the row without opening the Suspend dialog as shown below.
The Suspend dialog only should open, if on click of Suspend toggle(ON/OFF), validations return some warning message.
Below is code for Suspend dialog HTML.
<div class="container mt-2" style="width: 500px; height: 90px">
<div class="spinner" *ngIf="loadspinner" align="center"></div>
<div *ngIf="!loadspinner">
<mat-dialog-content>
<p style="color:rgb(255, 60, 0)">{{errorMsg}}</p>
<p align="center">{{warningMsg}}</p>
</mat-dialog-content>
<mat-dialog-actions align="center">
<button *ngIf="errorMsg ; else elseBlock" mat-raised-button mat-dialog-close tabindex="-1" (click)="oKBtn()" style="border-color:#3f51b5;">OK</button>
<ng-template #elseBlock>
<button mat-raised-button color="primary" [mat-dialog-close] = "true">SUSPEND</button>
<button mat-raised-button mat-dialog-close tabindex="-1" (click)="cancel()" style="border-color:#3f51b5;">Cancel</button>
</ng-template>
</mat-dialog-actions>
</div>
Update Dialog is Parent .ts for child suspend dialog .ts
Below is code for child suspend.ts
constructor(public dialogRef: MatDialogRef<SuspendModalComponent>,
#Inject(MAT_DIALOG_DATA) public data:DialogData, private updateService: UpdateService) {
this.mainData = this.data; }
ngOnInit() {
if (this.mainData.buttonChecked == false) {
this.updateService.validate(this.mainData.updateData).subscribe(value => {
this.loadspinner = false;
if (value != null) {
this.warningMsg = value.statusMessage;
} else {
this.warningMsg = "";
}
});
} else {
this.updateService.validateRecord(this.mainData.updateData).subscribe(record => {
this.loadspinner = false;
if (record != null) {
this.errorMsg = "Record is present";
}
});
}
}
cancel() : void {
this.dialogRef.close();}
Suspend/unsuspend service methods i'm calling from suspend() function of parent update.ts as shown in code.
suspend(){
const list: any[] = Array.of(this.mainData.updateData);
if(this.toggleRef.checked) {
if(this.toggleRef.record == null) {
this.updateService.unsuspendCall(list).subscribe(data => {
this.toggleRef.checked = true;
this.buttonChecked = false;
});
}
} else {
if(this.toggleRef.value == null) {
this.updateService.suspendCall(list).subscribe(data => {
this.toggleRef.checked = false;
this.buttonChecked = true;
});
}
}
Can anyone help on this how to hide suspend dialog if there is no validations message on click of Suspend Toggle button and show the dialog if validation message is there?
I have a charts that have a few options for user and I already implemented everything but not sure why I don't see any immediate changes when I click Delete Chart, Publish Chart or Unpublished Chart button. I can only see the result only after I refresh the browser.
I'm new to Angular so I'm wonder how to remove the selected chart immediately or make it disappear when delete button is click and also the same for publish and unpublish chart without having to refresh the browser. any help or suggestion will be really appreciated
#Input() chart: Chart;
data: ChartData;
chartData: ChartData;
hasError: boolean = false;
maxisChartConfig: ChartConfig;
hasChart: boolean = false;
#Input() editMode: boolean;
isTextChart: boolean = false;
constructor(private chartService: ChartService, private router: Router, private dialog: MatDialog) { }
isGrid: boolean = false;
#Input() wsType?: WorkspaceType;
isPublicWs: boolean = false;
ngOnInit(): void {
if(this.wsType) {
if(this.wsType == WorkspaceType.public) {
this.isPublicWs = true;
}
}
this.generateChartConfig();
if(this.chart.chartType == ChartType.text){
this.isTextChart = true;
}
else if(this.chart.chartType == ChartType.grid){
this.isGrid = true;
}
if (this.chart.data) {
if(!this.isTextChart){
this.hasChart = true;
}
this.chartData = this.chart.data;
}
}
deleteChart() {
this.chartService.deleteChart(this.chart.guid).subscribe((deleted) => {
console.log(deleted);
});
}
publishChart() {
this.chartService.setChartPublished(this.chart.guid, !this.chart.isPublished).subscribe((published) => {
console.log(published);
});
}
<button mat-menu-item (click) = "deleteChart()" *ngIf = "chart.hasAccess && chart.canEdit && !chart.isPublished">Delete Chart</button>
<button mat-menu-item (click) = "publishChart()" *ngIf = "chart.canEdit && chart.hasAccess && !chart.isPublished && isPublicWs">Publish Chart</button>
<button mat-menu-item (click) = "publishChart()" *ngIf = "chart.canEdit && chart.hasAccess && chart.isPublished && isPublicWs">Unpublish Chart</button>
The will not run but I uploaded the full code for this component here https://stackblitz.com/edit/angular-ivy-bepxss . Thanks
After each function you can call oninit to reconstruct the charts after changes like this -
deleteChart() {
this.chartService.deleteChart(this.chart.guid).subscribe((deleted) => {
console.log(deleted);
this.ngOnInit(); // Add this line
});
}
///This is how i have refreshed variables in my case -
saveWTPModel(){
if(some condition){
//Perform Save logic
var headers = new HttpHeaders();
headers.append('Content-Type', 'application/json');
const httpOptions = { headers: headers };
this.httpClient.post(environment.api_url + "/User/SavePriority",
this.finalWTPArray, httpOptions)
.subscribe(
response => {
if(response){
this.alertService.success('Priorities Saved.');
//Once i have saved everything I am again calling the api to get the updated data from backend. This function again is called to get the updated Priorities.
this.getWorkTypesPriority(this.loggedinUserId);
}
else{
this.alertService.warning('Problem Occurred.');
}
});
}
I have this block of html in my template to show or hide the div.
<div *ngIf="csvVisible">
<p>Paragraph works</p>
</div>
This is my component.
export class SettingsComponent implements OnInit {
csvVisible: boolean = false;
private dataSource: string[];
#ViewChild(MatTable, { static: true }) table: MatTable<any>;
constructor(private dialog: MatDialog, private templateParserService: TemplateParserService) { }
ngOnInit() {
this.templateParserService.subscribe({
next(result: string[]) {
if (result !== null) {
this.dataSource = result;
if (this.dataSource && this.dataSource.length) {
this.csvVisible = true;
} else {
this.csvVisible = false;
}
}
},
error(error: Error) {
console.log(error.message);
}
});
}
Eventhough the DIV is hidden at start, it doesnt automatically show / hide on the csvVisible value change. Value of csvVisible is properly set when the observer emits data. [hidden]="csvVisible" isn't working either.
Edit :
Subscriber registration on the service is done by the following code.
private subject = new Subject<string[]>();
public subscribe(observer: any): Subscription {
return this.subject.subscribe(observer);
}
Since you are using Object inside subscribe, this points to current subscribe object, Instead of using subscribe({next:()}) try using this way
component.ts
this.templateParserService.subscribe((result: string[])=>{
if (result !== null) {
this.dataSource = result;
if (this.dataSource && this.dataSource.length) {
this.csvVisible = true;
} else {
this.csvVisible = false;
}
}
},(error: Error)=>{
console.log(error.message);
});