Transfer alerts or notifications to next route with Angular 5 - javascript

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.

Related

Route wait for async data before redirect

I came from Angular world where resolve data for route is realy easy and out of the box. How can I do the same action in react?
Add async data loder for route so that redirect will after data is ready? I am using react-router.
I saw somewhere that I could use the Router.run() method to write own callback, but I can not find it in the official documentation.
I do not want to use componentWillMount because sometimes the data loads in seconds and the page flashes.
I know that the topic appears many times, but I can not find a satatory answer. I will be very graceful for help with the solution.
In my opinion, waiting for data before redirecting may cause bad user experience, because the user will need to wait something and he may not understand that the application is actually doing something in background.
It is better to render the UI and then take care of delivering the actual data to the page.
I usually use componentDidMount for such things.
E.g.:
Component is rendered without the data
Data is fetched within componentDidMount
The UI is then updated by setting the state
Depending on the specific look of the component, a loading icon can be shown as well.

Angular 2 - single service provider across multiple browser windows

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.

Update the DOM based on click event using service

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.

How do I run a callback function after a SailsJS REST Blueprint?

Is there a best practice for a callback after a blueprint route in SailsJS 11.x?
I have an API where Users can submit Ideas. After the Idea is created, if it's the User's first Idea they should get a Badge. As far as I can tell there are two places to put this functionality.
Copy/Override the Sails rest blueprint, add the functionality to the Idea.create() callback function.
Add a lifecycle callback for the afterCreate action on the Idea model.
I'm assuming any functionality I write in terms of granting badges should be put in a Service so it can be shared among the different models. It seems like option 1 wouldn't be as tightly coupled, but would lead to fatter controllers.
Overriding the "create" blueprint will mean that every model will use that code when handling a request to their POST /<modelName> route, which is probably not what you want. The lifecycle callback will work fine for granting the badge, but if you're looking to modify the response based on whether or not a badge was granted, this approach won't work--but you could easily send a socket notification from the lifecycle callback, using User.message().
The third option is just to override the create action in IdeaController.js. There's not all that much to it, especially if you let Waterline handle most of the field validation for you. In essence, it's just:
create: function(req, res) {
Idea.create(req.params.all()).exec(function(err, idea) {
if (err) {return res.negotiate(err);}
// ... do some stuff with the idea and send a response ...
});
}
I would go for the second option, however you should note that afterCreate gets triggered when testing via barrels and adding items, which might be an issue for the logic in afterCreate. (Ex: A user has 3 ideas, 1 badge from the first idea, you add them to barrels fixtures. When barrels is adding the first idea it will trigger the afterCreate then it will add the badge from the fixture resulting in the user having 2 duplicate badges.)

Marionette Event Aggregator vs Backbone Router

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.

Categories