AngularJS view not updating when $scope.$apply is called - javascript

Here's the workflow:
A user's account page should list all of the object owned by that user.
Next to each object is a "delete" button that opens a Bootstrap modal. The modal asks the user if they truly want to delete the object, and if they confirm, then the modal should dismiss, the object should be deleted, and the view should update to reflect the deletion.
I am dismissing the modal using the data-dismiss attribute on the confirmation button inside of the modal.
Here is the function in my controller that deletes the object and (should) update the view:
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
$scope.$apply();
},
error: function(object, error) {
// handle error
}
});
};
However, I have to refresh the page to see the updated view with the object removed.
Is there another way I should be using $scope.$apply?
EDIT: I found a workaround by creating a new $scope level function to load my collection of objects. Previously, this was done when the controller is loaded (not attached to any particular function.
In other words, my old code did this:
.controller('AccountCtrl', function($scope) {
var query = new Query('Object');
query.find().then(function(objects) {
$scope.objects = objects;
});
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
// do something
}
});
}
});
Now I've wrapped the find code in a $scope level function, which I can call explicitly when an object is destroyed:
.controller('AccountCtrl', function($scope) {
$scope.getObjects = function() {
var query = new Query('Object');
query.find().then(function(objects) {
$scope.objects = objects;
});
}
$scope.getObjects(); // call when the view loads
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
$scope.getObjects(); // call again when an object is deleted
}
});
}
});
I'm still hoping there is a cleaner solution to this, i.e. one where I don't have to manually update by object collection.

In your success you have to modify the local $scope.objects.
In you last exemple you should try this (code not tested, but this is how it should look):
$scope.deleteObject = function(object) {
object.destroy({
success: function(object) {
var objectIndex = $scope.objects.indexOf(object);
$scope.objects.splice(objectIndex,1);
}
});
}
In your controller you take the responsibility for updating your model. Angular take the responsibility for updating the view. Calling again the $scope.getObjects() is a way to do it. But the cleaner way is to implements your update of the model in case of success. You should also give an error method in case the server response is an error.
If you have correctly bind the collection to your view, i should update after a delete. Tell me if it helped you out.

Related

Angular Bootstrap Modal binding issue

I'm working with Angular Bootstrap and actually I'm trying to update correctly my model using a Modal.
Here is the very simple code:
controller:
function open(room) {
var roomModal = $uibModal.open({
templateUrl: 'room-modal.html',
controller: 'RoomModalController',
controllerAs: 'modal',
resolve: {
room: room
}
});
roomModal.result.then(function (response) {
RoomsService.update({
roomId: response._id
}, response).$promise (etc...);
});
}
Modal Controller:
var vm = this;
vm.room = room;
vm.save = function () {
$uibModalInstance.close(vm.room);
};
vm.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
Basically I'm resolving the Room to get a few information about it and then if needed I wish to update a few information about the room within the modal.
It is working fine unless I do not want to update some information and I click "close".
What happen is: if I updated a few information and then I click "close" the information has not been updated on the database (OK) but has been updated in the main view... Because Angular bind the Modal information to the main view...
It is quite weird because I'm passing those information to a separate scope (vm) and unless I do not click save I should not expect this behavior...
What I'm doing wrong here?!?
In your RoomModalController deep copy the room object to prevent when updating that the model is also updated.
vm.room = angular.copy(room);
Now this object will take care of the modal binding, and will not interfere when changed to your root scope vm.room object.
To finalize saving this data, you have to save the vm.root modal object to your database, and also update the root scope vm.room object according these changes made in the modal.

Is it possible within Backbone.js to reload route with history.loadUrl and pass in a string to be used as success message?

