I am trying to get events based on the current view. To track 'prev', 'next' clicks and initial display views, I'm using the CalendarOption viewRender which returns the full view, and invokes itself every click - which gives a new full view.
Problem is - I cannot call the service from which I'm getting my events from, based on the extracted parameter of the viewRender view output.
constructor (public eventService: EventService)
calendarOptions = {
...
...
eventLimit: true,
viewRender: function(view) {
this.eventService.getEvents(view) // <--- code stops here, error printed at bottom
.subscribe(
response => {
this.events = response.events;
},
error => {
console.log(error)
}
);
console.log(this.events);
},
events: this.events,
...
...
}
I'm expecting my service to work and be triggered from within that scenario - on calendar first initializing and each time the 'prev' / 'next' buttons are clicked
currently, the output is
ERROR TypeError: Cannot read property 'getEvents' of undefined
Is there any other way I can invoke this external command?
I also tried using httpClient directly, but HTTP is also a service which can't be accessed as it seems
It is now solved
Managed to get access to my service by appending:
const eventService = this.eventService;
here:
initCalendar() {
const eventService = this.eventService;
var events = []
this.calendarOptions = {
...
...
viewRender: function(view) {
eventService.getEvents(view) // <----- Not accessing with 'this' anymore
.subscribe(
response => {
events = response.events;
},
error => {
console.log(error)
}
);
},
events: events;
...
...
}
}
Related
I have a scenario where I have one parent machine and several child machines that can be spawned from the parent machine.
The current setup looks like this:
const parentMachine = Machine({
context: {
children: [] //can contain any number of child services
},
...
on: {
ADD_CHILD: {
actions: assign({
children: (ctx, e) => {
return [
...ctx.children,
{
ref: spawn(childMachine)
},
];
},
}),
},
UPDATE_CHILDREN: {
actions: ??? //need to somehow loop through children and send the UPDATE event to each service
}
}
});
When the parent machine receives the "UPDATE_CHILDREN" event, I want to update each of the child services. I know you can send batch events by passing an array to send, but I want each event to also be sent to a different service. I've only seen examples where they are sent to a single service at a time. I've tried several things, including the following:
UPDATE_CHILDREN: {
actions: ctx => ctx.children.forEach(c => send("UPDATE", { to: () => c.ref }) //doesn't send
}
Am I missing something obvious? Is this possible?
Ah, I bumped into exactly the same issue as you!
It turns out that if you give actions a function, it assumes the function to be the actual action, not a function that returns actions.
If you want to generate your actions based on context, you need to use a pure action:
import { actions } from 'xstate';
const { pure } = actions;
...
actions: pure((context, event) =>
context.myActors.map((myActor) =>
send('SOME_EVENT', { to: myActor })
)
),
This is a tricky mistake to fall into as you get no feedback that you're doing something wrong..
Had a realization about how this is supposed to work in XState.
The references to the children are already being stored, so we can just basically send events to them directly without using the "to" property:
actions: ctx => ctx.children.forEach(c => c.ref.send("UPDATE"))
I've implemented Instascan (https://github.com/schmich/instascan) to allow users to read QR Codes from within my Angular 5 app.
I have to trigger some actions after a successful scan and update my component's view accordingly.
I'm using the following code inside my component to detect my cameras and start scanning:
cameras: Array<any> = []
selectedCamera: any
scanner: any
content: string
ngOnInit () {
let vm = this
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
vm.cameras.push(...cameras)
} else {
console.error('No cameras found.')
}
}).catch(function (e) {
console.error(e)
})
}
startScan () {
let vm = this
this.scanner = new Instascan.Scanner({
video: this.videoContainer.nativeElement,
backgroundScan: false,
mirror: false
})
this.scanner.addListener('scan', function (content) {
vm.content = content
})
this.selectedCamera = this.cameras[0]
this.scanner.start(this.selectedCamera)
}
And in my template I have an element that exists only if 'content' exists, and on click emit the scanned content to the parent component through an EventEmitter:
<div *ngIf="content" class="btn" (click)="emitContent()">
PROCEED
</div>
The problem is that in 'scan' event callback the changes in 'content' seems to don't be applied to my view, and my 'PROCEED' button don't become visible. An even stranger behavior happens: after I click anywhere in the screen, those changes are applied to my view.
I've also tried using ChangeDetectorRef.detectChanges() method inside the callback and binding 'this' into the callback, but both are not working.
How can I overcome this issue?
Thanks!
I've managed to solve this problem by using NgZone like this:
import { NgZone } from '#angular/core'
constructor (
private ngZone: NgZone
) {}
startScan () {
this.scanner = new Instascan.Scanner({
video: this.videoContainer.nativeElement,
backgroundScan: false,
mirror: false
})
this.scanner.addListener('scan', function (content) {
this.ngZone.run(() => {
this.content = content
})
}.bind(this))
this.selectedCamera = this.cameras[0]
this.scanner.start(this.selectedCamera)
}
I don't know if this is the best solution, and actually I don't know how NgZone usage affects the application performance/state at all.
If someone know a better solution, it will be welcome!
Thanks!
My code has been refactored and some extracted into a service that subscribes to functions. However, my original code had a call within the subscription that referenced a variable within the file, but now I'm not sure how to best reach it?
I am struggling with where to place the line:
this.select.reset('some string'); found within the subscribeToMessageService() function.
Original code
event.component.ts
select: FormControl;
#ViewChild('mySelect') mySelect: ElementRef;
subscribeToMessageService() {
this.messageService.serviceMsg
.subscribe(res => {
// unrelated code
this.select.reset('some string');
});
}
subscribeToEventService() {
this.eventService.eventSubject
.subscribe(res => {
this.select = new FormControl(res.status);
this.select.valueChanges.subscribe(value => {
// manual blurring required to stop error being thrown when popup appears
this.selector.nativeElement.blur();
// do something else
});
});
}
Refactored code
status.service.ts
subscribeToMessageService(): void {
this.messageService.serviceMsg
.subscribe(res => {
// unrelated code
// This is where 'this.select.reset('some string');' would have gone
});
}
status.component.ts
select: FormControl;
#ViewChild('exceptionalSelect') selector: ElementRef;
subscribeToEventService() {
this.eventService.eventSubject
.subscribe(res => {
this.select = new FormControl(res.status);
this.select.valueChanges.subscribe(value => {
// manual blurring required to stop error being thrown when popup appears
this.selector.nativeElement.blur();
this.onStatusChange(value);
});
});
}
Since you still want to subscribe to the original source messageService.serviceMsg your new StatusService needs to expose this observable to the injecting component (StatusComponent).
This can be done for example by creating a public observable in the StatusService (possibly by utilising rxjs Subject or angular EventEmitter) and triggering the emit in the subscription of messageService.serviceMsg.
Then your StatusComponent only needs to inject StatusService and do
this.statusService.serviceMsg // <-- might choose another name to make clear that this is passed on.
.subscribe(res => {
// unrelated code
this.select.reset('some string');
});
I have a replies component and newReply component in my page. When a newReply is added, i emit an event on a global vue message bus to notify the replies component that a new reply has been added so that it reloads and re-renders.
methods: {
updateReplyList: function(newReply){
window.vueMessageBus.$emit('notifyingrepliescomponent',newReply);
},
}
i have attached the event listener for the notifyingrepliescomponent event inside the created() hook of replies component.
UPDATED CODE SNIPPET
//file: replies.vue
methods: {
fetch: function(url = null){
let vm = this;
if(!url)
{
axios.get(route('replies.paginated',{ 'thread' : this.thread_id }))
.then(function(serverResponse){
vm.replies_with_pagination = serverResponse.data;
//'replies_with_pagination' holds the paginated collection returned by laravel for the current paginated page
});
}
else
{
var page = url.match(/\?page=(\d+)/i)[1];
axios.get(route('replies.paginated',{ 'thread' : this.thread_id, 'page' : page }))
.then(function(serverResponse){
vm.replies_with_pagination = serverResponse.data;
});
}
},
reloadNew: function(url){
this.fetch(url);
window.scrollTo(0,0);
},
},
created() {
this.fetch();
window.vueMessageBus.$on('notifyingrepliescomponent',newReply => {
console.log('added object: ' + newReply);
this.all_reply_items.push(newReply);
this.reloadNew(route('replies.paginated',{ 'thread' : this.thread_id, 'page' : this.pageForQueryString }));
this.$emit('repliescountchanged',this.all_reply_items.length);
});
},
The whole system works, except for the first time. That is when there are no replies to a thread, and i add a new reply, the replies component does not reload. But for the subsequent replies, this works fine.
I am guessing this is an issue with my event listener? Can anyone please help?
TIA, Yeasir
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'title' of undefined
I used the same code from 'heroes' example to load a detail object in a single route. I kept getting this error because, I think, the data is not loaded before the view already started to render and that's why I am getting this error.
I had this problem to display "currentUser.name" which I solved by using currentUser?.name but in this case, it doesn't make sense to add to all the places of the object properties with '?'.
I have to spend more time in OnInit than what heroes example did. Because I need to fetch more stuff. So I know that the view just kicks in much earlier than the binding object journal is loaded.
ngOnInit() {
this.userService.getUser().then( (user) => {
this.currentUser = user;
this.accountsService.getAccounts().then( (accounts) => {
this.accounts = accounts;
this.userAccounts = this.currentUser.accounts.map(
accountId => this.accounts.find(
elem => elem.id == new String(accountId)
)
);
this.route.params
.switchMap((params: Params) => this.journalService.getJournal(+params['id']))
.subscribe( (journal) => {
console.log("journal", journal);
this.journal = journal;
});
});
});
}
How can I instruct the view to wait until the data is loaded before it starts to render itself?
Or is there something wrong with the code?
You could wrap your template with a condition.
Steps:
1 - Create a variable and initializes it with a falsy value:
loaded: boolean = false;
2 - Set it to true when your request is finished:
this.route.params
.switchMap((params: Params) => this.journalService.getJournal(+params['id']))
.subscribe((journal) => {
console.log("journal", journal);
this.journal = journal;
this.loaded = true; // -> here
});
3 - In your template use *ngIf to prevent errors:
<ng-container *ngIf="loaded">
... content
</ng-container>