In my application, am loading views using ngRoute,
.state('ReportViewer', {
url: "/ReportViewer",
controller: 'ReportViewerControl',
templateUrl: "views/Report_viewer.html"
})
I have a search Panel where users can search reports, and when selecting a report, it should route to a different page and load the report inside an iframe. Here is my code when the user select the report,
$scope.goReport = function(report){
$state.go('ReportViewer');
}
I have defined a constant in the config which changes dynamically based on the report selection
.constant('Digin_ReportViewer','http://192.226.192.147:8080/api/repos/%3Ahome%3Aadmin%')
Here i need to pass the 'Report' variable to the ReportViewerControl when the user select the report,
Here is my ReportViewerControl
routerApp.controller('ReportViewerControl', ['$scope', '$routeParams','Digin_ReportViewer',function($scope, $routeParams,Digin_ReportViewer) {
//here i need to append the report url ,
$scope.reportURL = Digin_ReportViewer+routeParams.report ;
$scope.trustSrc = function(src) {
return $sce.trustAsResourceUrl(src);
}
}
]);
you are using ui-router for confiuring routes ,but below in ReportController you are using $routeParams.I hope you have to use $stateParams for that.
routerApp.controller('ReportViewerControl', ['$scope', '$stateParams','Digin_ReportViewer',function($scope, $stateParams,Digin_ReportViewer) {
//here i need to append the report url ,
$scope.reportURL = Digin_ReportViewer+stateParams.report ;
$scope.trustSrc = function(src) {
return $sce.trustAsResourceUrl(src);
}
}
]);
also you have to pass the params from url or in method like this
.state('ReportViewer', {
url: "/ReportViewer",
controller: 'ReportViewerControl',
templateUrl: "views/Report_viewer.html",
params: ['report']
})
Then you can navigate to it like so:
$scope.goReport = function(report){
$state.go('ReportViewer', { 'report':'monthly' });
}
Or:
var result = { 'report':'monthly' };
$state.go('ReportViewer', result);
Related
I have no background in JavaScript and/or Angular but I need to use it for a research project. Shortly, I need to display on a webpage the JSON which is the answer from another component.
Here is the flow:
From the user interface clicking on Submit button a JSON file is sent to another component which does something and returns a JSON. This answer JSON should be displayed on a webpage.
The submit button is as follows and happens in the page named page2.html:
<button name="topage1" class="button-submit" ng-click="generateJSON()">Submit</font></button>
The method generateJSON() has the following code:
$scope.generateJSON = function(){
generateIdForComponents();
addRestrictions();
// REST
data = angular.toJson($scope.schema, true);
headers= {
'Content-Type': 'application/json',
'Cache-Control':'no-cache',
'Access-Control-Allow-Origin': '*'
};
$http.post('http://127.0.0.1:5000/re/z3', data, {headers:headers}).
then(function(response) {
console.log("Merge post ", response.data);
$scope.greeting = response.data;
});
}});
The routing is as follows:
app.config(function($routeProvider) {
$routeProvider
.when("/topage1", {
templateUrl : "page1.html",
controller : "page1Controller"
})
.when("/topage2", {
templateUrl : "page2.html",
controller : "page2Controller"
})
.when("/results", {
templateUrl : "outputOffers.html",
controller : "resultsController"
})
.otherwise({
templateUrl : "page1.html",
controller : "page1Controller"
});
});
What code should I write such that the JSON is displayed on outputOffers.html.
I suggest that in the callback of your $scope.generateJSON method, you redirect to the outputOffers results page (where you want to display the response json data) and you pass the json data as a parameter.
Then in your resultsController you can assign the json data (sent as a parameter) to a $scope.greeting variable to use in your outputOffers view.
You need the routeParams service to pass parameters between views and you will need to inject it into your resultsController.
You will also need the $location service in your page2Controller (in order to perform the redirect) so inject it as follows:
myApp.controller("page2Controller", function($scope, $location){...
Your generateJSON method in your page2 controller would look like this:
$scope.generateJSON = function(){
generateIdForComponents();
addRestrictions();
// REST
data = angular.toJson($scope.schema, true);
headers= {
'Content-Type': 'application/json',
'Cache-Control':'no-cache',
'Access-Control-Allow-Origin': '*'
};
$http.post('http://127.0.0.1:5000/re/z3', data, {headers:headers}).
then(function(response) {
console.log("Merge post ", response.data);
//redirect to the outputOffers view, passing the json data as a parameter
$location.path('results').search({jsonData: response.data });
});
}});
resultsController
Firstly inject the $routeParams to your resultsController as follows so we can get any parameters sent with the url (replace myApp with the name of your app):
myApp.controller("resultsController", function($scope, $routeParams){...
Use an anonymous function in the resultsController which checks whether the jsonData parameter exists (which we sent from the page2 controller). If it does exist then we assign it to a $scope.greeting variable
(function() {
if($routeParams.jsonData == null || $routeParams.jsonData === ""){
//If the jsonData is not set or if it doesnt contain a value (i.e is the empty string) then redirect to the page2 view.
$location.path('topage2');
}else{
//the jsonData parameter does exist so assign it to our scope.greeting variable so we can use it in our view.
$scope.greeting = $routeParams.jsonData;
//log the data to make sure it was passed as parameter:
console.log($scope.greeting);
}
})();
Then in the outputOffers.html view you can use the $scope.greeting variable.
For example if the json contains a "title" and a "message" property then we can do the following:
<p>{{greeting.title}}</p>
<p>{{greeting.message}}</p>
Update:
After seeing a snippet of your json in your comment you could do the following to display it:
<div ng-repeat="g in greeting">
<p>id: {{g.id}}</p>
<p>clockspeed: {{g.offer.clockSpeed}} </p>
</div>
One option can be create a service which will contain two functions to store and to fetch the value. The function that stores the value will be placed in 'page2Controller'.
The function that fetches the value will be placed in resultsController.
Find below a short tutorial.
the service should look like this:
app.config(function($routeProvider) {
$routeProvider
.when("/topage1", {
templateUrl : "page1.html",
controller : "page1Controller"
})
.when("/topage2", {
templateUrl : "page2.html",
controller : "page2Controller"
})
.when("/results", {
templateUrl : "outputOffers.html",
controller : "resultsController"
})
.otherwise({
templateUrl : "page1.html",
controller : "page1Controller"
});
});
app.service('greetingService', function() {
this.greeting = '';
this.store = function (greeting) {
this.greeting = greeting;
}
this.fetch = function () {
return this.greeting;
}
});
page2Controller should look like this:
app.controller('page2Controller', function($scope, greetingService) {
$scope.generateJSON = function(){
generateIdForComponents();
addRestrictions();
// REST
data = angular.toJson($scope.schema, true);
headers= {
'Content-Type': 'application/json',
'Cache-Control':'no-cache',
'Access-Control-Allow-Origin': '*'
};
$http.post('http://127.0.0.1:5000/re/z3', data, {headers:headers}).
then(function(response) {
console.log("Merge post ", response.data);
greetingService.store(response.data);
});
}});
});
resultsController should look like this:
app.controller('resultsController ', function($scope, greetingService) {
$scope.greeting = greetingService.fetch();
});
then, somewhere in your 'outputOffers.html' put this:
{{greeting}}
I'm trying to dynamically set the page title in my angular app.
I've already have it working with hardcoded title dynamically changing based on the state
.state('faq-detail', {
url: '/faqs/:faqId',
templateUrl: 'client/faq/faq-detail.view.ng.html',
controller: 'FaqListCtrl',
data: {
title: 'Faq details'
}
});
but I want to get the, in this case the question title, and put it as the page title.
The question comes from mongo database and I'm using angular-meteor.
.state('faq-detail', {
url: '/faqs/:faqId',
templateUrl: 'client/faq/faq-detail.view.ng.html',
controller: 'FaqListCtrl',
data: {
title: Faq.findOne({_id: $stateParams.faqId}).question
},
resolve: {
theFaq: function ($meteor, $stateParams) {
return $meteor.subscribe('faq').then(function () {
return Faq.findOne({_id: $stateParams.faqId});
});
}
}
I've tried this but as you may know, $stateParams is not defiend in the data.title area.
The other method I've tried is this one:
// ATEMPT TO SET THE TITLE DYNAMICALLY WITH THE QUESTION
$scope.autorun(() => {
console.log($scope.getReactively('theFaq'));
if ($scope.theFaq) {
let faqTitle = $scope.getReactively('theFaq').question;
// $state.get('faq-detail').data.title = faqTitle;
$state.current.data.title = faqTitle;
}
});
But this instead sets the title after I load another view with the same controller. So now I have the title from the last visited view instead of the current one.
Question
How do I set the page title from an object key value returned from mongo collection in angular-meteor?
For me, the cleanest way to achieve what you want is to do it in your controller. For example :
angular.module('mymodule').controller('FaqListCtrl', function($stateParams, $window){
let title = Faq.findOne({_id: $stateParams.faqId}).question;
$window.document.title = title;
})
I'm trying to find out what the best practice is for dynamic content in an angular app. I have an array that contains a set of phone numbers and i want to create a page/view base on the country of the phone numbers. So all German phone numbers should be listed under #/app/numbers/germany for example.
The array that holds the phone numbers will be fetched at page load - so it's ready for use and filtration.
Normally I would create a filtration based on the url parameters like ?country=Germany, but I don't suppose this is the right way to do it.
I use a filter for removing duplicates from the view in order to create a list over all countries (which should hold the link to the numbers under each country):
.filter('unique', function(){
return function(collection, keynam){
var output = [];
keys = [];
angular.forEach(collection, function(item){
var key = item[keyname];
if(keys.indexOf(key) === -1) {
keys.push(key);
output.push(item);
}
});
return output;
};
})
So basically I want to know what the best practice in this scenario is - using (dynamic) routes, load data based on URL or something entirely different?
Solution
I've found a solution by using $stateParams from the routing. My dynamic state:
.state('app.single', {
url: '/numbers/:country',
views: {
'menuContent': {
templateUrl: 'templates/country.html',
controller: 'CountryCtrl'
}
}
})
In the controller I assign the $stateParams to a scope variable like this:
.controller('CountryCtrl', function($scope, $stateParams, Numbers) {
//Load Firbase ref and get data
$scope.numbers = Numbers;
$scope.currentCountry = $stateParams.country;
})
And finally in the view I use $scope.currentCountry to filter out the numbers that match the current state/route:
ng-repeat="item in numbers | filter:{Country:currentCountry}"
The great thing about this is that i don't need to load data more than once, but I can rely on controller logic.
If you have a service (PhoneNumberSvc) with the function "getNumbers(country)" that filters the phone numbers by country:
app.module('appName')
.service('PhoneNumberSvc', [
'$http',
function ( $http ) {
this.getNumbers = function ( country ) {
return $http.get('numbers.json')
.then(function ( response ) {
var return_data = [];
angular.forEach(response.data, function ( item ) {
if ( item.country = country ) {
return_data.push(item);
}
});
return return_data;
});
};
}
]);
Then you could do something like this in your config:
$routeProvider.when('/app/numbers/:country', {
templateUrl: 'yourview.html',
controller: 'YourController',
resolve: {
data: function ( $route, PhoneNumberSvc ) {
var country = $route.current.params.country;
return PhoneNumberSvc.getNumbers(country);
}
}
});
Then, in your controller, be sure to inject the parameter "data":
angular.module('appName')
.controller('YourController', [
'$scope',
'data',
function ( $scope, data ) {
$scope.numbers = data;
}
]);
I would load only the data you need:
At first let's declare an angular route
$routeProvider
.when('/numbers/:country',{
templateUrl : '/foo/bar/baz/numbers.html',
controller : 'NumbersController'
})
Then in the NumbersController you can use the country parameter to query the backend and fetch the array of numbers related to the requested country
app.controller("NumbersController",function($scope,$http,$routeParams){
$http({
url: "some_url",
method: "GET",
params: {country: $routeParams.country}
}).success(function(response){
//Handle the data here
}).error(function(response){
//Handle errors here
});
});
The first benefit of this approach is that you don't have to load the entire array, but only what you need.
Furthermore you don't have to do filtering and parsing and other more complex operations.
There is not one single way to solve your problem, but this is a common approach in the angularJS world
I am creating an login page using Angular. After I process my login in the backend, I set the values in MyService from my LoginCtrl and then move to the next page using $window.location.href= 'main.jsp'; . But when I call the values which I set in LoginCtrl from HomeCtrl, the values are empty?
I know that Services are singletons and will maintain the same state throughout the app. But in this case, It jut resets. I think it is because of using $window.location.href. Please help me solve my problem.
This is my service ( MyService ):
app.service('MyService', function() {
var user = {
name: '',
permissions: ''
};
this.getUser = function() {
return user;
}
this.setUser = function(userr) {
this.user = userr;
}
});
This my LoginCtrl: ( I've just posted the http.post part)
$http({
method: 'POST',
url: 'login',
data: JSON.stringify($scope.user),
headers: {
'Content-Type': 'application/json'
}
}).success(function(data) {
if (!("failure" == data)) {
console.log(data);
var user = MyService.getUser();
user.name = data.name;
user.permissions = data.permissions;
console.log(user);
console.log(MyService.getUser());
$window.location.href = 'main.jsp';
// MyService.changeLocation('main.jsp', true);
} else {
$scope.information = "Invalid username/password!"
}
}).error(function(data) {
console.log(data);
});
And this is my HomeCtrl:
app.controller('HomeCtrl', function($scope, $http,MyService) {
console.log(MyService.getUser());
var user = MyService.getUser();
$scope.flashMessage="Hello " + user.name;
});
Here user.name is empty.
You are changing your web page. The angular application is not persisted across the website boundary; remove the alteration to the window.location.href.
In order to simulate page changing in Angular consider using the official router (shipped with Angular 1.4+), ngRoute or Angular UI Router. These solutions use the HTML History Api and fallback to hashbang URLs to emulate the sort of thing you're trying to achieve.
This ends up creating a single-page application, which is what Angular is designed for.
In LoginCtrl, while reaching the success callback, you are not setting the response value(data in your case) to user object in MyService service.
You are getting the user object from the Service by
var user = MyService.getUser();
But setting the values to that object will not set the user object in the Service.
You need to use MyService.getUser(user); to set values in your service and the same will be available in your HomeCtrl
$http({
method: 'POST',
url: 'login',
data: JSON.stringify($scope.user),
headers: {
'Content-Type': 'application/json'
}
}).success(function(data) {
if (!("failure" == data)) {
console.log(data);
var user= {};
user.name = data.name;
user.permissions = data.permissions;
MyService.getUser(user); //set the values for user
var obj= MyService.getUser(); //get the values for user
console.log(obj);
//should display user object
//with respective name and permissions should be available
console.log(MyService.getUser());
$window.location.href = 'main.jsp';
// MyService.changeLocation('main.jsp', true);
} else {
$scope.information = "Invalid username/password!"
}
}).error(function(data) {
console.log(data);
});
UPDATE:
The reason why your code doesnt seem to work is: you are using $window incorrectly to change the route. $window.location.href = 'main.html' is somehow changing the route outside angular's context and hence not running the HomeCtrl. To fix this, you need to do the following:
First, define routes for your angular application (preferabbly using ui-router)
app.config(function($stateProvider){
$stateProvider
.state('login',{
url:'/',
templateUrl:'login.html',
controller:'LoginCtrl'
})
.state('main',{
url:'/main',
templateUrl:'main.html',
controller:'HomeCtrl'
})
.state("otherwise", { url : '/'})
})
Use $location.url('/main'). Notice it is same as the url pattern we defined for state: main. Or better, you should use $state.go('home'); to redirect the user to desirable state
Here's a working plunkr
Hope this helps!
In one application that I'm working with, the route system need to be integrated with i18n, like the example below:
$routeProvider.when('/:i18n/section', ...);
But I'm facing some issues due to, what I guess it is, the $digest cycle, which doesn't change the i18n param at the runtime.
Other issue that I'm facing is, if the location path is pointed to something like:
http://localhost:9000/section/...
not like:
http://localhost:9000/en/section/...
the i18n path param ends being associated with /section/, which means, on $routeParams service, $routeParams.i18n = 'section';. This is expected, but I need to be able to parse the /:i18n/ param, to avoid this conflicts, and change the URL, concatenating one locale, to contextualize the session, replacing the current route with the new one, i18n-ized, whithout refreshing the view/app automatically, yet selectivelly, because some features only need to be changed, not all.
Also, I've designed one service that evaluates, based on a list of possible language settings and its weights, the language that'll be selected to the current context:
var criteria = {
default: {
tag: 'en',
weight: 10
},
user: {
tag: 'pt',
weight: 20
},
cookie: {
tag: 'zh',
weight: 30
},
url: {
tag: 'ru',
weight: 40
},
runtime: {
tag: 'es',
weight: 50
}
};
function chooseLanguage (languages) {
var deferred = $q.defer();
var weights = [];
var competitors = {};
var choosen = null;
if (defineType(languages) === 'array') {
for (var i = languages.length - 1; i >= 0; i--) {
if (languages[i].tag !== null) {
weights.push(languages[i].weight);
competitors[languages[i].weight] = languages[i];
}
}
choosen = competitors[Math.max.apply(Math, weights)];
} else if (defineType(languages) === 'object') {
choosen = languages;
} else {
return;
}
setRuntimeLanguage(choosen.tag);
deferred.resolve(choosen);
return deferred.promise;
}
Explaining the code above, when angular bootstraps the app, the snippet is executed, selecting which language is defined and if its strong enough to be selected. Other methods are related to this operation, like de detection of the URL param, if there's one logged user, etc, and the process is executed not only on the bootstrap, but on several contexts: $routeChangeStart event, when the user autenticates its session, switching the languages on a select box, and so on.
So, in resume, i need to be able to:
Parse the URL and apply the locale param properly, if its not informed initialy;
Change the URL i18n param during the runtime, whithout reloading the whole view/app;
Deal with the language changes correctly, which means, if my approach based on weights isn't the better way to go, if you suggest me something else.
Edit 1:
A $watcher doesn't do the trick because the app needs the correct locale in every path, even before it instantiates all the elements. AngularJS is used in every step of this check, but if there's any clue to do this outside, before Angular instantiates, we can discuss about it.
For now, I'm using the accepted answer below, with a solution that I developed, but it has to be improved.
I've ended up doing a kind of preliminary parse on the URL. Using only ngRoute (ui-router wasn't an option...), I check if the path matches with the restrictions, if not, a redirect is triggered, defining correctly the path.
Below, follows a snippet of the solution, for the primary route, and a simple subsequent example, due to the quantity of the routes on the app, and their specific data, that doesn't belongs to the basis idea:
$routeProvider
.otherwise({
redirectTo: function () {
return '/404/';
}
})
.when('/:i18n/', {
redirectPath: '/:i18n/',
templateUrl: 'home.html',
controller: 'HomeCtrl',
resolve: {
i18n: [
'$location',
'$route',
'i18nService',
function ($location, $route, i18nService) {
var params = $route.current.params;
return i18nService.init(params.i18n)
.then(null, function (fallback) {
if (params.i18n === '404') {
return $location.path('/' + params.i18n + '/404/').search({}).replace();
}
$location.path('/' + fallback).search({}).replace();
});
}
],
data: [
'dataService',
function (dataService) {
return dataService.resolve();
}
]
}
})
.when('/:i18n/search/:search/', {
redirectPath: '/:i18n/search/:search/',
templateUrl: 'search.html',
controller: 'SearchCtrl',
resolve: {
i18n: [
'$location',
'$route',
'i18nService',
function ($location, $route, i18nService) {
var params = $route.current.params;
return i18nService.init(params.i18n)
.then(null, function (fallback) {
$location.path('/' + fallback + '/search/').search({
search: params.search
}).replace();
});
}
],
data: [
'$route',
'searchService',
function ($route, searchService) {
var params = $route.current.params;
return searchService.resolve({
'search': params.search
});
}
]
}
});
The redirectPath property is used in case of you need the pattern for that route, because ngRoute keeps one copy for private use, but doesn't give access to it with $route public properties.
Due to the required parse before any app request (except the i18nService, which loads the locale list and triggers angular-translate to load the translations), this methods causes several redirects, which leads to a significant delay on the instantiation.
If any improvements are possible, I'll thanks for, also, if I found a better way to do it, I'll update this answer with it.