I am having trouble working out if it is possible to pass a string into a view using backgone.history.loadUrl?
//My current method of reloading the view
Backbone.history.loadUrl(Backbone.history.fragment);
I would like to use this method if possible so that it renders the view from the Backbone router rather then rendering my view again from within itself and passing it in as the options for that view.
//Destroy old view
//Render view again
var view = new myView("success msg");
Here's a simple way to extend Backbone.history with a reload function:
_.extend(Backbone.history, {
reload: function(){
return this.loadUrl(this.getFragment());
}
});
Don't do this if you're writing a library/plugin.
Then calling Backbone.history.reload(); anywhere will call the route again.
Is it a good way to re-render a view? No.
If you want to re-render a view within itself, do that, don't reload the whole route.
var View = Backbone.View.extend({
// events, etc. ...
render: function() {
// rendering
if (this.success) {
// show success message
}
return this;
},
onMyEvent: function() {
// here, a re-render with a success message is necessary
this.success = true;
this.render();
}
})

Save form data in view

how can i save data from 1 view to another in angularjs?
i did $rootScope
From what I see, you use 2 different controllers for each view (or one for the view and none for the root view).
The problem is that Angular can't share data between controllers like that.
You either have to use a service/factory, or use the rootscope, but not as you did, rather with broadcast and emit
If I were you I would use a service.
EDIT Here you go, a service for you :
(function() {
'use strict';
angular
.module('YourModuleName')
.factory('CountriesService', CountriesService);
CountriesService.$inject = ['Your', 'dependencies', 'here', 'in', 'string'];
/* #ngInject */
function CountriesService(your, dependencies, here, not, in, string) {
var service = {
setCountries: setCountries,
getCountries: getCountries
};
var vm = this;
vm.countries = []; // Or maybe an object ?
// ... List of other variables you need to store.
return service;
////////////////
function setCountries(listOfCountries) {
vm.countries = listOfCountries;
}
function getCountries() {
return vm.countries;
}
}
})();
This will store your variables. In your controller you add CountriesService as a dependency, to save you use CountriesService.setCountries and to load you use CountriesService.getCountries. Be aware that refreshing the page will delete all the data !
EDIT NUMBER 2
If you're scared of John papa guidelines, here is a simple service you can use in the same file you put your controller :
app.factory('CountryControl', function(your, dependencies) {
var service = {
setCountries: setCountries,
getCountries: getCountries
};
this.countries = []; // Or maybe an object ?
// ... List of other variables you need to store.
return service;
////////////////
function setCountries(listOfCountries) {
this.countries = listOfCountries;
}
function getCountries() {
return this.countries;
}
});
I have an app that does this more or less. A service fixes this nicely AND creates a mechanism such that you can do this anywhere in your app.
First, I would recommend not trying to manage this with scope. Just put an object on your controller (myFormObj), and add the properties you want to it (name, rank, serialnumber, etc).
Then bind the input fields of the form, to the properties in that object (as opposed to scope vars). So your ng-model things would look like myCtl.formObj.name, and so on.
When the user triggers the event that changes the view, save a COPY (angular.copy) of that formObj off to the side, usually in a Service (think FormStateService or something). FormStateService could do nothing more than hold a simple array.
this.forms = { 'TheNameOfYourForm' : theFormObjToSave };
So, when the user triggers that event that leaves the form, you just do this:
formStateSvc.forms [ 'NameOfMyForm' ] = angular.copy ( theFormObj );
When the user comes back to the original view and the controller initializes, you just ask the formStateSvc:
if ( 'NameOfMyForm' in formStateSvc.forms ) {
this.formObj = formStateSvc.forms [ 'NameOfMyForm' ];
}
Voila, your old form state is restored.
More robustly, you could create "addForm, removeForm" methods etc, you could ensure against things like undefined, and you could make the rebind to the former state implicit (when your form's controller inits, just ask it to restore the state if there's any to restore). So your controller would just have:
this.formObj = formStateSvc.rebindOldDataIfItExists ( 'MyFormName' );
You get the idea.
A simple approach is to create a value provider object and publish it on scope:
//Create value provider object
app.value("FormObj", {});
app.controller("myController", function($scope, FormObj) {
//Publish on scope
$scope.FormObj = FormObj;
});
Then have the ng-model directives use that object:
Name <input ng-model="FormObj.name"><br>
Rank <input ng-model="FormObj.rank"><br>
SerialNum <input ng-model="FormObj.ssnum"><br>
The value object is a singleton which persists for the life of the application. Changes to the contents of the object will be retained and available to other controllers and will survive changes to the view.
The DEMO on PLNKR

