Question: What is the best way and the best time to pre-load .ng files that are used in routing templates?
In researching this thus far, I've found the following answers:
Use Angular's script directive.
Problem: My content cannot be loaded as a literal string but needs to be fetched from the server. In other words, I have an .ng file as my partial so I cannot do
my content must remain in a .ng file on the server and be fetched
Use $templateCache.put to put a template literal in the cache. Same problem as above.
Use $http to load the .ng file. This works in that it is not a string literal but I am struggling to find the best time to perform this so that it is not blocking (realize its async but still)
To save you from suggesting resources I've already seen, I've read the following:
Is there a way to make AngularJS load partials in the beginning and not at when needed?
https://groups.google.com/forum/#!topic/angular/6aW6gWlsCjU
https://groups.google.com/forum/#!topic/angular/okG3LFcLkd0
https://medium.com/p/f8ae57e2cec3
http://comments.gmane.org/gmane.comp.lang.javascript.angularjs/15975
Maybe use a combination of 2 and 3.
Like you said, $http is async, so why not just put each partial into the templateCache after the app has loaded.
For example:
var myApp = angular.module('myApp', []);
myApp.run(function($http, $templateCache) {
var templates = ['template1.ng', 'template2.ng'];
angular.forEach(templates, function(templateUrl) {
$http({method: 'GET', url: templateUrl}).success(function(data) {
$templateCache.put(templateUrl, data);
});
});
});
I've never had the need for this and definitely haven't tested it, but the idea is there.
Related
Right now, I have a factory which loads a JSON file.
angular.module("app").factory("RolesFactory", ['$http', '$q', function($http, $q) {
var d = $q.defer();
$http.get('events.json').success(function(data) {
d.resolve(data);
});
return d.promise;
}]);
And then I call this factory when I need the contents of events.json with this controller:
App.controller('rolesCtrl', ['$scope', 'RolesFactory', function($scope, RolesFactory) {
RolesFactory.then(function(roleData){
$scope.roles = roleData.roles;
});
}]);
All good, but whenever I need to use this data. Isn't it refetching the contents of events.json? Meaning: is Angular reloading the file over and over again? I was hoping to load the file once and call it by a global variable or something.
When my app loads initially, I want it to load and store the contens of events.json -- and then I'd like my app to be able to use this data whenever/wherever.
Is this possible?
As AngularJS is a stateless framework, you have only a few options here, all of which are some kind of client-side caching:
Use localStorage to store your data. Once the data is fetched, you can just save it to localStorage using localStorage.setItem after Stringifying the JSON. You'll need to re-parse the JSON the next time you use it though, so if this is a giant JSON, this is not the best idea
Use sessionStorage to store your data. This is exactly the same as #1, but you will lose data upon termination of session,i.e. closing your browser.
Trust the JSON to be cached in your browser. This is most likely the case. Static assets are by default cached by most modern browsers. So, the second time your factory requests the JSON, the resource isn't actually fetched from the server. It is merely pulled from the browser's cache.
NOTE: The way to check this is to see what the HTTP status code for your resource is, in Chrome's Developer Tools Network tab. If the status says 304 that means it has been pulled from cache.
Is it possible to get the response object sent by the render() (Symfony2) method?
An example:
public function indexAction(){
$params = array('Hi' =>'Hello world', 'userName' =>'Jack');
return $this->render('exampleBundle:Default:index.html.twig', $params);
}
I'm totally able to capture the var $params and its content in the html.twig file, but I can't figure out how to get that content it in javascript to render the view using angular. Just in case, I tried many absurd things, but they didn't work at all obviously.
Maybe it isn't possible at all and I'll need to redesign it or make a new ajax call once the document is loaded instead of passing the content via the render() method. I'm not really sure so, can I reach this without make an ajax request?
This approach seems to me a bad thing. Most of modern client applications philosophy is to to load as soon as possible the web page from server and the load data through xhr request.
Said that if you need or want to do it, you can achieve it in differents manners:
I did not tried the examples, so this can contains some syntax errors:
1- JS Vanilla approach; in your html.twig
<script type="text/javascript">
window.nameSpaceOfMyApp = window.nameSpaceOfMyApp || {};
window.nameSpaceOfMyApp.exchange = window.nameSpaceOfMyApp.exchange || {};
window.nameSpaceOfMyApp.exchange.params = JSON.parse({{$params |json_encode()}});
</script>
2- More "Angular Way" Not overloading global namespace and better testing...If you have load angular.js at this point you can create in twig template the main module of your angular App (if you cant use your main module, you can use a new module and require it as dependency of your main module)
in your html.twig:
<script type="text/javascript">
angular.module('moduleName',[<dependencies>,...])
.value('exchange',{
params: JSON.parse({{$params |json_encode()}})
})
</script>
Later you can inject this value where you need it.
angular.module('moduleName').controller('nameOfController',['exchange', function(exchange){
console.log(exchange.params)
}]);
I am trying to call an API end point once a user clicks a button holding a myNavigator.pushPage() request. However,I can not get the $scope data generated from the $http.get request to be passed to the new page.
If I test using console.log('test'); inside the .success of the $http.get request I successfully get the log info in the console but any data held in $scope.var = 'something'; does not gets passed to the page! Really confused!
$scope.historyDetails = function(id){
var options = {
animation: 'slide',
onTransitionEnd: function() {
$http.get('http://xxx-env.us-east-1.elasticbeanstalk.com/apiget/testresult/testId/'+id).success(function(data) {
$scope.testscore = 'something'; // this is not getting passed to page!
console.log('bahh'); // But I see this in console
});
}
};
myNavigator.pushPage("activity.html", options);
}
Page:
<ons-page ng-controller="HistoryController">
...
<span style="font-size:1.2em">{{testscore}} </span><span style="font-size:0.5em;color:#555"></span>
...
</ons-page>
Yes, that's so because both pages has different controllers, resulting in different scopes. One can not access variables from one scope to another.
Hence one solution in this case can be using rootScope service.
Root Scope is parent scope for all scopes in your angular application.
Hence you can access variable of root scopes from any other scope, provided that you are injecting $rootScope service in that controller.
to know more about rootScope check this link.
Good luck.
Update 1:
check these articles
http://www.dotnet-tricks.com/Tutorial/angularjs/UVDE100914-Understanding-AngularJS-$rootScope-and-$scope.html
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
As Yogesh said the reason you're not getting your values is because if you look at $scope.testscore and try to find where is the $scope defined you will see that it's an argument for the controller function (thus it's only for that controller).
However we can see that the controller is attached to the page and you are pushing another page.
So in that case you have several options:
Use the $rootScope service as Yogesh suggested (in that case accept his answer).
Create your own service/factory/etc doing something similar to $rootScope.
(function(){
var historyData = {};
myApp.factory('historyData', function() {
return historyData;
});
})();
Technically you could probably make it more meaningful, but maybe these things are better described in some angular guides.
If you have multiple components sharing the same data then maybe you could just define your controller on a level higher - for example the ons-navigator - that way it will include all the pages. That would be ok only if your app is really small though - it's not recommended for large apps.
If this data is required only in activity.html you could just get it in that page's controller. For example:
myApp.controller('activityController', function($scope, $http) {
$http.get(...).success(function(data) {
$scope.data = data;
});
}
But I guess you would still need to get some id. Anyway it's probably better if you do the request here, now you just need the id, not the data.
You could actually cheat it with the var directive. If you give the activity page <ons-page var="myActivityPage"> then you will be able to access it through the myActivityPage variable.
And the thing you've been searching for - when you do
myNavigator.pushPage("activity.html", options);
actually the options is saved inside the ons-page of activity.html.
So you can do
myNavigator.pushPage("activity.html", {data: {id: 33}, animation: 'slide'});
And in the other controller your id will be myActivityPage.options.data.id.
If you still insist on passing all the data instead of an id - here's a simple example. In the newer versions of the 2.0 beta (I think since beta 6 or 7) all methods pushPage, popPage etc return a promise - which resolve to the ons-page, making things easier.
$scope.historyDetails = function(id){
myNavigator.pushPage("activity.html", {animation: 'slide'}).then(function(page) {
$http.get('...' + id).success(function(data) {
page.options.data = data;
});
});
});
Side note: You may want to close the question which you posted 5 days ago, as it's a duplicate of this one (I must've missed it at that time).
I am getting started with Angular, and I want to get HTML template from a file using JavaScript, I don't want to use ng-include or any other directive as I don't really have an html to play with, I have only JavaScript file with data rendered from REST service and I need to send HTML output to the page I am in, so I don't really have any existing elements in the DOM.
So how do I get HTML from another file and use it just from JavaScript, in an Angular way?
I think you should use directive and as James point out use the $compile.
You can look some doc https://docs.angularjs.org/guide/compiler
If for any reason you don't want to do that, I can put an exemple of a really BAD angular code I use on my app to show the CGU (an HTML from another domain).
Note that we use jQuery, and that's not a good thing in Angular world.
$http({
method: 'GET',
url: 'URL_HTML'
}).success(function (data, status, headers, config) {
var html_from_server = $.parseHTML(data);
data = $(html_from_server.find('#container'));//look for the content of an ID.
$('#content_cgu').html(data.html());// get an element on our page and replace the html content
});
Take a look at $compile - it sounds like that's what you're looking for. You can take the html from your service and compile it to work in your AngularJS application.
Let me rephrase it. How do I get HTML from another page and store it in a variable inside my JS file using Angular? What do I do? var returnedHTML = ???
Straightforward solution:
var returnedHTML;
var url = 'http://www.google.com';
$http.get(url, { cache: $templateCache }).then(function(result){
returnedHTML = result.data;
});
I was just dealing with this today. I think your question was misunderstood.
You simply include an ng-include into your code like so:
ng-include="'https://en.wikipedia.org/wiki/Special:Random'"
Angular will display your URL as directly in the page.
I'm building an artist portfolio web app where the artist can create a 'project' which consists of a series of content pieces (mostly images at this point) which can then be put in an order and the artist can choose between different preset layouts. I'm using angular and node + express. I'm struggling to find a good way to dynamically set templates. The only functional scheme i've devised so far is to put the template name in a query parameter in the url like this.
ui-sref="webapp/project/photo?template=vertical"
then in ui-router it's relatively simple to set the template using state params
templateUrl : function (stateparams) {
return 'templates/' + stateparams.template + '.html';
},
Although it's functional I don't like this mostly because it creates messy urls and allows anyone to change templates with query params or more likely load something without a real template because the url was typed incorrectly or correctly without the query parameter.
I can't make an api call from the templateUrl function because it's not injectable so I can't use the $http service. I've tried to use template provider but haven't made anything functional out of that yet. It does allow for an injectable function but that function must return an entire template instead of a url. If I can get a template url for it how can a load the template with that? I assume I'm not the first person to want dynamic templates (templates set in the database) from angular. What are my best options?
I have found a functional solution to my problem using ui-router, $stateParams, and $http. I'll be looking for a better architecture scheme as this necessitates 3 server requests every time a project is requested. One to get the template name and another to load the template file and another to load the project data. I suppose I only added one request to the norm. Anyways.. This solution is working for me but next I will be moving the logic to get template by the project name to an angular service to keep everything clean.
.state('projects/:project_url', {
url : '/projects/:project_url',
templateProvider : function ($stateParams, $http) {
return $http.get('/api/projects/' + $stateParams.project_url)
.then(function (data) {
// get template name
var templateName = data.data[0].template;
// return template by name
return $http.get('/pages/' + templateName + '.html')
.then(function (data) {
return data.data;
});
});
},
controller : 'projectCtrl'
});
http://dotjem.github.io/angular-routing/ supports your scenario with inject-able template functions. Note however that you must provide the raw template in the return statement, but that is easily done using the $template service...
It is very similar to UIRouter, so it should be a fairly easy transition if you find it worth it.
http://plnkr.co/edit/dkPIWMW236ixifETohNW?p=preview
If you take the latest stable release rather than the head you must add a "get" call to that service as: $template.get('template.html') rather than the newer: $template('template.html')