How to refresh view without refreshing page in an angularjs app - javascript

I have a simple app that lists contact reports,
in it i made a list view that fetches data from Mongolab.
On that i also made an input form that makes a new contact report in the list when submitted
the function i use in the controller is modelled from angular's example on their site :
app.factory('Contact',function($mongolabResource){
return $mongolabResource('contacts');
});
function ContactCreateCtrl($scope,$location,Contact) {
Contact.save(contact,function(){
$location.path('/');
});
};
the $location.path() is the callback that reloads the page.
how do i rewrite this so that when the data has been submitted ( .save() is successful ) the view reloads without the page reloading?
i tried deleting and then redefining the array but doesnt seem to work :
Contact.save(contact,function(){
delete $scope.contacts;
$scope.contacts = Contact.query();
});
i would like to implement this on the delete function as well. Can somebody point me to where i can learn this?
Much thanks for any help

Okay, I updated your fiddle to fetch the value from the database: http://jsfiddle.net/joshdmiller/Y223F/2/.
app.controller( 'MainCtrl', function ( $scope,Contact ) {
$scope.updateContacts = function () {
Contact.query( function( data ) {
$scope.contacts = data;
});
};
$scope.save = function( newContact ) {
Contact.save( newContact, function() {
$scope.updateContacts();
});
};
// The initial data load
$scope.updateContacts();
});
Two things to note:
(1) I moved your Mongo query into a function so that it can be called again when the new record is created;
(2) the $mongolabResource expects a callback to be executed on success; your app flickered because you didn't provide one. In other words, from the time you called the query to the time fetch was complete, your list was empty. Instead, we want it to change only when we get the new data. I changed that too.
In terms of adding the item manually or fetching from the database, best practice is based on the use case and there are trade-offs. But for small data such as this, just fetch from the DB.

got this to work, but still not sure about pushing into the array at the scope, would be better if we could fetch from database
function ContactCreateCtrl($scope,$location,Contact) {
Contact.save(contact,function(){
$scope.contacts.push(contact);
});
also i would need the _id object thats automatically generated by the db, for linking purposes. this method doesnt give me the _id, any insights?

I'm sharing my answer for how I cleared data in the view after sign out from firebase using the firebase auth service. The data was still persisting after calling $scope.currentUser = null; on the signout method. Had to reload to see the data change. Not best UX.
$scope.getCurrentUser = function() {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
$scope.currentUser = user;
} else {
// No user is signed in.
$scope.currentUser = null;
console.log('user not signed in.');
}
});
}
$scope.getCurrentUser();
$scope.signout = function() {
firebase.auth().signOut().then(function() {
// Sign-out successful.
console.log('signed out success');
$scope.getCurrentUser();
}, function(error) {
// An error happened.
console.log(error);
});
}
so calling the getUserData method and making the currentUser = null there updated the view without a reload. this is an example using firebase but with a few adjustments it might apply to your needs for clearing data from view without a full page reload. Firebase does the heavy lifting here of clearing out the user object but my view doesn't care until I check again in my getCurrentUser method to see if there is still a user and if not then clear it from the $scope without reloading the view.

Related

Mongoose save() not saving changes

I have a fully functioning CRUD app that I'm building some additional functionality for. The new functionality allows users to make changes to a list of vendors. They can add new vendors, update them and delete them. The add and delete seem to be working just fine, but updating doesn't seem to be working even though it follows a similar method I use in the existing CRUD functionality elsewhere in the app. Here's my code:
// async function from AXIOS request
const { original, updatedVendor } = req.body;
let list = await Vendor.findOne({ id: 1 });
if (!list) return res.status(500).json({ msg: 'Vendors not found' });
let indexOfUpdate = list.vendors.findIndex(
(element) => element.id === original.id
);
list.vendors[indexOfUpdate].id = updatedVendor.id;
list.vendors[indexOfUpdate].name = updatedVendor.name;
const updated = await list.save();
res.json(updated);
The save() isn't updating the existing document on the DB side. I've console logged that the list.vendors array of objects is, indeed, being changed, but save() isn't doing the saving.
EDIT:
A note on the manner of using save, this format doesn't work either:
list.save().then(res.json(list));
EDIT 2:
To answer the questions about seeing the logs, I cannot post the full console.log(list.vendors) as it contains private information, however, I can confirm that the change made to the list is showing up when I run the following in the VendorSchema:
VendorSchema.post('save', function () {
console.log(util.inspect(this, { maxArrayLength: null }));
});
However, the save still isn't changing the DB side.
Since you are using nested objects, Mongoose will not be able to detect the changes made. You need to mark the modified as an object before the save
list.markModified('vendors');

Ember How to retain query parameters while manually refreshing the page?