How to do dynamic Helpcontext with anchor scroll in angularjs?

Here I created a sample for a dynamic Help Context. Based on the page it will load dynamic help data which is working fine.
In local $anchorScroll is also working fine.
What I need exactly is while I click the help button I need to set anchorScroll id inside this function $scope.HelpFunction. Can any one help me on this.
example
Sample Link
Maybe you are trying to do something like in this plunk which is a fork of yours. Click home link then click the help button; it should scroll to foo4.
I changed your service from:
.service('myService', function() {
var data = {};
return{
setData: function(val){
return val;
}
}
if(data == {})
{
console.log("inside")
data = {'a':'foo1'};
}
console.log(data)
return data;
})
to
app.service('myService', function() {
var data = {};
return{
data: data,
setData: function(key, val){
data[key] = val;
}
}
});
As that was the malfunctioning part of your code (no api to access the data object).
Also since objects are passed by reference in JavaScript, you don't need a setter on data, you can do direct assignments. Moreover, instead of a service you can also just use a angular value provider and inject that as a dependency where you want to share that object between different components of your app.

File and code structuring in DurandalJS

I'm trying to figure out a way to structure my app in a way that the API interactions are independent from my views and viewmodels.
At the moment, my ajax calls (get, add, save, remove, etc) and models (User model, Message model) are inside my view models, but in the future, we'll have a mobile app that will be a bit different from the desktop app, so I'd like to keep these actions accessible in one place.
I've seen people use a 'services' folder where they have models that handle loading and storing data, but haven't seen a complete structure that also includes handling new and current data.
Let's say I have a separate 'profile page' shell that includes a 'messages' tab and a 'user details' tab. This section needs the following:
get user details
get messages
User Model
Message Model
add/edit/remove message
edit user details
How would I go about structuring this? Individually by component (messages with model + get + add/edit/remove and user with model + get + edit in separate files/folders) or by site area (everything in one file/folder)?
Hopefully this makes sense. Thank you!
I'm not experienced in Durandal but have some positive background working with KO. I would recommend you to apply module pattern and incapsulate all your API service methods into the separate class (lets call it Router) also putting it into separate file. And then use methods of the Router class inside viewmodels.
// file with Router class
(function ($, ko, app) {
app.Router = function () {
var self = this;
self.get = function (url, queryString, callBack) {
$.get(url, data, function(data) {
callBack(data);
});
};
self.post = function (url, queryString, callBack) {
$.post(url, data, function(data) {
callBack(data);
});
};
};
})(jQuery, ko, window.app)
// file with viewmodel
(function ($, ko, app) {
app.UserModel = function () {
var self = this;
//create instance of Router class.
//Create it here just for the example. Will be better to create it out of the models to have just one instance.
//Or convert Router class to singleton
var router = new app.Router();
self.getUserDetails = function() {
//use proper router method to GET data, providing params
router.get(properRestServiceUrl, {userID: 1}, self.showUserDetails);
};
self.addMessage = function() {
//use proper router method to POST data, providing params
router.post(properRestServiceUrl, {userID: 1, message: 'test message'}, self.showConfirmation);
};
//callback function
self.showUserDetails = function(data) {
alert(data);
};
//callback function
self.showConfirmation = function(data) {
alert("The message was added successfully!");
};
};
})(jQuery, ko, window.app)
I don't know what you use for back end, but in case it's ASP.NET MVC, I would strongly suggest checking out HotTowel template. Even if it's not ASP.NET MVC, it is still a good starting point to see how to efficiently structure your app.
http://www.asp.net/single-page-application/overview/templates/hottowel-template

Categories

Resources