So I have yet another DurandalJS question.
So I have a few widgets that are pretty much self contained. They render or hide themselves depending on whether the current user is logged in or not e.g. I have a widget that displays the current users name, and another one that displays some setting for the current user. The 'current user is a value stored in local storage so everyone basically knows to get it from there and do their bit.
I have a security module which triggers an event on itself when a user is logged in and when a user is logged out.
All my widgets including shell require this security module and they all handle the event.
Now I know the event is working because shell's event handler gets called but the widgets never see the event even thought they are displayed in shell.
However, if I do a hard refresh of the page (whether the user is logged in or out), all the widgets render properly so I know the widgets know what to do.
Am I doing this wrong? If yes how best do I go about it.
Thanks
The problem was that I was returning singletons from my widgets. I have no idea what the difference was but I never got the events when I returned singletons. Durandal expects widgets to be constructor functions so that it can instantiate multiple widgets of the same kind see here
viewmodel.js is a function exported module that will serve as a location for all your widget's code. It will be bound to view.html by the widget infrastructure via the composition module.
Related
I am building a CRUD (Create, Read, Update, Delete) form with Angular 5 and want to navigate to the route showing an overview of all already added elements including the new one.
this.myElement.create({
//... Do some API magic
}).subscribe(() =>
this.router.navigate(['/overview']);
);
After updating the database the user gets router.navigate'd to the overview. There should be displayed a message (alert, notification, toast or what ever) whether the change was successfull or not.
Possible aproaches:
Do I have to implement an own service to transfer informations like this one?
Is there a built-in solution in Angular?
Should I use something like an event handler or oberserver watching for any CRUD methods?
You need to be careful where you implement this alert. If it's a service, you might do
.subscribe(() =>
this.toast.show('Task is finished');
this.router.navigate(['/overview']);
);
Since you are switching routes, the toast component has to be in the parent templates of <router> or up.
If the service is designed targeting the body tag, ex. using position: fixed. Then you are safe no matter what.
I don't think the entire page will get refreshed if that's your concern.
Just to answer your question
Do I have to implement an own service to transfer informations like this one?
YES, not necessarily you, but someone.
Is there a built-in solution in Angular? Maybe, because you can just put an element that positioned to the screen as a simple alert. Angular doesn't do anything specific for this task.
Should I use something like an event handler or oberserver watching for any CRUD methods? No need, you want to finish this alert before moving to others. However if you want others to know an alert has been fired, then YES, you want that service to publish an observerable.
I am trying to supply an alert once a task is complete - the user may be in any of multiple pages at the time. The alert should display to all pages.
I am using a service implementing BehaviorSubject
The provider for which is in my app.component.ts page - single instance
In my app.component.html I have the two components, one the alert, the other that fires the alert.
<alert></alert>
<submit-service></submit-service>
The service emits to the alert component which renders the alert.
This works fine, but only ever on the page that submits the service (not to any other page) - submission function is also in the alert component.
submit-service utilises
public emit: BehaviorSubject<model> = new BehaviorSubject(new model());
Once the event is completed it then fires off this.emit.next(_model);
In the alert component I subscribe to the event
ngOnInit(): void {
this.service.emit.subscribe(data=> {
this.fireAlert(data);
}
});
}
so I suppose the main question is, how do I have a single service subscribed across multiple instances, across multiple pages?
EDIT 1
Apologies for the ambiguity, by page I mean separate browser window or tab i.e. window.open
Just in case others are having this same issue - there is in fact an answer.
Using global storage events allows the traversing of information across all browser tabs/windows.
In the service instead of using BehaviourSubject, the service updates or 'emits' the data to a local storage item, event listener utilising a HostListener() decorator can then listen for these udpates - which listens across all windows and tabs.
Example:
#HostListener('window:storage', ['$event'])
onStorageChange(ev: StorageEvent) {
console.log(ev.key);
console.log(ev.newValue);
}
See here for further information: Storage events
So there's a couple things at play here. The first is the service that let's your application know that it's time to display the alert. It sounds like you already have that, but for simplicity sake I would make sure you are declaring that in a forRoot() context. I won't go into a crazy amount of detail regarding this topic, but essentially you need to make sure that your service is running in the root context. If you start lazy loading modules, and then subscribing to your service from within the lazy loaded module, it will create it's own Dependency Injection context and you'll start pounding your head against the table wondering why your service isn't updating. (been there :)
The next thing to look at is where you want to render your alert. You'll likely want to use the ComponentFactoryResolver to render your alert in the highest level component you can think of that makes sense. Basically (if I understand your need correctly), you need this to be within the same component, or higher as all of the pages you want to have the alert rendered to. For example I am working on an application that has a dashboard where we have a ComponentFactoryResolver that renders any and all modals we might need throughout the application. This allows us to call modals from anywhere within the dashboard using, like you, a behavior subject that activates the modals. Here's a great article on using the ComponentFactoryResolver.
Update 1
So after realizing that "page" was actually a new browser window this method won't necessarily work. Using BehaviorSubjects will only update within the application context, so opening a new window creates a new application context, i.e. killing the BehaviorSubject of being a viable candidate to make this work. You'll need to have a service that is not instance specific. Web sockets as you mentioned would be a good alternative.
It is worth noting though that if it's possible to refactor the code to open modals instead of new windows, you could maintain the integrity of your Dependency Injection tree, and then use BehaviorSubjects to achieve this. Otherwise you'll need something outside of the application that is maintaining state.
I'm trying to update the DOM based on a click event, by using a service.
Specifically, I'm trying to use bootstrap's alerts to alert the user when they click on a particular button on the page. I'd like to be able to call notify.createNotification("Saved successfully") for instance, to tell the user that they have saved successfully.
I'm using ui-router and have managed to abstract the notification to a (isolate scope) directive and the accompanying template. The notification currently shows, updated, at the head of my application (all other views inherit this view) upon $scope.createNotification() (from within the click event). This works because the child views inherit the $scope property. Clearly though this pollution and abuse of the inheritance of the $scope is not ideal, hence I'd like to move it all into a service.
I've got as far as trying to use a factory to update the notification object, which contains state information for the alert, i.e. display:true/false, text etc. The trouble with the factory is it just returns where it's called from, I need to be able to update the parent.
...I feel I've done 9/10ths of the work on this, but that last 1/10th is really puzzling me.
Any help would be greatly appreciated.
Note: Guess I'm also looking for a 'best practices' here too, I mean $scope pollution works, but it's far from ideal. Thanks
This could be solved using pub/sub approach.
Create NotificationService that is used to send notifications. For callers it would look like NotificationService.alert({text: '..', ...})
Create <notification-area> component that subscribes to NotificationSerivce and displays notifications sent from anywhere.
NotificationService itself should implement pub/sub interface. Use any implementation of EventEmitter (like this one) or even angular.element to provide on(), off(), trigger() methods.
I use Backbone.js. I have popup1, which creates popup2. popup2 is component-like and when it closes it triggers the event 'school_address:saved'. I need to send a request to the server on 'school_address:saved' event. I placed the handler which do so in the view of the popup1 (its instance still exists), but I'm not sure that this place is appropriate, because views are responsible for template UI logic, aren't they?.
What do you think is the best place for such code? And what would it be if I used Marionette.js?
We have been using BackboneJS for a few years now and have wondered about similar cases in the past...
Since BackboneJS (unlike other JS frameworks)does not enforce any common way of doing this, I would generally say this really depends on your implementation and application flow.
However, since these are popups/"application modals", I think it would help if you consider the following based on your current need:
If it is ONLY popup1 that can instantiate and display popup2, then have popup1 listenTo the events triggered by popup2.
If you foresee your application flow changes, and possibly having popup2 display elsewhere, or independently, I would suggest you listen to popup2 events from a view at a higher level (possibly one you have a route for). this way, both of your popups will be "independent", and you could recycle their code more easily and support greater flexibility as your application grows.
I'm pretty new to this world so I need some clarifications on it. Maybe I could be wrong on the subject. So, feel free to correct me.
I'm studying how Marionette and Backbone work together. Oh yeah. Marionette gives us extension to Backbone. Really nice stuff.
The thing that is not obvious to me is when to use the routing mechanism provided by Backbone and when to use publisher/subscriber pattern by Marionette.
Is there any rule of thumb?
Here, Where to use event aggregator in backbone marionette?, a similar discussion but there is no advice on how using that or the other.
My take on the route management is explained in the free preview to my book on Marionette (http://samples.leanpub.com/marionette-gentle-introduction-sample.pdf)
Basically, my opinion (others don't necessarily share it) is that Backbone's routing should be used to configure the application state when the user "enters" the application via a URL. In other words, it will parse parameters, and call proper controller actions.
But once that initial state is setup, routing code should no longer be triggered, even as the user navigates through the application.
Here's an example:
The user enters arrives on the "contacts/2/edit". Backbone routing code will extract the 2 argument and call the edit controller action with that id parameter (which fetches that contact, displays the proper views, etc.). In other words, the initial application state is being configured.
The user clicks on the "show all contacts" link leading to the "contacts" URL. Here, I believe this modification should be handled through Marionette events (i.e. indicating the user wants to see all contacts). After all, we know what the user wants to do, and which URL fragment should be displayed. In other words, there is no reason for the routing code to get involved.
Note that this is my opinion, and other developers simply pass trigger: true when the user clicks a link. But as I explain in the book extract linked above, this tends to lead developers to create "stateless applications in javascript" (e.g. passing lots of parameters in the URL, even though they should be stored in the application's state). Ater all there is a reason that by default, Backbone's navigate method has trigger: false.
Derick Bailey (Marionette's creator) also discussed the issue here: http://lostechies.com/derickbailey/2011/08/03/stop-using-backbone-as-if-it-were-a-stateless-web-server/
Event aggregator is more useful for notifying things. (think small bits of feedback)
Message from server (updated record)
Let other models know things have changed
Lock everything down while saving until saved
Single Moment in time things
Router is for things where you want the state to be save-able (think separate page in a MPA)
Model Edit Page
Model View Page
Something that will stay until another event or activity changes it
If you are not sure if something is an event or a page, then think about it and ask that separate question.