This description at the moment is all theory, I don't have any code yet. I was hoping to bounce ideas off people.
I have a VueJS app, let's say a To Do app. It lists all of the things I need to do today. When I complete a To Do, I check a box in my Vue app and Axios fires of an Http Post to a .NET API end point. Let's say that API method has to do several things, like update several databases, execute a few stored procedures, etc. as in it can take a few seconds to complete. My Vue app gets a success response and I can then go on to check the next completed item.
If I have several things I've completed it could take several minutes of my day to check, wait, check and wait. Now I want to check several items or maybe even select all. I want to submit a list of items to the API, let them queue up and process in the background while I go about other business in the app. All the while, a panel in the app displays the items still being processed in the background. As each one completes in the API, a push notification occurs and the UI updates, removing the item from the list.
Does this sound doable? Would I have Vue listening for updates from the API? or would Vue periodically have to poll the API to see what it still has outstanding? What is the preferred way? The goal is to free up the user to keep working rather than watch paint dry.
#Connie, I can tell you from experience that it's really possible, with a few tweaks.
The first thing I'd do, is to add all the logic inside Vuex.
Making it really simple, the steps would be:
1. Create a vuex state called toDos, and I'd assume that each toDo would be an object containing a format such:
toDoModel = {
id: 1,
completed: false
}
API receives only 1 ID for updating
Create a vuex mutation for updating this toDos state:
updateToDo(state, toDoObject) {
const toDoObjectIndexOnState = state.toDos.findIndex(toDo =>
toDo.id == toDoObject.id)
//ToDo not found on state list
if (toDoObjectIndexOnState == -1) {
state.toDos.push(toDoObject)
return
}
state.toDos[toDoObjectIndexOnState] = toDoObject
}
Create a vuex action called updateToDoState, to perform the Axios call and update state:
updateToDoState({commit}, toDoId){
// Call API on Axios, assuming `data` as the key for returning the toDo with updated info
response = axios.post(ENDPOINT, toDoId)
.then({data: toDo} => {
if (!toDo) return
// Call mutation
commit('updateToDo', toDo)
})
Make the call on your main Vue Component to call the updateToDoState action on each checklist click to update toDo state
API can receive multiple IDs for updating
(you have two approaches:
- Have a mutation to change each toDo per time and the action would loop through them all
- Have the action to pass thewhole list and the mutation would take care of updating the store object for each returned Id
Here my examples assume the fist option
Create a vuex mutation for updating this toDos state:
updateToDo(state, toDoObject) {
const toDoObjectIndexOnState = state.toDos.findIndex(toDo =>
toDo.id == toDoObject.id)
//ToDo not found on state list
if (toDoObjectIndexOnState == -1) {
state.toDos.push(toDoObject)
return
}
state.toDos[toDoObjectIndexOnState] = toDoObject
}
Create a vuex action called updateToDosState, to perform the Axios call and update state:
updateToDosState({commit}, toDosIdsList){
// Call API on Axios, assuming `data` as the key for returning the toDo with updated info
response = axios.post(ENDPOINT, toDosIdsList)
.then({data: toDos} => {
if (!toDos) return
// Call mutation
toDos.forEach(toDo => commit('updateToDo', toDo))
})
Make the call on your main Vue Component to call the updateToDoState action for batch updating the toDos state
In case any part of this logic / code is not 100% clear, just let me know and I can update here!
Related
Options for users can be turned on or off through checkboxes on the front end of a webApi Web-app:
Users Func A Func B Func C Func D
========================================
James o o o o
Mary o o o o
Clicking on a checkbox (o) calls an API end-point that updates that function setting for a user.
Users need to be sent an email when their options change BUT sending a separate email every time a checkbox is clicked isn't desireable for obvious reasons. I know I need to implement a user-timer that delays actaully sending the user an email until, say, a minute after a given user's option was last changed.
The code called every time a function was clicked would look something like this:
private void UpDateUserNotificationsSettings(UserModel model)
{
// Call Api endpoint to update the setting.
// Create a timer if one doesn't exist for this user.
// (I need the code for this bit)
_delayTimer = // (create if doesnt exist) new System.Timers.Timer();
_delayTimer.Interval = 60000;
_delayTimer.Elapsed += (o, e) => sendEmailMethod();
_delayTimer.Start();
}
However, I (think I) need a separate timer for each user, and am not sure how to go about doing that.
I agree with AlwaysLearning. The idea is to create a background running service that runs at set intervals or always running. You can use Hangfire for this https://www.hangfire.io/. Hangfire provides clean way of managing background jobs. You can write a CRON expression for a job. There are many tools like http://corntab.com/ to generate desired CRON expression.
You will have to maintain state of Functions change in database for each user that can show what changed from previous settings. If changes made falls in the window, then notify users.
I hope this helps to help you achieve final working code.
I'm newbie with SignalR and want to learn so much. i already read beginner documents. But in this case i've stucked. what i want to do is when a user got new message i want to fire a script, like alert or showing div like "you have new mail" for notify the recieved user. And my question is how can i do that ? is there anyone know how to achieve this ? or good "step-by-step" document? i really want to work with SignalR.
ps: i'm using Visual Studio 2012 and MsSQL server
edit: i forgot to write, notification must be fired when message created to DB
Thank you
In your Scripts use the following, naturally this is not all the code, but enough based off tutorials to get you going. Your userId will be generated server side, and somehow your script can get it off an element of the page, or whatever method you want. It runs when the connection is started and then every 10 seconds. Pinging our server side method of CheckMessage() .
This js would need refactoring but should give you the general idea.
...
var messageHub = $.connection.messageHub;
var userId = 4;
$.connection.hub.start().done(function () {
StartCheck();
}
//Runs every 10 seconds..
function StartCheck()
{
setInterval(messageHub.server.checkMessage(userId,$.connection.hub.id), 10000);
}
This method takes in a userId, assuming your db is set up that way, and grabs them all from your database; naturally the method used is probably not appropriate for your system, however change it as you need to. It also checks if the user has any messages, and if so sends down another message to our SignalR scripts.
public void CheckMessage(int userId,int connectionId)
{
var user = userRepo.RetrieveAllUsers.FirstOrDefault(u=>u.id == userId);
if(user.HasMessages)
{
Clients.Group(connectionId).DisplayMailPopUp();
}
}
Finally this message, upon being called would run your code to do the 'You have Mail alert' - be it a popup, a div being faded in or whatever.
...
messageHub.client.displayMailPopUp = function () {
alert("You have Mail!");
};
...
Hopefully this helps - I recommend the following links for reading up and building your first SignalR app:
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-getting-started-with-signalr-20-and-mvc-5
And a smaller sample: http://code.msdn.microsoft.com/SignalR-Getting-Started-b9d18aa9
Some architecture dilemma:
I'm using WPF as my client-side, EF Code First as my Data Access Layer, and WCF to connect between those. My probelm is hou to reupdate the UI after I did some changes to the DB, for example:
User insert new "Person" on the UI (ID=0)
User save the "Person" to the DB (ID=10, for example)
When talking about one user it's very simple - I can return the ID and update my UI as well (so next change to this person will be considered as "Update"), but what about adding more than one user at once, or updating other properties that was calculated on the server? should I return the whole graph? not to mention is very hard to remap it on the client side.
Before CodeFirst we could use STE, but it has it's own problems. anyone knows about known CodeFirst approach?
Would be happy to hear your voice.
Thanks!
You can send as the request to your wcf service the dateTime of your last update in client-side. But in the server-side you take all Persons which was updated/added after that dateTime and return it as the result. In this way you will get only modified/added Persons from your server-side.
So add lastUpdate collumn to your entity Person.
EDIT 1
If you want to server update the information in client but not client ask for news from server.
You can use the way like it works in Web Programming.
(1)The client-side asks server-side - "hey, my last update was at 20:00 10.02.2013", then server looks into DB - "is news after 20:00 10.02.2013?" if yes:
a) returns the news to the client
if no news in DB:
b) He dont returns null, but he does Thread.Sleep(somevalue). He sleeps then repeats the query to db and asks is "there news in db". So it's all repeats untill the news in DB will apear. After news in db appears he return the List<data> which is in updated after the dateTime. After that client gets the data he goes back to the point - (1).
So you dont make a lot of requests to the server but making only one request and wait for the news from the server.
Notice 2 things:
1) If the client waits too long the server side will throw the exception(dont remember actually the error code but it's not important now), so you have to catch this exception on client-side and make a new request to server-side. Also you have to configure on the server-side as long as you can wait time, to minimize the amount of requests from client.
2) You have to run this data-updater in the new thread not in the main, where the application runs.
How it will looks from the code(it may not work, i just want to show you the logic):
Server side:
public List<SomeData> Updater(DateTime clientSideLastUpdate)
{
List<SomeData> news = new List<SomeData>();
while(true)
{
List<SomeData> news = dbContext.SomeData.Where(e=>e.UpdateDateTime > clientSideLastUpdate).ToList();
if(news.Count()>0)
{
return news;
}
}
}
Client-side:
public static void Updater()
{
try
{
var news = someServiceReference.Updater(DateTime clientSideLastUpdate);
RenewDataInForms(news);
Updater();
}
catch(ServerDiesOrWhatElseExcepption)
{
Updater()
}
}
And somewhere in the code you run this updater in the new thread
Thread updaterThread = new Thread(Updater());
updaterThread.Start();
Edit 2
if you want update by one request all entities but not only SomeData then you have to add Dto object which will contain the List of every entities you want to be updatable. The server-side will complete and return this Dto object.
Hope it helps.
What I have is an AJAX form on a View that makes a call to the server. This call perform n number of tasks where n is a number decided by records in a database (typically no more than 10 records). Each record corresponds to a Build Definition in TFS so what I am trying to do is get all of these Build Definitions, queue them in TFS, and as each build completes update the UI so that user knows which builds have completed.
Unfortunately I am not sure about how best to do this. I was thinking something along these lines:
foreach (var dep in builds)
{
TFS tfsServer = new TFS(TFS_SERVER_ADDRESS);
IBuildServer buildServer;
int id = tfsServer.QueuBuild(dep.TeamProject, dep.BuildDefinition);
string teamProject = dep.TeamProject;
Task.Factory.StartNew(() => GetBuildStatus(teamProject, id, tfsServer));
}
The task that is called is:
private void GetBuildStatus(string TeamProject, int BuildID, TFS Server)
{
Server.GetBuildStatus(TeamProject, BuildID);
AsyncManager.OutstandingOperations.Decrement();
}
The problem here is that my Completed method isn't going to get called until all of the builds have completed. How would I go about feeding data back up to the UI a piece at a time?
It is also worth mentioning that the GetBuildStatus method looks like this:
do
{
var build = buildsView.QueuedBuilds.FirstOrDefault(x => x.Id == BuildID);
if(build != null)
{
status = build.Status;
detail = build.Build;
}
} while (status != QueueStatus.Completed);
return detail.Status.ToString();
Given that the duration of a build will be longer than the timeout for an HTTP request you cannot leave the browser waiting while this happens. You need to return a page and then poll for updates from that page using AJAX. Typically you'd have a timer in javascript that triggers a regular call back to the server to get the updated status information.
But, since you are using .NET you could also consider trying SignalR which lets you use long polling, server sent events or web sockets to wait for updates from the server and it wraps it all up in some easy to implement .NET classes and Javascript.
When the user clicks on a link to generate report I make an AJAX call which generates a pdf file in the background.Now the files are huge running upto 10mb or more.So it takes some time.In the mean time the user should be able to navigate other links as if nothing has happened.So I need to implement in such a way that the pdf generation process gets started & user doesn't have to wait for the process to finish.Is this possible?I am using AJAX Pro with c# with dot net framework 2.0
The problem here is that as soon as the AJAX activity begins the browser enters into a hung stage & the user has to wait although he clicks on a different link.
I would probably create a 'queue' or an 'inbox' for the user ...
start your pdf generation routine with a ThreadPool.QueueUserWorkItem (you would also need to modify your generation method to output to their inbox)
then on each http request check that inbox and notify the user of the item ... you can always poll the server on an interval or somthing
Sure, but once the user navigates to another page, the Javascript that is waiting for the Ajax response is no longer running, so that request is lost. You'd have to either find a way to keep that page open (using frames or exclusively Ajaxified navigiation), or find a way to store the response and notify the user of its completion on the next page view. For instance, storing a session variable that indicates that the operation is completed, or storing it in a database with (perhaps) an "unread" boolean value.
You can have asynchronous Ajax call with which you can do other tasks while response objects returns from the Ajax page.
Here is some example, testAjax.aspx is the Ajax page here :
http_request.onreadystatechange = function() { alertContents(http_request); };
http_request.open('GET', 'testAjax.aspx?', true);
http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
http_request.send(null);
function alertContents(http_request)
{//debugger;
if (http_request.readyState == 4)
{
if (http_request.status == 200)
{
var vResult;
vResult=http_request.responseText;
//Write your logic after successful Ajax call here.
}
else
{
alert('There was a problem with the request.');
}
}
}