Update $scope variable that is used in ng-repeat, from other controller - angularjs

I have a controller HomeworkPageController where I get all the topics from MongoDB using method getAllMainTopics from TopicService. $scope.topics is then used to show all topics. I have a button that open a modal where a new topic is add in MongoDB. The modal is using another controller AddTopicController. How can I update $scope.topics from HomeworkPageController in AddTopicController ? I want to do this because after I close the modal, the list of all topics should be refreshed, it must contain the topic that has been added. I tried to use HomeworkPageController in AddTopicController and then call the method getAllMainTopics but the $scope.topics from html is not updated. Thanks.
Here is HomeworkPageController:
app.controller('HomeworkPageController', ['$scope','TopicService',
function ($scope, TopicService) {
$scope.topics = [];
$scope.getAllMainTopics = function () {
TopicService.getAllMainTopics('homework')
.success(function(data) {
$scope.topics = data;
}
$scope.addTopic = function () {
ModalService.openModal({
template: "templates/addTopic.html",
controller: 'AddTopicController'
});
}
]);
Here is AddTopicController:
app.controller('AddTopicController', ['$scope','$controller', '$timeout','TopicService', '$modalInstance',
function ($scope, $controller, $timeout,TopicService, $modalInstance) {
var homeworkPageController = $scope.$new();
$controller('HomeworkPageController',{$scope : homeworkPageController });
$scope.save = function() {
TopicService.saveTopic(data)
.success(function(result){
homeworkPageController.getAllMainTopics();
$modalInstance.close();
})
}
}]);
Here is the view where I use $scope.topics:
<div class="homework-content-topic-list" ng-repeat="topic in topics">
<label> {{ topic.subject }} </label>
</div

You should probably keep your list of topics in a service and then inject that service into both controllers. This way you would be able to access and update the topics in both of your controllers. It could look something like
app.controller('HomeworkPageController', ['$scope','TopicService',
function ($scope, TopicService) {
$scope.topics = TopicService.topics;
// Do stuff here
]);
Then you just need to modify your TopicService to have it's methods work on the stored object.

you can solve this by two methods
1)look at the example given in ui-bootstrap's website. They have given an example that will suit your requirement - plunker. There are three items in the modal - item1, item2, item3. If you select one of those items and click 'ok', the selected item is sent to the main controller through "resolve" attribute in the $scope.open function.
2)You can write a custom service that acts as a bridge to the two controllers and you can write getter and setter methods in the service.
angular.module('app').service('popupPageService', function() {
var topics;
var setDetails = function(param) {
topics = param;
};
var getDetails = function() {
return topics;
};
return {
setDetails: setDetails,
getDetails: getDetails,
};
});
call the setDetails function in the AddTopicController and once when you come out of the modal, update your $scope.topics in HomeworkPageController by pushing the new value added (getDetails)

Related

Auto trigger function throuigh service in angularjs

hi all i am using angulrajs passing one value from one controller to another controller using service it's work fine but my need is when service value change in controller 2 i get the service value in one scope when scope value change i need trigger the function it's called refresh function when service value change and that i need to call the refresh function here my fiddle
https://jsfiddle.net/ctawL4t3/10/
You can just $watch your value.storeObject. Though it's not best of the practices, but it suits this kind of feature.
$scope.$watch('value.storedObject', function(newVal) {
if(newVal !== '') {
refresh()
}
})
working fiddle (open console to see refresh function logging)
You can try to use angular default $emit, $broadcast, or try to do 2 simple functions in own service
angular.module('app').factory('StoreService', function() {
var listeners = {};
var emit = function(name, val) {
if(listeners[name]) {
listeners[name](val)
}
}
var on = function(name, callback) {
listeners[name] = callback;
}
return {
emit: emit,
on: on,
storedObject: ''
};
});
JSFiddle example
JSFiddle example $watch
JSFiddle example ng-change is better because, you can use easily debounce
you can use broadcast function for that
Please check this SO link to find the related answer
How to call a function from another controller in angularjs?
app.controller('One', ['$scope', '$rootScope'
function($scope) {
$rootScope.$on("CallParentMethod", function(){
$scope.parentmethod();
});
$scope.parentmethod = function() {
// task
}
}
]);
app.controller('two', ['$scope', '$rootScope'
function($scope) {
$scope.childmethod = function() {
$rootScope.$emit("CallParentMethod", {});
}
}
]);

Angularjs - how to watch for changes in DOM and apply a new style to it?

I have a very simple angular app that pushes data in without refreshing the page using setInterval. Now, how can I listen or watch for new data/changes, so that if the new value/data differ from the previous one a new css style will be applied to that particular new value (for example it will change the font color to red).
My code is below:
view:
<h1>{{title}}</h1>
<ul>
<li ng-repeat="friend in friends"><strong>Name: </strong>{{friend.name}} : {{friend.username}}</li>
</ul>
data:
angular
.module ('myApp')
.factory ('Friends', ['$http', function ($http) {
return {
get: function () {
return $http.get ('users.json').then (function (response) {
return response.data;
});
}
};
}]);
Controller:
angular
.module ('myApp')
.controller ('summaryCtrl', ['$scope', 'Friends', function ($scope, Friends) {
$scope.title = "Friends";
$scope.loadData = function () {
Friends.get ().then (function (data) {
$scope.friends = data;
});
};
//initial load
$scope.loadData();
var timer = setInterval(function(){
$scope.loadData();
},5000);
}]);
many thanks
Use $interval instead of setInterval, since it triggers a digest loop it will update your data automatically
angular
.module ('myApp')
.controller ('summaryCtrl', ['$scope', 'Friends', '$interval' function ($scope, Friends, $interval) {
$scope.title = "Friends";
$scope.loadData = function () {
Friends.get ().then (function (data) {
$scope.friends = data;
});
};
//initial load
$scope.loadData();
var timer = $interval(function(){
$scope.loadData();
},5000);
}]);
My recommendation would be to manually compare each friend item and assign a changeFlag whenever the data has changed.
To start, keep a reference to the old data and whenever new data comes in, compare the two, like this:
var oldData = undefined; // Somewhere in initialization.
...
Friends.get().then(function (response) {
var newData = response;
if (oldData && JSON.stringify(oldData) != JSON.stringify(newData))
{
$scope.friends = newData;
$scope.$apply(); // Force the entire page to be redrawn. You can do style bindings to change a style.
}
oldData = response;
}
This will get you half-way to your goal. You will only be refreshing the page whenever something has changed, but there is no indication as to which friend has changed. I imagine this is what you are attempting to accomplish. You want to highlight those friends that have changed.
To do this we could simply create a comparison function that applies a flag to each object that has changed. However, this code assumes that some property on each friend remains fixed. This is normally why an id property is given to each item in a database. I'm going to assume you have an id property for each friend that never changes regardless if their name, age, email, etc. does.
var changeFlagFriendsObjects = function(oldData, newData) {
var idToOldDataMap = {};
oldData.forEach(function (friend) {
idToOldDataMap[friend.id] = friend;
});
newData.forEach(function (friend) {
var oldFriendData = idToOldDataMap[friend.id];
friend.changeFlag = JSON.stringify(oldFriendData) != JSON.stringify(friend);
});
};
// You would call changeFlagFriendsObjects in the other example above. I'm sure this would be easy to figure out how to place.
Regarding binding styles in the HTML to properties, see here.
An example would be like the following:
<!-- Apply the 'highlight' style when changeFlag is true -->
<li ng-repeat="friend in friends" ng-style="highlight={changeFlag: true}"><strong>Name: </strong>{{friend.name}} : {{friend.username}}</li>

Execute scope function from service parameter

I am trying to create a service when I can set my formSubmit.
For example. In controller A I call "service.setFormSubmit(doThis(obj))" and in controller B I call "service.getFormSubmit()". Where it will execute the function doThis(obj) in controller B.
UPDATE - Re-formulated question.
I have 1 view where I want to edit or create a category. This means I need a dynamic ng-submit. I want to to this in the controller. So like this:
$scope.editCategory = function(obj) {
$scope.formSubmit = 'editCategory'
}
And on the create I want to change the formSubmit var to createCategory of course.
So I can make a difference between creating and editing the category.
Is this possible? Would be really nice if someone has a way to do this..!
Thanks in advance!
Instead of passing around strings which need to be eval'ed, use the service to share functionality directly between controllers.
The service can be dirt-simple:
.factory('MyService', function(){
var service = {};
return service;
});
Once injected and assigned to scope variables in both controllers you have an intermediary unit which can act as a modifiable channel for cross-controller collaboration.
.controller('FirstController', function($scope, MyService){
$scope.service = MyService;
})
.controller('SecondController', function($scope, MyService){
$scope.service = MyService;
$scope.service.create = function(obj){
console.log('Creating');
}
$scope.service.edit = function(obj){
console.log('Editing');
}
})
From the scope of FirstController, you can now call the function also available on the scope of SecondController:
<div ng-controller="FirstController">
<input type="checkbox" ng-model="button.type"> Toggle create/edit<br/>
<button ng-if="button.type" ng-click="service.create(obj)">Create</button>
<button ng-if="!button.type" ng-click="service.edit(obj)">Edit</button>
</div>
Demo
If you aren't reloading the page you can create an encapsulated variable in your service. Your set call would assign the value passed to that variable and your get call would return that variable to the caller.
One way I have achieved passing the data is to submit the form using the service and return a Json result to the service. Store the Json object in the encapsulated variable on the return and then pass a success or failure to the controller. When successful, let the controller redirect the view which will redirect using angular routing and ng-view. Once the new view, along with the new controller is loaded into the page, you can call the variable in your service to retrieve the data on the next controller.
Example Code:
app.factory('service', function ($q, $http) {
var savedData;
return {
loadData: function() {
return data;
},
search: function (parameters) {
var searchURL = '/MVCController/Search?parameter1=' + parameters.one +
'&parameter2=' + parameters.two;
var deferred = $q.defer();
$http.get(searchURL).success(function (data) {
savedData = data;
deferred.resolve(true);
}).error(function(data) {
data = 'An error occurred while searching: ' + data;
savedData = data //(if you want to save the error)
deferred.reject(data);
});
return deferred.promise;
}
}
});

Preserve state with Angular UI-Router

I have an app with a ng-view that sends emails to contact selected from a contact list.
When the users select "Recipient" it shows another view/page where he can search, filter, etc. "Send email" and "Contact list" are different html partials that are loaded in the ng-view.
I need to keep the send form state so when the users select someone from the Contact List it returns to the same point (and same state). I read about different solutions ($rootScope, hidden divs using ng-show, ...) but I want to know if UI-router will help me with it's State Manager. If not, are there other ready-to-use solutions?
Thanks!
The solution i have gone with is using services as my data/model storage. they persist across controller changes.
example
the user service ( our model that persists across controller changes )
app.factory('userModel', [function () {
return {
model: {
name: '',
email: ''
}
};
}]);
using it in a controller
function userCtrl($scope, userModel) {
$scope.user = userModel;
}
the other advantage of this is that you can reuse your model in other controllers just as easly.
I'm not sure if this is recommended or not, but I created a StateService to save/load properties from my controllers' scopes. It looks like this:
(function(){
'use strict';
angular.module('app').service('StateService', function(){
var _states = {};
var _save = function(name, scope, fields){
if(!_states[name])
_states[name] = {};
for(var i=0; i<fields.length; i++){
_states[name][fields[i]] = scope[fields[i]];
}
}
var _load = function(name, scope, fields){
if(!_states[name])
return scope;
for(var i=0; i<fields.length; i++){
if(typeof _states[name][fields[i]] !== 'undefined')
scope[fields[i]] = _states[name][fields[i]];
}
return scope;
}
// ===== Return exposed functions ===== //
return({
save: _save,
load: _load
});
});
})();
To use it, I put some code at the end of my controller like this:
angular.module('app').controller('ExampleCtrl', ['$scope', 'StateService', function ($scope, StateService) {
$scope.keyword = '';
$scope.people = [];
...
var saveStateFields = ['keyword','people'];
$scope = StateService.load('ExampleCtrl', $scope, saveStateFields);
$scope.$on('$destroy', function() {
StateService.save('ExampleCtrl', $scope, saveStateFields);
});
}]);
I have found Angular-Multi-View to be a godsend for this scenario. It lets you preserve state in one view while other views are handling the route. It also lets multiple views handle the same route.
You can do this with UI-Router but you'll need to nest the views which IMHO can get ugly.

AngularJS: Handling Model data injection in Controller

I have two controllers, to add Item and to delete Item, and a Model to show all items.
This model is injected into the controller ( on working on same template).
Whenever an item is added, I broadcast a message, which is listened by Model and it reloads the data from server.
Code:
ItemModule.factory('ItemListModal', function ($resource, $rootScope){
var allItem = $resource('item/page/:pageId.json', {'pageId': pageId });
var items = allItem.query();
$rootScope.$on('ItemAdded',function(){
items = allItem.query();
});
return items;
});
//Item is another Model, used to send data on server.
function CreateItemCtrl($scope, $rootScope, Item) {
$scope.save = function() {
Item.save($scope.item, function(data) {
$scope.result = data;
$rootScope.$broadcast('ItemAdded');
}, function(data) {
$scope.result = data.data;
});
}
}
function ListItemCtrl($scope, ItemListModal) {
$scope.allItems = ItemListModal;
}
Issue: Now since the dependency on ListItemCtrl is already resolved when template was first loaded, on adding Item it only changes the Model, but this is not re-injected into the ListItemCtrl. And due to this, the list on template do not change.
Is there any way to tell AngularJS to re-resolve the controller's dependency?
I really don't want to listen for event in Controllers and re-query data there, as there are other controllers which also needs same data from server.
Add another level of indirection on what you return from your service.
ItemModule.factory('ItemListModal', function ($resource, $rootScope){
var allItem = $resource('item/page/:pageId.json', {'pageId': pageId });
var data = {items:allItem.query()};
$rootScope.$on('ItemAdded',function(){
data.items = allItem.query();
});
return data;
});
function ListItemCtrl($scope, ItemListModal) {
$scope.allItems = ItemListModal;
// use as $scope.allItems.items wherever you need it. It will update when changes occur.
}
But it might be better to have a canonical representation of the item list on the client, and work to keep that current when you add things (just saving it to the server quietly).
The issue seems to be that while item is getting updated (have you tried console.log in the $on?) it's not an object and so hasn't been passed by reference. If you switch around your service to this:
ItemModule.factory('ItemListModal', function ($resource, $rootScope){
var ItemListModalScope = this;
var allItem = $resource('item/page/:pageId.json', {'pageId': pageId });
ItemListModalScope.items = allItem.query();
$rootScope.$on('ItemAdded',function(){
ItemListModalScope.items = allItem.query();
});
return ItemListModalScope;
});
And then wherever you use your allItems in your dome, you would do
{{ allItems.items }}

Resources