I have question about live updating view using AngularJS. I would like add comment and live upadate view no reloading page. Now I am using $route.reload() is this good solution?. Second solution is fire function after add comment, below function working good, but always after add comment view scroll to bottom. Have you any advices to live update view? Comments are loading from mongodb
userFactory.readMoreCourse = function(id) {
return $http.get('/api/product/' + id)
}
function getComment() {
User.readMoreCourse($routeParams.id).then(function(data){
app.comment = data.data.product.comments
});
}
getComment();
You should use $interval which Angular provides.
This will allow you to set a specific time in seconds that your function will trigger (as shown is 5 seconds).
You then want to push the data into your object so that the view will update automatically without refreshing etc.
app.controller('myController', function($scope, $interval) {
var id = $routeParams.id;
$scope.app = {
comment: []
};
$scope.getComment = function() {
User.readMoreCourse(id).then(function(response) {
// Single result.
$scope.app.comment.push(response.data);
// Multiple results.
angular.forEach(response.data, function(value, key) {
$scope.app.comment.push(value);
});
});
};
$interval($scope.getComment, 5000);
});
This example will show you how this works in practice.
Related
I am using the following service to pass data between two controllers
app.service('sharedDataService', function () {
var selectedObj = {};
var setSelectedObj = function (obj) {
selectedObj = obj;
};
var getSelectedObj = function () {
return selectedObj;
};
return {
SetSelectedObj: setSelectedObj,
GetSelectedObj: getSelectedObj
};
});
It works fine except when on the 2nd controller, the users press F5 or refresh the browser page. The selectedObj is cleared returns null.
Edit: based on some answers and comments - I am saving the data in the 2nd controller when I use the following line
$scope.Form = sharedDataService.GetSelectedObj();
Do I need to save it differently?
After getting the result please add it in session, after page refresh you can access those values again, even we can't root scope variable as well because those also cleared after page refresh.
I'm trying to retrieve a list of data from mysql database by using electron and bind it to a list in the controllers scope. I'm using mysql2. Here is my controller:
$scope.carList = [];
mysql.execute("SELECT * FROM cars").spread(function(results){
$scope.carList = results;
console.log(results);
})
I do get the results back, but the in the view carList remains empty. How can I solve this problem?
I just added a button to my view and bound it to a check function like this:
$scope.check = function(){
console.log($scope.carList);
}
After I click on the button, my list in the views gets populated. Now my question would be how can I have my list populated on the start of the controller rather than wait for an event ro make it happen?
I think mysql.execute("").spread(fn) promise is not a part of the AngularJS digest cycle. You did not provide enough code to fully reproduce your problem but I think by triggering a new digest cycle it should work for you. E.g. try it with $timeout which triggers a new digest cycle.
$scope.carList = [];
mysql.execute("SELECT * FROM cars").spread(function(results){
$timeout(function () {
$scope.carList = results;
});
})
I would prefer to create a AngularJS service which handles your electron mysql in a nice way. You could globally apply your $scopes in it, right after finishing your mysql procedures which are not a part of your digest cycle.
Approach by using AngularJS promises
var myApp = angular.module('myApp', []);
myApp.controller('MyCtrl', function($scope, $q) {
$scope.carList = [];
getCars.then(function(cars) {
$scope.carList = cars;
});
function getCars() {
var deferred = $q.defer();
mysql.execute("SELECT * FROM cars").spread(function(results) {
deferred.resolve(results);
});
return deferred.promise;
}
});
I forked an example from Angular 1's tutorial:
https://plnkr.co/edit/I48XFq
2 controllers (MainCtrl and AltCtrl) reference to the same Hero data from one dataService
MainCtrl and AltCtrl use a heroDetail component to render view of data
My setup:
dataService udpates data.time every 3 seconds using setInterval (I'm trying to not use Angular's $interval to get data rendered on view)
.factory('dataService', function() {
var data = {};
data.location = "Safe House";
data.count = 0;
data.time = new Date();
setInterval(function() {
data.time = new Date();
data.count++;
}, 1000);
data.updateLocation = function(origin) {
if (origin === 'click') return;
data.location = "Safe House #" + data.count;
}
return data;
});
In the heroDetail view, I put a button that invoke dataService.updateLocation('click'). Invoking from this button will do nothing, just return.
Also in the heroDetail controller, there's a setInterval to call dataService.updateLocation('setInterval') that actually update data.location
function HeroDetailController(dataService) {
var $ctrl = this;
$ctrl.update = function() {
dataService.updateLocation('click');
}
setInterval(function() {
dataService.updateLocation('setTimeout');
}, 3000); }
Result:
The service's data though gets udpate via background setInterval, but is not rendered on component view
But when I click on the front-end button, data is rendered with latest udpate, dispite the button just do nothing on data.
Could you help to explain why and how data got updated from service to the view in this case?
Thank you!
Using built-in $interval service instead of window.setInterval may solve your problem. AngularJs $interval documentation
To understand the problem, first read about JavaScript event loop. Event loop explained here
AngularJS $digest loop is based on event loop and after each digest cycle, it updates DOM according to the model that is two-way bound to the view. $digest cycle explained here
I think that you should add to your "setInterval" function the following line of code:
data.location = "Safe House #" + data.count;
Hope this helps.
I have an AngularJs app with a master details that I have changed to use
ui-router.
Previously, I had a SelectionService with a plain JavaScript observer that was
used by the master to notify selection and by the details to update himself. The
selection was just a technical identifier, so that the DetailsController has
to get the item from my BackendService which is basically a local cache.
Now with ui-router when an item is selected into the master, I go the details
state and the same flow remains (use the technical id to get details from backend).
My problem is that into the previous version all updates made on the details
where automagically updated on the master. But that is broken with the ui-router
version, probably because the MasterController and DetailsController don't
share the same scope.
So, my question is : How do you ensure that a list of items is updated when one
item is changed. Do you rely on some AngularJs functionalities (then how) or do
you use a classic events mechanism with $scope.$broadcast and $scope.$on ?
Edit, after more investigations
I have read some articles that are clearly against the usage of AngularJs events ($scope.$broadcast, $scope.$emit and $scope.$on) and recommand a custom event bus/aggregator.
But I would like to avoid that and thus rely on the AngularJs digest lifecycle. However what is suggest by #Kashif ali below is what I have but my master is not updated when the details changes.
angular
.module('masterDetails')
.service('BackendService', function($q){
var cache = undefined;
var Service = {}
Service.GetImages = function() {
var deferred = $q.defer();
var promise = deferred.promise;
if ( !cache ) {
// HTTP GET from server .then(function(response){
cache = JSON.parse(response.data);
deferred.resolve(cache);
});
} else {
deferred.resolve(cache);
}
return promise;
}
Service.GetImage = function(key) {
return GetImages().then(function(images){
return images[key];
});
}
Service.SetImage = function(key, updated) {
GetImages().then(function(images){
images[key] = updated;
// TODO; HTTP PUT to server
});
}
})
.controller('MasterController', function(BackendService){
var vm = this;
vm.items = [];
vm.onSelect = onSelect;
init();
function init() {
BackendService.GetImages().then(function(images){
// images is a map of { key:String -> Image:Object }
vm.items = Object.keys(images).map(function(key){
return images[key];
});
});
}
function onSelect(image) {
$state.go('state.to.details', {key: image.key});
}
})
.controller('DetailsController', function(BackendService, $stateParams){
var vm = this;
vm.image = undefined;
init();
function init() {
BackendService.GetImage($stateParams.key).then(function(image){
vm.image = image;
}).then(function(){
// required to trigger update on the BackendService
$scope.$watch('[vm.image.title, vm.image.tags]', function(newVal, oldVal){
BackendService.SetImage(vm.image.key, vm.image);
}, true);
});
}
});
Edit, this is due to the states
So, when I open the app on #/images the images state start. Then I select one image to go to the images.edit state and everything works well, the master is updated when details changes.
However if I start on #/images/:key which is the images.edit state, then the master ignore all changes mades on the master.
You can rely on both the solution you have mentioned
1.You can achieve this using factories in angularjs
Factories/services are the singleton objects that is shared along the the app:
example:
angular.module("app",[]).factory('myfactory',function(){
var data;
{
getData:getData,
setData:setData
};
function setData(data1)
{
data=data1;
}
function getData()
{
return data;
}
}
).controller('myclrl1',function($scope,myfactory){
}).controller('myclrl2',function($scope,myfactory){
});
you can inject these controller in different views and can access singleton factory(all controller will share the same object) "myfactory" in both controller using getter and setter
you can use $scope.$broadcast and $scope.$on to make nested contollers to communicate with each other
you can find the detailed Example over here.
$scope.$broadcast and $scope.$on
hope that would be helpful
Regards
I'm trying to developpe a chrome extension with angularjs and I have a strange behaviour when I try to initialize the $scope with the url of the active tab.
Here the code of my controller:
var app = angular.module('app', ['app.service']);
app.controller('ItemCtrl', function ($scope, chromeHelper) {
$scope.website = "No result!";
// Does not work until I click on something :-/
chromeHelper.getActiveTabDomain(function (domain) {$scope.website = domain; });
});
So when I try to initialize directly the $scope.website member it doesn't succeed but when I click on the button aftewards $scope.website then updates.
I really don't understand why.
Here is the code of my Chromehelper service:
var service = angular.module('app.service', []);
service.factory('chromeHelper', function() {
var chromeHelper = {};
chromeHelper.getActiveTabDomain = function (callback){
chrome.tabs.query({'active': true}, function(tabs){
if(tabs && tabs.length > 0) callback(getDomainFrom(tabs[0].url));
});
};
return chromeHelper;
});
function getDomainFrom(url) {
return url.match(/:\/\/(.[^/]+)/)[1];
}
Thank you very much in advance!
The OP solved the problem (see comment above) by adding $scope.$apply() at the end of the callback:
// Does not work until I click on something :-/
chromeHelper.getActiveTabDomain(function(domain) {
$scope.website = domain;
$scope.$apply(); // <-- adding this line did the trick
});
A short explanation for anyone landing on this page with a similar problem:
From the AngularJS docs on 'scope' (more specifically from the section titled 'Scope Life Cycle'):
Model mutation
For mutations to be properly observed, you should make them only within the scope.$apply(). (Angular APIs do this implicitly, so no extra $apply call is needed when doing synchronous work in controllers, or asynchronous work with $http or $timeout services.
See, also, this short demo.