I am using ember 2.7.0.while manually refreshing the page ember clears the ember-data as well us query parameters, so i am unable to load the page in setupController while refreshing. Is there any possible way to retain both model & query parameters, at least retaining query parameter would be fine to reload my page.
route.js
model(params) {
return this.store.peekRecord("book",params.book_id);
},
setupController(controller,model,params){
if(!model){
//fetch book record again if the model is null
}
controller.set('isdn',params.queryParams.isdn);
controller.set('book',model);
}
Any help should be appreciable.
Edited setupController as per Adam Cooper comment :
setupController(controller,model,params){
var isdn = params.queryParams.msisdn;
controller.set('isdn',isdn);
if(!model){
this.store.findRecord('book', isdn).then((customer) => {
this.set('book',customer);
},(resp,status) => {
this.set('errorMessage', `Book with this ${isdn} does not exist.`);
this.set('book', []);
});
}else{
controller.set('device',model);
}
}
Page gets rendered before "findRecord" returning promise.Is there any way to stop page rendering till find record resolves the promise?
You are setting in route properties instead of controller..
setupController(controller, model, params){
var isdn = params.queryParams.msisdn;
controller.set('isdn', isdn);
if(!model){
this.store.findRecord('book', isdn).then((customer) => {
controller.set('book', customer);
}, (resp, status) => {
controller.set('errorMessage', `Book with this ${isdn} does not exist.`);
controller.set('book', []);
});
}else{
controller.set('device', model);
}
}
Only the controller properties will decorate template.
You can even try the below, why don't you give opportunity to model hook to resolve since that will wait for the Promises to resolve.
model(params) {
var result = this.store.peekRecord("book",params.book_id);
if(result !== null){
result= this.store.findRecord('book', params.book_id)
}
return result;
}
setupController(controller,model){
controller.set('book',model);
}
You will need to generate an actual controller for your route and then define a queryParams property in the controller. It looks like the query param you're trying to hold onto is isdn so your controller should look something like:
export default Ember.Controller.extend({
queryParams: ['isdn']
});
"manually refreshing the page ember clears the ember-data as well us query parameters"
Once you completely refresh the browser, a new ember app instance is created and hence ember-data cannot be retained. Ember-data is just for the app on the UI, once ember is exited it will not be retained.
"as well us query parameters"
your query params are part of your url and it should not get cleared. Make sure the below two are present
Include queryParams in ur controller i.e.
queryParams: ['param1', 'param2']
And in your route make sure you have done
queryParams : {
param1: {
refreshModel: true
},
param2: {
refreshModel: true
}
}
"Page gets rendered before "findRecord" returning promise"
You are not doing something right, is the adapter, model, serializer etc defined correctly(if required) in order to use findRecord? Just to debug return a plain object and make sure ur setupController is called before rendering. i.e.
model() {
return {dummy: 'dummy'};
}

In a React.js app, an API calls fails in the store, how do I transmit that back to the view?

