How to pass data between views using stateParameters (not routes) in AngularJS - angularjs

I have some views in my applications, and I have hard time to show the data when moving from one view to another.
I have a list of news and when I click on the particular news I want the view for that particular news to be shown. Here is my code:
My app.js :
.state('app.list', {
url: "/list",
views: {
'appScreen': {
templateUrl: "list.html",
controller: 'List.Ctrl'
}
}
})
.state('app.singleview', {
url: "/list/:newsId",
views: {
'appScreen': {
templateUrl: "single.html",
controller: 'SingleCtrl'
}
}
})
My controllers:
ListCtrl.$inject = ['$http', '$scope', 'datacontext'];
function ListCtrl( $http, $scope, datacontext) {
$scope.list = [];
datacontext.getPosts().then(function (posts) {
console.log('posts', posts);
$scope.list= posts;
}, function(reason) {
alert(reason);
});
The following controller is the one which will show me the single news and I have written some code but is not correct. In the URL I get the ID but I can't manage to show the data for that ID.
SingleCtrl.$inject = ['$scope', '$stateParams', 'datacontext'];
function ListNewCtrl($scope, $stateParams, datacontext) {
$scope.New = getNewsById($stateParams.newsId);
function getNewsById(id) {
datacontext.getPosts().then(function(posts) {
var found = null;
for (var i = 0; i < posts.length; i++) {
if (posts[i].id == id) {
found = posts[i];
break;
}
}
return found;
})
}
};
So in this controller what I am trying to do is get the ID and match it with postsId, and then show the data accordingly but it does no seem to work

You're confused with the asynchronism. The code should be
getNewsById($stateParams.newsId);
function getNewsById(id) {
datacontext.getPosts().then(function(posts) {
var found = null;
for (var i = 0; i < posts.length; i++) {
if (posts[i].id == id) {
$scope.New = posts[i];
break;
}
}
});
}
So that, when the success callback is executed, the New scope variable is initialized by the found post.
That said, I have a hard time understanding why you're getting a whole list of posts from the backend instead of using a REST service returning a single post by ID. If you did, it would be reduced to
function getNewsById(id) {
datacontext.getPost(id).then(function(post) {
$scope.New = post;
});
}

Related

AngularJS Dynamically Load Controller and Template based on Route

I am trying to dynamically load BOTH a template and controller based on the route (in Angular 1.6), pulling from the current directory architecture.
app
|__login.module.js
|__home.controller.js
|__login.factory.js
|__home.view.html
|__404.view.html
|__index.html
Currently, the below code works to pull the proper template, but the controller won't load:
angular.module('common.auto-loading-routes', []).config(function($routeProvider){
function getTemplate(routeParams){
var route = (routeParams.current) ? routeParams.current.params.route : routeParams.route;
var directory = route.split('/');
directory = directory.filter(function(entry) { return entry.trim() != ''; });
var page = directory[directory.length - 1];
directory.splice(-1,1);
directory = directory.join('/');
return directory + '/' + page +'.view.html';
}
function getController(routeParams){
//I think I need a promise here, but I don't know how to write it
routeParams = routeParams.route.split('/').join('.');
return routeParams + 'controller';
}
$routeProvider.when('/:route*', {
controller: function($routeParams) { //does not work
return getController($routeParams);
},
templateUrl: function($routeParams) { //works
return getTemplate($routeParams);
},
resolve: {
check: function($route, $http, $location){
return $http.get(getTemplate($route)).then(function(response){
if (response.status !== 404){
return true;
} else {
$location.path('404');
}
});
}
}
}).otherwise('/404');
});
I understand that controllers need to be present at the start of the app, but I am unable to write a proper resolve with a promise statement.
Can someone help me right a resolve with a simple promise that returns a string of the controller name based on the route?
I was able to get it working by not including the controller in the routing at all. Instead I put the ng-controller attribute in the view I was loading.
This worked!
angular.module('common.auto-loading-routes', []).config(function($routeProvider){
function getTemplate(routeParams){
var route = (routeParams.current) ? routeParams.current.params.route : routeParams.route;
var directory = route.split('/');
directory = directory.filter(function(entry) { return entry.trim() != ''; });
var page = directory[directory.length - 1];
directory.splice(-1,1);
directory = directory.join('/');
return directory + '/' + page +'.view.html';
}
$routeProvider.when('/:route*', {
templateUrl: function($routeParams) { //works
return getTemplate($routeParams);
},
resolve: {
check: function($route, $http, $location){
return $http.get(getTemplate($route)).then(function(response){
if (response.status !== 404){
return true;
} else {
$location.path('404');
}
});
}
}
}).otherwise('/404');
});
In the HTML of that view, I just put ng-controller="home.controller"(or whatever controller is appropriate)

how to dynamically set templateUrl in ionic tabs?

Is it possible to pass a templateUrl parameter to the .state tab definitions similar to the following? I have multiple different pages to write and don't want a bunch of nested ng-ifs within a single template to parse out which "html" page to show:
.state('tab.menu', {
url: '/menu',
views: {
'tab-menu': {
templateUrl: 'templates/tab-menu.html',
controller: 'MenuCtrl'
}
}
})
.state('tab.menu-detail', {
url: '/menus/:menuID',
views: {
'tab-menu': {
templateUrl: function($stateParams,Menus) {
console.log("In function") ;
console.log("Template: " + Menus.get($stateParams.menuID).url) ;
return 'templates/' + Menus.get($stateParams.menuID).url ;
},
controller: 'MenuSubCtrl'
}
}
})
// controllers
.controller('MenuCtrl', function($scope,Menus) {
$scope.menus = Menus.all() ;
})
.controller('MenuSubCtrl', function($scope,$stateParams,Menus) {
$scope.menu = Menus.get($stateParams.menuID);
})
// services
.factory('Menus', function() {
var menus = [
{name:"Account",url:"menu-account.html",menuID:0},
{name:"Contact",url:"menu-contact.html",menuID:1},
{name:"Help",url:"menu-help.html",menuID:2},
{name:"Privacy",url:"menu-privacy.html",menuID:3},
{name:"Rate App",url:"menu-rate.html",menuID:4},
{name:"Report Bugs",url:"menu-bugs.html",menuID:5},
{name:"Settings",url:"menu-settings.html",menuID:6}
] ;
return {
all: function() {
return menus ;
},
get: function(menu) {
for (var i = 0; i < menus.length; i++) {
if (menus[i].menuID === parseInt(menuID)) {
return menus[i];
}
}
return null;
},
} ;
}) ;
In the above .state('tab.menu-detail'..., the templuateUrl function fires and the first console log entry works, the 2nd one does nothing, no message, no error...nothing. It just stops. I have tried multiple iterations to get this to work and am at a complete loss. When I click the above Tab page link (which is supposed to take you to a sub-custom view) it does nothing. No console message, no error...nothing.
You can assign function to template Url. I have faced the similar scenario following solution worked fine.
check this:
link

Angularjs - read inside json file

This is my first attempt with angularjs and ionic-framework.
I have an example json file and i'd like to display onscreen some data from it.
The displaying-data bit works, but i'd like to populate a "details" page with some info that are stored as an abject inside the main json file, and i need to use the id from the url to select to display only the data that i need.
Here's some code:
App.js
angular.module('hgapp', ['ionic', 'hgapp.controllers', 'ngResource'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
.state('app.details', {
url: '/details/:roomID',
views: {
'menuContent': {
templateUrl: 'templates/details.html',
controller: 'DetailsCtrl'
}
}
})
$urlRouterProvider.otherwise('/app/home');
});
Controllers.js
angular.module('hgapp.controllers', ['hgapp.services'])
.controller('AppCtrl', function ($scope, HGJson) {
HGJson.get(function (data) {
$scope.rooms = data.data;
})
})
.controller('DetailsCtrl', function ($scope, $stateParams, HGJson) {
$scope.roomID = $stateParams.roomID;
console.log($stateParams.roomID);
})
services.js
angular.module('hgapp.services', ['ngResource'])
.factory('HGJson', function ($resource) {
return $resource('json/data.json')
});
Data.json (Just a simplified example)
{
tm: 00000000,
errors: 0,
data: {
{id: 0, name: Value 0, url:url-0},
{id: 1, name: Value 1, url:url-1},
{id: 2, name: Value 2, url:url-2}
}
details.html
<ion-view view-title="Details">
<ion-content>
<h1>{{roomID}}</h1>
</ion-content>
In the details page i'm printing the roomID just to see if the controller (detailsCtrl) works, and i have the correct id printed every time. Now, the bit where i'm stuck is how to manipulate the data from HGJson service so that it allows my to print on data from the right room id.
I hope this question is clear enought, if not, feel free to ask for more clarification.
Thanks a lot
EDIT
At the end i solved it adding this to my controller.js file:
.controller('DetailsCtrl', function ($scope, $stateParams, HGJson) {
HGJson.get(function (data) {
angular.forEach(data.data, function (item) {
if (item.id == $stateParams.roomID)
$scope.currentRoom = item;
});
});
})
Just do the same thing as what you're doing in the app controller, but find the room you want in the returned JSON:
HGJson.get(function (data) {
$scope.room = data.data.filter(function(room) {
return room.id == $stateParams.roomID);
})[0];
});
You could also put that filtering functionality in your service, so that in the future, when you have a real dynamic backend, you call a different URL returning only the requested room rather than calling a URL that returns all the rooms.
angular.module('hgapp.services')
.factory('HGJson', function ($http) {
return {
getRooms: function() {
return $http.get('json/data.json').then(function(response) {
return response.data;
});
},
getRoom: function(roomId) {
return $http.get('json/data.json').then(function(response) {
return response.data.data.filter(function(room) {
return room.id == roomID;
})[0];
});
}
};
});
Note that your JSON is invalid: data must be an array, not an object.
In your controller, you will need to create a function to "find" the correct object in your data object.
Try something like this:
$scope.getRoom = function(id) {
for(var i in $scope.rooms) {
if($scope.rooms[i].id === id) {
return $scope.rooms[i];
}
}
};
And you can display it in your DOM:
{{ getRoom(roomID) }}
BUT it would probably be even better to set the current room to a scoped variable instead of running the function every time. So in this case (I strongly recommend), instead of returning $scope.rooms[i], you could set angular.copy($scope.rooms[i], $scope.currentRoom) (this will copy the room into the currentRoom scoped variable) and then use it in the DOM with simply {{ currentRoom }}
Good luck!

passing form data from one controller to another in angular

.controller('CyclesController', function ($scope, $state) {
$scope.age = 0;
$scope.name = "";
$scope.email = "";
$scope.calculateByAge = function (age, name, email) {
$scope.data = $scope.data || {};
if (age > 0) {
$scope.data.age = age;
$scope.data.name = name;
$scope.data.email = email;
$state.go('tab.cycles-detail');
}
}
})
.controller('CyclesDetailController', function ($scope, $stateParams, CyclesService) {
console.log('scope data', $scope.data); // <--- undefined.
})
This may be a dumb question, but can get to get the data from the form on the CyclesDetailController controller.
If it's simple property you could do it by routing. Just change your "tab.cycles-detai' to 'tab.cycles-detai/:age' in your ui-router configuration and pass it when you're redirecting: $state.go('tab.cycles-detail', {age: age});
in 'CyclesDetailController' access it by $stateParams.age;
e.g:
//app.config
//...
.state('tab.cycles-detail', {
url: "^detail/{age:int}",
controller: 'CyclesDetailController',
templateUrl: "url_for_detail_template"
})
//...
// CyclesController
.controller('CyclesController', function ($scope, $state) {
$scope.age = 0;
$scope.calculateByAge = function (age) {
$scope.data = $scope.data || {};
if (age > 0) {
$scope.data.age = age;
$state.go('tab.cycles-detail', {age: age);
}
}
})
//CyclesDetailController
.controller('CyclesDetailController', function ($scope, $stateParams, CyclesService) {
console.log('scope data', $stateParams.age);
})
//
If you want to pass data from one route to another but dont want to expose it in browser menu bar, you can use squash.
Example -
.state('app.enroll', {
url: '/enroll/',
params: {
classId: {
value: null,
squash: true
},
className: {
value: null,
squash: true
}
},
title: 'Students Enrollment',
templateUrl: helper.basepath('enroll.html')
})
2nd Technique -
You can use localStorage / cookies to save data and retrieve at later stage.
3rd Technique -
You can always share data via services/factory in between controllers.

Back Arrow and Angular Routing - Press Back Twice

Angularv1.1.5
Site: http://tilsa.azurewebsites.net
I have a very simple route setup however when the user goes from the default/home route to the detail (pregunta) route and then clicks the back button nothing happens. The 2nd/3rd time the back button is clicked the user returns (chrome) to the default/home route. I'm not sure as to how or why this is happening.
$routeProvider.
when('/', {
templateUrl: '/js/app/partial/index.html',
controller: 'IndexCtrl'
})
.when('/pregunta/:id', {
templateUrl: '/js/app/partial/detalle.html',
controller: 'PreguntaDetalleCtrl'
}).
otherwise({
redirectTo: '/'
});
Here are the two relevant controllers. I've removed some of the code that doesn't seem relevant (polling for new info/etc):
// load the index list of questions, the actual questions are loaded in parent scope
.controller('IndexCtrl', ['$scope', 'services', 'data', '$modal', 'navigation', 'timeFunctions', function ($scope, services, data, $modal, navigation, timeFunctions)
{
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/';
$scope.url = encodeURIComponent($scope.noEncodeUrl);
// controls the back arrow visibility to go back
navigation.setReturn(false);
}])
.controller('PreguntaDetalleCtrl', ['$scope', '$routeParams', 'services', 'navigation', 'graphService', 'stringFx', '$timeout', 'timeFunctions', function ($scope, $routeParams, services, navigation, graphService, stringFx, $timeout, timeFunctions) {
$scope.notas = [];
$scope.comentario = '';
navigation.setReturn(true);
$scope.loadPregunta = function (id, loadComments)
{
services.preguntas.getDetalle(id).then(function (data)
{
$scope.safeApply(function ()
{
$scope.pregunta = data;
graphService.setProp('title', $scope.pregunta.pregunta);
$scope.noEncodeUrl = 'http://tilsa.azurewebsites.net/pregunta/' + id;
$scope.url = encodeURIComponent($scope.noEncodeUrl);
$scope.preguntaText = stringFx.removeAccent('¿'+$scope.pregunta.pregunta+'?');
});
if (loadComments)
{
$scope.commentTracker = {
defaults: { },
skip: 0,
take: 20
};
$scope.$on('$destroy', function (e)
{
$scope.stopPolling();
});
$scope.startPolling = function ()
{
// scrollTimeout will store the unique ID for the $setInterval instance
return $scope.scrollTimeout = timeFunctions.$setInterval(poll, 10000, $scope);
// Function called on interval with scope available
function poll($scope)
{
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
}
}
$scope.stopPolling = function ()
{
return timeFunctions.$clearInterval($scope.scrollTimeout);
}
$scope.startPolling();
$scope.cargarAnteriores = function ()
{
//$scope.commentTracker.skip++;
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas, true).then(function (data)
{
$scope.safeApply(function ()
{
$scope.notas = $scope.notas.concat(data.notas);
$scope.masNotas = $scope.notas.length > 0;
});
});
}
$scope.cargarAnteriores();
}
});
}
$scope.notaNueva = function () {
//$scope.commentario;
if ($scope.comentario.length < 3)
{
alert('Escribe algo mas, no seas tacano con tus palabras');
return;
}
$scope.processing = true;
services.preguntas.insertNota($scope.pregunta.id, $scope.comentario, $scope.notas, false).then(function (data)
{
$scope.comentario = '';
$scope.processing = false;
$scope.loadPregunta($scope.pregunta.id, false);
services.preguntas.getNotas($scope.pregunta.id, $scope.commentTracker, $scope.notas).then(function (data)
{
$scope.safeApply(function ()
{
for (i = 0, l = data.notas.length; i < l; i++)
{
$scope.notas.unshift(data.notas[i]);
}
});
});
});
}
$scope.loadPregunta($routeParams.id, true)
$scope.$on('updatedpregunta', function (event, obj)
{
$scope.loadPregunta(obj, false)
});
}]);
I had this issue as well! Turned ut that artur grzesiak was right! I had a iframe on my page that had a binding for its src-attribute.
<iframe src="{{selected.url}}"></iframe>
Since the default value of $scope.selected.url was null the first thing that happened was that it was loading a url called null.
After some research I found that there was a special directive for the iframe:
<iframe ng-src="{{selected.url}}"></iframe>
This change solved my is
It seems that the Angular side of your app is fine.
99% the problem is caused by some external library. For sure there is some problem with this script kVEquaeit4R (it seens to be a facebook plugin), as it fails to load some resource (404 error): The resource you are looking for has been removed, had its name changed, or is temporarily unavailable. and as a consequence a couple of further errors are generated (look at the console). And in turn it prevents the app from calling window.location.hostname.replace what actually is present in the kVEquaeit4R script.
So my suggestion is as follow: remove this fb plugin from your site and check if the routing works properly...

Resources