I have an Umbraco project with an Area section configured with Angular.
I use the Plugins to integrate the Area with the use of package.manifest like this:
Into edit.controller.js, I have this script:
'use strict';
angular.module("umbraco")
.controller('Administration.AdministrationTree.EditController', function administrationEditController($scope, $routeParams, $http) {
//set a property on the scope equal to the current route id
$scope.id = $routeParams.id;
$scope.url = "";
$scope.canShow = false;
$scope.showIframe = function () {
if ($scope.url === "") {
return false;
}
return true;
};
$scope.canShow = false;
if (!$scope.id) {
return;
}
$http.get('/umbraco/backoffice/administration/CustomSection/GetUrl/?node=' + $scope.id)
.success(function (data) {
$scope.url = JSON.parse(data);
$scope.canShow = $scope.url;
});
});
When I run the project and click on any node in this area, I receive most of the time a 404 error like if the page was not exist. I say "most of the time" because 1 out of 10, it works and the page is displayed.
However, if I put a breakpoint in the javascript function below and I click on any node and resume the javascript after the breakpoint was hitting, the node related html page is displayed correctly.
Anybody know why when I put a breakpoint, Umbraco or Angular are able to resolve 100% of the time the page but not when I don't have any breakpoint in this function?
Thanks
I really hate to answer my own questions but after 2 weeks without answers and a lot of reflections on my side, I finally found a solution to this problem.
What was causing the problem of out synching between Umbraco and Angular was due to the $http.get query which is asynchronous with Angular (no other choice I think) and after a response from the server to get a valid URL, the $scope object was not able to talk to Umbraco to redirect to the valid URL.
On my asp.net MVC controller, the GetUrl function was trying to get a valid URL doing a query to the database where I keep a structure of nodes which correspond to a tree displayed to the user. This is a slow process and the time required to respond to the HTTP get request was too long the vast majority of the time.
Here is my solution to this problem:
'use strict';
var myCompany = myCompany || {};
myCompany.myProject = myCompany.myProject || {};
myCompany.myProject.controller = (function (parent){
parent.urls = {};
function loadUrls () {
$.get('/umbraco/backoffice/administration/CustomSection/GetUrls')
.success(function (data) {
parent.urls = data;
});
};
loadUrls();
return parent;
})({});
angular.module("umbraco")
.controller('Administration.AdministrationTree.EditController', function administrationEditController($scope, $routeParams, $http) {
//set a property on the scope equal to the current route id
$scope.id = $routeParams.id;
$scope.url = "";
$scope.canShow = false;
$scope.showIframe = function () {
if ($scope.url === "") {
return false;
}
return true;
};
$scope.canShow = false;
if (!$scope.id) {
return;
}
var url = myCompany.myProject.controller.urls.find(function (element) {
return element.Key == $scope.id;
});
if (url) $scope.url = url.Value;
$scope.canShow = $scope.url;
});
Note in this case that I have an iffe function which query the server to build an array of all my URLs from the backoffice and then when Angular need a redirection, I search directly from the array.
The iffe function is calling only once when the user enters in the backoffice section which I think is nice because the structure behind rarely changes.
I'm not sure if it's a hack or the valid way to do the thing due to my lack of experience with Angular but it works like a charm.
Related
I am fairly new to angularjs, and would like to ask a few questions.
I am working on a project where I need to get a form object from the server. The form is a complicated tree object with many layers, and I have created 4 different components/tabs to bind to the corresponding objects. I had created a Service to get the data.
angular.module('myService', ['restangular'])
.factory('FormService', ['Restangular', '$q', function(Restangular, $q) {
function FormService() {
var self = this;
self.form = null;
self.getForm = function getForm(id)
{
var deferred = $q.defer();
if (self.form !== null)
{
deferred.resolve(self.form);
console.log("Cache!");
}
else {
Restangular.one('form', id).get()
.then(function successCallback(response)
{
self.form = response;
deferred.resolve(response);
console.log("from server!");
}, function errorCallback(response) {
deferred.reject(response);
console.log("error, cannot resolve object");
});
}
return deferred.promise;
}
return new FormService();
}])
});
Then I had my components all with similar config below:
angular.module('page1Summary', ['formService']).component('page1Summary', {
templateUrl: 'page1-summary/page1-summary.template.html',
controller: ['FormService', function Page1SummaryController(FormService) {
var ctrl = this;
// ******* Init Params Start *********** //
this.$onInit = function() {
// init value when object ready
FormService.getForm()
.then(
/* on success */
function successCallback(data) {
console.log("page1-summary init");
ctrl.form = data;
console.log("page1-summary got the data");
},
/* on error */
function errorCallback(data)
{
console.log("failed to get form");
}
);
}
/* other stuff here */
}
I was printing either "cache!" or "from server" on the getForm service. So that I can figure out whether I am pulling the data from server or memory. However, everytime I refresh, the result is different. Sometimes, the data saved in the local variable in service, and got "cached", but sometimes, some of my pages will get the data "from server".
I would like to know what is going wrong? I thought only the first time the service would get from server, but it seems like it is not the case.
Can someone please help me out and point out what I did wrong?
Thanks in advance!
You are caching your result into self.form.
self.form is again a variable FormSerivce Factory member.
It will cache the result till you do not refresh the page.
Once you refresh the page the value in self.form will get reset just like all the other variable in your application.
What you want is instead of caching result in self.form, cache it in localstorage.
So you can get the result back even after your page refresh.
I have a loading problem in Firebase. I want to display a list of images when I open the view but nothing happens till i go back ( there is a flash and i can see my photo list). It's working but not displaying in the opening.
What am i missing please ?
There is the beginning of my Controller view:
'Use Strict';
angular.module('App').controller('valider_photosController', function($scope, $state, $localStorage, Popup, Firebase, $firebaseObject, $ionicHistory, $ionicPopup, $ionicModal, $cordovaCamera) {
$scope.imagestab = [];
var ref_logements = firebase.database().ref('logements');
var ref_images = firebase.database().ref('images');
ref_logements.child(id_logement).child('images').on('child_added', added);
function added(idxSnap, prevId){
ref_images.child(idxSnap.key).once('value', function(datasnap){
var bidule = datasnap.val();
bidule['key'] = datasnap.key;
$scope.imagestab.push(bidule);
console.log('La valeur'+datasnap.key+'donne '+datasnap.val());
});
};
});
Since firebase works with asynchronous calls, by the time firebase responds with your data the angular cycle had already finished and you won't have your scope updated. You can force it by using $scope.$apply();.
ref_images.child(idxSnap.key).once('value', function(datasnap){
var bidule = datasnap.val();
bidule['key'] = datasnap.key;
$scope.imagestab.push(bidule);
$scope.$apply();
});
There is a tool that integrates angular and firebase in a way that you won't have to be concerned with things such as applying the scope. Its called angularfire. I totally recommend you to start using it in your application.
With angularfire you can get your data simply using
$scope.bidule = $firebaseObject(ref_images.child(idxSnap.key));
or
$scope.images = $firebaseArray(firebase.database().ref('images'));
I created a Factory
.factory('Firebase', function ($firebaseArray, $firebaseObject) {
var ref = firebase.database().ref();
return {
all: function (section) {
var data = $firebaseArray(ref.child(section));
return data;
},
getById: function (section, id) {
var data = $firebaseObject(ref.child(section).child(id));
return data;
},
get: function (section, field, value) {
var data = $firebaseArray(ref.child(section).orderByChild(field).equalTo(value));
return data;
}
};
})
And then in my controller, i replaced like you said :
var ref_logements = firebase.database().ref('logements');
var ref_images = firebase.database().ref('images');
ref_logements.child(index2).child('images').on('child_added', added);
function added(idxSnap, prevId) {
var monimage = Firebase.getById('images', idxSnap.key);
$scope.imagestab.push(monimage);
};
And it Works like a charm ! Thank you again :)
So i'm building a web application in AngularJS that connects to an API (Qlik Sense Engine API) with QSocks.
Qsocks is a lightweight wrapper around the Qlik Sense Engine API wrapper that is written in NodeJS but can also be imported in a web environment. QSocks contains and uses the NPM package Promise package so it uses it's own non AngularJS promises.
My service looks like this:
var app_promise = (appFactory.activeConnection() ? appFactory.activeConnection() : appFactory.app());
this.getData = function(qMeasures, time) {
ratioChild.qHyperCubeDef.qMeasures[0].qDef.qDef = qMeasures;
ratioChild.qHyperCubeDef.qMeasures[0].qDef.qLabel = qMeasures;
ratioChild.qHyperCubeDef.qDimensions[4].qDef.qFieldDefs = [time];
ratioChild.qHyperCubeDef.qDimensions[4].qDef.qFieldLabels = [time];
var deferred = $q.defer();
app_promise.then(function (obj) {
obj.createChild(ratioChild).then(function (childObj) {
deferred.resolve(childObj);
});
});
return deferred.promise;
}
In simple words, when i call this service in e.g. a controller. I get an object where i can build other objects on it.
Side Note:
I do need to make a new AngularJS promise because app_promise.then and obj.createChild(ratioChild).then are the NPM promise package promises.
This is how my controller looks like (first part):
if (!$rootScope.balanceSheetFixedObj) {
var fixYearsqMeasure = "Sum({<Jaar=>}Saldo)";
balanceSheetService.getData(fixYearsqMeasure, self.time).then(function (childObj) {
$rootScope.balanceSheetFixedObj = childObj;
return childObj;
}).then(handleFixData)
} else {
handleFixData();
}
This is how my controller looks like (second part):
function handleFixData(childObj) {
childObj = (childObj) ? childObj : $rootScope.balanceSheetFixedObj;
childObj.getLayout().then(function(data) {
self.data = data;
if (data.qHyperCube.qPivotDataPages[0].qData.length > 0) {
var fixPivotData = data.qHyperCube.qPivotDataPages[0];
self.labels = fixPivotData.qLeft;
$scope.$apply(); // Here is my problem!
With $scope.$apply() my view is publishes/updated after a second.
If i leave out the $scope.$apply() it do publish/update the view but after 10-15 Seconds.. Way to late! Why is my view so slow? I would like to leave out the $scope.$apply()
I manage to solve my own problem. After looking back it was quite obvious what my problem was.
Thanks to #charlietfl i've taken a look to the childObj.getLayout(). What i saw what that the getLayout() function returns a QSocks promise and the code that updates my view was written inside of the .then() of the QSocks promise. As getLayout() is not an angular promise, this was the problem. My view was not updated properly.
My solution was to create a service function that creates an Angular Promise
this.getObjLayout = function(childObj) {
var deferred = $q.defer();
childObj.getLayout().then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
And in the controller i invoke that function
function handleFixData(childObj) {
childObj = (childObj) ? childObj : $rootScope.balanceSheetFixedObj;
balanceSheetService.getObjLayout(childObj).then(function (data) {
self.data = data;
if (data.qHyperCube.qPivotDataPages[0].qData.length > 0) {
var fixPivotData = data.qHyperCube.qPivotDataPages[0];
self.labels = fixPivotData.qLeft;
}
})
}
I have a view within my App which does a database pull to show a user images they have previously uploaded.
The problem is that another view allows them to upload new images, but when switching back to the view of their uploaded images, they have to do a full page refresh to see their new uploads.
The question is how can I force the $http.get to run every time the view is loaded?
This is what I am trying but is not doing what I think it should:
capApp.controller('myUploadedPhotos', function ($scope, $http) {
$scope.nameFilter = "";
$http.get("/ajax/myUploadedPhotos.php", { cache: false})
.success(function(response) {
$scope.photos = response;
});
});
Is there a way to do this?
Your code looks correct so possibly the request is cached from the server? You can try appending a random string to your url to break the cache. e.g.
"/ajax/myUploadedPhotos.php" + new Date().getTime()
After thinking about it, I think you can also remove the { cache: false} because Angular also won't be able to cache the request if the timestamp changes. The old requests would just be sitting around somewhere taking up memory.
I'm not quite understand your question, but there isn't any issues with next initialization behaviour:
(function(angular) {
var capApp = angular.module('yourModule', []);
capApp.controller('myUploadedPhotos', ['$scope', '$http',
function ($scope, $http) {
$scope.nameFilter = "";
$scope.actions = angular.extend($scope.actions || {}, {
init: function () {
return $http.get("/ajax/myUploadedPhotos.php", {cache: false}).then(
function (response) {
$scope.photos = response;
}, function (reason) {
console.log('Error occured: ' + reason);
});
}
});
// You could even use it in $watch
$scope.actions.init();
}
]);
})(angular);
Hi all,
My environment : vwd 2013, sql server 2012, project template : Hot towel - Angular (by John Papa), BreezeJs, Breeze.WebApi2, Breeze.ContextProvider.EF6, Entity-Framework ADO .Net 6.
I'm making a little Web app SPA, everything used to work correctly as long as I instanciate a new BreezeManager before every Web API call with entityManagerFactory, all the web api calls are done in 2 services datacontext and authenticationservice (of my own development). Then I discover that app was going much faster with a global BreezeManager instanciated once at the start of the 2 services, and use it for all the web api calls. But apparently it's going too fast so that "Busyindicator" / togglespinner to be started, so the page appears white before being populated with data, the togglespinner and its waiting message are not displayed while the treatment.
I've well seen, in the shell.js, that the togglespinner is started at every route's change and stopped at every controller succesfull activation.
So to start correctly the app, I have created an intermediate page 'await.html' and its vm 'await.js' that's only redirecting to a new page, this page is at the root of routes.
So it should be working like this, the web app is started, going to 'await.html', then redirection to a new page, the togglespinner is started, then promise of this new page is correctly terminated and the togglespinner is stopped, but it doesn't work.
I remind you, that with a new BreezeManager for every web api call, it works.
My page is constituted (as originally) with a dynamic sidebar menu, a dynamic topnav and the page itself, in these 3 components there are web api calls, for the sidebar and topnav the web api calls are done only at the app start, maybe there is a problem of concurrency with these 3 components ?
Here is some code :
datacontext.js :
(function () {
'use strict';
var serviceId = 'datacontext';
angular.module('app').service(serviceId, ['common', 'config', 'model', 'entityManagerFactory', 'breezePartialMapper', datacontext]);
function datacontext(common, config, model, entityManagerFactory, breezePartialMapper) {
var $q = common.$q;
var manager = entityManagerFactory.newManager();
var entityNames = model.entityNames;
.
.
.
function getUser() {
//var manager = entityManagerFactory.newManager();
var query = breeze.EntityQuery.from('GetUser').expand("Membership").withParameters({ UserId: user.userId });
//var resLoc = getLocal(query);
return $q.when(manager.executeQuery(query)
.then(successCallback)
.catch(failCallback) // same as 'then(null, failCallback)'
.finally(finalCallback) // sort of like 'then(finalCallback, finalCallback)'
);
}
.
.
.
function successCallback(data) {
return data.results;
}
.
.
.
}
In the displayed page vm :
(function () {
'use strict';
var controllerId = 'FGHomePage';
angular.module('app').controller(controllerId, ['common', 'datacontext', FGHomePage]);
function FGHomePage(common, datacontext) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var $q = common.$q;
var vm = this;
vm.title = 'FGHomePage';
vm.user;
vm.imageUrl = "";
activate();
function activate() {
var promises = [getUser()];
common.activateController(promises, controllerId)
.then(function () { log('Activated Admin View'); });
}
function getUser() {
datacontext.user.userId = 1;
return datacontext.getUser()
.then(function (data) {
vm.user = data[0];
if (vm.user.fileExtension != "" && vm.user.fileExtension != null) {
vm.imageUrl = vm.user.userName.replace(" ", "_") + "." + vm.user.fileExtension;
}
else {
vm.imageUrl = "NonImage.gif";
}
//return $q.when(vm.user);
return vm.user;
});
}
}
})();
Do you see something wrong in the code ?
Thanx for your help.
I found a solution to this problem.
I had to change the event starting the togglespinner.
Now to start the togglespinner I've choosen the beginning of activatecontroller before the resolution of promises.
For this :
- in config.js : I add the start activate event :
var events = {
controllerStartActivate: 'controller.startActivate',
controllerActivateSuccess: 'controller.activateSuccess',
spinnerToggle: 'spinner.toggle'
};
and :
//#region Configure the common services via commonConfig
app.config(['commonConfigProvider', function (cfg) {
cfg.config.controllerStartActivateEvent = config.events.controllerStartActivate;
cfg.config.controllerActivateSuccessEvent = config.events.controllerActivateSuccess;
cfg.config.spinnerToggleEvent = config.events.spinnerToggle;
}]);
//#endregion
Then in common.js I broadcast this event at the beginning of activateController :
function activateController(promises, controllerId) {
$broadcast(commonConfig.config.controllerStartActivateEvent);
return $q.all(promises).then(function (eventArgs) {
var data = { controllerId: controllerId };
$broadcast(commonConfig.config.controllerActivateSuccessEvent, data);
});
}
Then in shell.js I start the togglespinner on this event :
$rootScope.$on(events.controllerStartActivate,
function (data) { toggleSpinner(true); }
);
In this way the togglespinner will be started at the beginning of activatecontroller and stopped after the resolution of the promises of the controller. If there are several controllers activated at (almost) the same time, for each a togglespinner will appear (as there are instances of the same togglespinner we don't see a difference visually). This last case doesn't appear often.
Hope this will help, if you see an improvement...