https://cask.scotch.io/2014/10/V70cSEC.png
^ According to this flow, I have to make a change in the store, which is then picked up by the view.
More specifically, I am trying to delete a user, but when the store gets an error from the DB, I want to show a modal saying the error occurred. Would the right way of transmitting the message be done through the store variables and then picked up in the view on the getStateFromFlux method?
userStore = {
initialize: function(options) {
// other variables
this.userDeletionError = false;
},
deleteUser: function(payload) {
Axios.delete(DBURL)
.then((response) => {
// succeeds
})
.catch((error) => {
// other error handling
this.userDeletionError = true;
});
}
}
If I understand well you are using an ajax call in a store, this is an antipattern. The right way to do is make the call in the action file then transmit it in the store.
To answer your question the flux-pattern should look like that (not sure if it match 100% your use case).
View => User want to delete a 'user', he clicks on the delete button
View triggers an action.
Action triggers an ajax call 'delete this user'
Action receives the answer and transmit it to the store (here you are using the react dispatcher , example below:
MyAjaxCall.then(function(answer) {
Dispatcher.handleViewAction({
actionType: Constants.ActionTypes.DELETE_USER,
result: answer
});
});
5.Your store is catching the ajax answer still through the dispatcher (example below:
MyStore.dispatcherIndex = Dispatcher.register(function(payload) {
var action = payload.action;
var result;
switch(action.actionType) {
case Constants.ActionTypes.USER_DELETE:
registerAnswer(action.result);
MyStore.emitChange();
break;
}
return true;
});
You can see that your store will trigger registerAnswer(), in this function you can check if the ajaxcall has been executed (I mean is the user deleted or not) and accordingly build the object. Here there is two way to tell your view about the answer status 1. you build an dataAnswer object with a field message for example and then your view can check it 2. you emit a special event.
I prefer the first way if find it more generic.
Store emitChange and your view catch the event (example below:
componentDidMount: function() {
MyStore.addChangeListener(address, this._onDeleteUser;
},
Then your view check the 'message' field you filled in the store accordingly to the answer and you can render whatever is appropriate.
I hope it's clear. Here is an example of store in case you need it. https://facebook.github.io/flux/docs/todo-list.html#creating-stores
To resume, your approach is good except doing the ajax call in the store. Don't do that it's really bad.
Hope it helps

How to return angularFire $on value in angular services?

I have following code in a controller. This code get the current user's task list.
I want to pack this into a angular service so I can call this easily whenever I want in any controller. example Task.getList()
In Controller
$scope.tasks = {};
$firebaseSimpleLogin(instance).$getCurrentUser().then(function(user) {
$firebase(instance).$child('users/' + user.uid + '/tasks/incomplete').$on('child_added', function(taskId) {
$scope.tasks[taskId.snapshot.name] = $firebase(instance).$child('todos/' + taskId.snapshot.name);
});
});
but I facing a problem here. When I call it in controller. it always returns undefined
In services
getList: function() {
$firebaseSimpleLogin(instance).$getCurrentUser().then(function(user) {
$firebase(instance).$child('users/' + user.uid + '/tasks/incomplete').$on('child_added', function(taskId) {
return $firebase(instance).$child('todos/' + taskId.snapshot.name);
});
});
}
You can simplify this problem by breaking things down into smaller pieces. You're trying to return from an inner callback, which is really tricky to deal with. Rather than trying to get everything all loaded in at once, we can separate out these responsibilities.
A nice way of handling this is to separate out the auth code ($getCurrentUser), and the task code (getList).
Here's a sample to a Plunker app I wrote. It will load the tasks for each user who logs in.
http://plnkr.co/edit/M0UJmm?p=preview
I have an Auth factory that handles my Auth code.
.factory('Auth', function($firebaseSimpleLogin, Fb, $rootScope) {
var simpleLogin = $firebaseSimpleLogin(Fb);
return {
getCurrentUser: function() {
return simpleLogin.$getCurrentUser();
},
// see plunker for the rest
};
})
I also have a TaskStore that handles adding, removing, and syncing of my user's tasks. The store takes in a user's id to initialize. It will then know how to grab the tasks listed by the user.
Then in my controller I can use these together to load a user's tasks. Inside of the promise returned by the $getCurrentUser function, the TaskStore with the user's id can get initialized.
This way my TaskStore is re-useable and independent of my Auth code and a bit easier to manage.

Updating AngularJS View when user action has completed

I'm writing some small exercises to teach myself AngularJS and I'm trying to write some simple user Authorisation tasks. I have a form to collect/input a username and password, these are then sent to a rest service using $http and CORS (as my REST service is running on a different port), they are checked and if there is a match I return a UUID and create a token and I $broadcast a loggedIn value to true that is on the $rootScope, something like this.
// this is in a service I call 'authService'
this.login = function (user) {
return $http({method: 'POST', url: 'http://127.0.0.1:3000/login', data: user})
.then(function (response) {
// set up a local storage token
storageService.setLocalStorage('token', response.data[0].uuid);
// broadCast is loggedIn - we have a match
$rootScope.loggedInUser = true; // this is set to false at the .run() of the app
$rootScope.$broadcast('LoggedIn');
return 1;
}, function () {
// return http code later
return 0;
});
};
this.getLoggedIn = function () {
return $rootScope.loggedInUser;
};
Now in a separate menu view I have the following condition (the authService is added as a dependancy on the menu controller):
<div id="logIn" ng-show="!authService.getLoggedIn()">...</div>
Now when I load the app for the first time the condition is correct, however I want this condition to update should a user log in correctly (so the div) isn't shown. In the menu controller I have the following code, none of it seems to do anything?
$scope.$on('LoggedIn', function () {
authService.getLoggedIn(); // doesn't update the view?
console.log($rootScope.loggedInUser); // returns true
console.log(authService.getLoggedIn()); // returns true
});
$scope.$watch('loggedInUser', function () {
console.log('loggedInUser has changed ' + $rootScope.loggedInUser);
// This runs once when we set $rootScope.loggedInUser in the .run() of the app, output is: 'loggedInUser has changed false'
// then when we have successfully logged in again, output is 'loggedInUser has changed true'
});
Okay, so the condition on the <div> in my menu view doesn't update when I changed the $rootScope.loggedInUser, I'm doing something wrong in my approach, can someone give me some advice or correct my approach to this. Thanks
You don't have to do anything special in Angular to update view when some prop is updated, provided it is in the correct scope. I provided a Plunkr for you which demonstrates that you don't have to do anything special to refresh the view.
http://plnkr.co/edit/B6kUwJdA4lRKkjAItSFO?p=preview
You don't have to do watches, you don't have to do anything. That is the power of Angular. Also, it is weird that you set stuff in rootscope. My advice for you is to look at my example and revise/restructure your code. Also, having stuff like this:
ng-show="!authService.getLoggedIn()"
Is not the recommended way of doing things. You can have a controller, in which you say:
$scope.userLoggedIn = autService.getLoggedIn();
and then in your view:
ng-show="userLoggedIn"
You can also take a look at this plunkr:
http://plnkr.co/edit/aRhS0h7BgpJJeRNvnosQ?p=preview

Categories