Angular UI-Router. Dynamic Template URL - javascript

I got a small situation I am creating a front end portion of an application using Angular and ui-router for the routing.
Te Database is holding the routes for the links. The url would look like (AdminMsgReview?init=1) which initiates the building of the frame.
I am trying to put that frame into a single page application (hence angular), I tried hard coding the url and i know it works and injects the frame into the view.
When i try to build it dynamically, I get the issue where the templateUrl is loading first before the resolve is finished getting the url.
This is the route.js
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('login', {
url: '/login',
templateUrl: function(){
return 'AdminMsgReview?init=1'
}
})
.state('app', {
url: '/app/:id/:fnid',
resolve: {
fnUrl: function(Toolbar) {
return Toolbar.getToolbar();
}
},
controller: ['$rootScope', '$stateParams','fnUrl', function($rootScope, $stateParams, fnUrl){
var mid = Number($stateParams.id) -1 ;
var fnid = $stateParams.fnid;
$rootScope.newUrl = fnUrl;
$rootScope.url = JSON.stringify($rootScope.newUrl[mid].Function[fnid].URL);
alert("Url should be = " + $rootScope.url);
}],
templateUrl: function($rootScope){
console.log($rootScope.url);
alert($rootScope.url);
return $rootScope.url;
}
})
This is my factory for getting the data:
var json = "rwd/resources/data/toolbar.json";
return{
getToolbar : function(){
return $q.all([
$http.get(json)
]).then(function(results){
var toolbar = [];
angular.forEach(results, function(result){
toolbar = result.data;
});
return toolbar.toolbar;
});
}
}

Related

Handling successful node/angularjs login without reloading page

I'm trying to build a single page node/angular ( v 1.56 ) application that leverages angular's ui-router to change pages inside the application without having any browser reloads. My main obstacle is that I'm trying to figure out how, after a successful login event, do I get the user to the dashboard page without having to redirect/reload that page? Ideally, I'm looking for a way to programmatically trigger a route just as if I had clicked on the link.
I tried using angular's $http.get('/dashboard') to the target route after the loginAction post response, but this doesn't work, as $http.get() is quite different than a GET call that results from actually clicking on an href="/dashboard" anchor tag. The latter click event calls the dashboard page as it should, rendering it in the tag on the index page. Is there a 'graceful', angular way to handle this ? This issue is the same using node's express webserver or a custom webserver that leverages filestreams.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var app = angular.module('myApp',['ui.router']);
app.config(function($stateProvider) {
var aboutState = {
name: 'about', //for testing
templateUrl: '/about'
};
var dashboardState = {
name: 'dashboard',
templateUrl: '/dashboard'
};
$stateProvider.state(aboutState);
$stateProvider.state(dashboardState);
});
controller
app.controller("myCtrl", function($scope, $http) {
$scope.userMessage = "";
$scope.loginSubmit = function () {
$scope.userMessage = "Submitting...";
$http.post("/loginAction",{ 'username': $scope.username, 'password':$scope.password }).then(function(response) {
if( response.data.loginStatus == 'Authenticated' && response.data.userType != '' ) {
// OK ! - we're free to go to the dashboard page now. But how ?
// I could do: document.querySelector("#dash").click();
// this works, but this doesn't seem very secure
// The following doesn't work:
$http.get("/dashboard").then(function( response ) {
// Why doesn't the above '/dashboard' route , but
// clicking on something like Dashboard actually works ?
// Short of taking the dashboard html in the response and using bind-html to force it
// into the dom, is there a better solution to avoid a window.location reload here ?
$scope.userMessage = "Login Successful";
});
}
});
}
});
I think I answered my own question. I needed to leverage the 'ngRoute' service and inject $location into my controller like so:
<script>
var app = angular.module('myApp',['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'login',
controller : 'myCtrl'
})
.when('/test', {
templateUrl : '/test',
controller : 'myCtrl'
})
.when('/dashboard', {
templateUrl :'/dashboard',
controller : 'myCtrl'
}).otherwise({redirectTo: '/'});
});
app.controller("myCtrl", function( $scope, $http, $location ) {
$scope.userMessage = "";
// fire this function upon successful login:
$scope.changeRoute = function( route ) {
$location.path( route );
}
$scope.loginSubmit = function () {
$scope.userMessage = "Submitting...";
$http.post("/loginAction",{ 'username': $scope.username, 'password':$scope.password }).then(function(response) {
if( response.data.loginStatus == 'Authenticated' && response.data.userType != '' ) {
$scope.userMessage = "Authenticated...";
$scope.changeRoute( response.data.destination_route );
}
});
}
});

Is it possible to pass param from state to state with angularJs?

I wish to pass a param from one store-state to the display product info in products-state:
My app - storeApp
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('store', {
url: '/store',
templateUrl: 'store/store',
controller: 'storeCtrl'
})
.state('products', {
url: '/products/:productSku',
templateUrl: 'store/product',
controller: 'productCtrl',
resolve: {
productResource: 'productFactory',
_product: function(productResource, $stateParams){
return productResource.getProduct($stateParams.productSku);
}
}
Store.jade
a(href='/products/{{product.sku}}')
Product controller
.controller("productCtrl", function ($rootScope, $http, $stateParams, productFactory, storeFactory) {
//.controller('productCtrl', ['_product', function ($scope, $rootScope, storeFactory, _product) {
console.log($stateParams.productSku);
Product Factory
function getProduct(sku) {
return $http.get('http://localhost:3000/api/products/' + sku );
}
Since I am using MEAN Stack, node has the router attached to express:
Server.js
const storeController = require('./controllers/store');
server.get('/store/product', passportConfig.isAuthenticated, storeController.getProductPage);
Store.js
exports.getProductPage = (req, res) => {
res.render('store/product', {
title: 'PP',
angularApp: 'storeApp'
})
}
I tried returning _product but I get Unknown provider: _productProvider <- _product <- productCtrl
I tried using ui-sref - a(ui-sref="products({productSku:'{{product.sku}}'})") in store.jade to send param from store_State to products_State & finally got an object back from API.
Now the issue is that node will not return the view.
Basically what I am trying to achieve is:
Node serving client views, all store views - store/ product/ cart are attached to angular app served through Server.js, Clicking store product will redirect to product page after resolve product info from api.
I am getting product info but not getting product view.
I looked it up but all solutions did not work....maybe my bad :-(
How can I go about this?
UPDATE-1: this is whats happening:
UPDATE-2:
When I pass the control to angular, I have express routing the menu, and angular stateProvider routing/ connecting views to controllers.
Main view that loads is the store itself:
app.js - store route
$stateProvider
.state('store', {
url: '/store',
templateUrl: 'store/store',
controller: 'storeCtrl'
})
server.js (express)
server.get('/store', passportConfig.isAuthenticated, storeController.getStorePage);
store.js
exports.getStorePage = (req, res) => {
res.render('store/store', {
title: 'S--tore',
angularApp: 'storeApp'
});
}
store.ctr.js
angular.module("storeApp")
.controller("storeCtrl", function($rootScope, $http, storeFactory) {
var products;
storeFactory.getProducts().then(function(_products) {
products = _products.data;
$rootScope.products = products;
});
That loads just fine!
But when I try to send the param productSku from store view to product view and have the resolve send product params back to product view that where it stops working, it's either I get the view OR i get the params.
I tried different ways of resolve, they all result the same - view OR product params.
app.js - product route
.state('products', {
url: '/products/:productSku',
templateUrl: 'store/product',
controller: 'productCtrl',
resolve: {
_product: function ($stateParams, $state, $http) {
return $http.get('http://localhost:3000/api/products/' + $stateParams.productSku );
//return productResource.getProduct($stateParams.productSku)
}
}
})
If I remove the resolve and send a(href='/products/{{product.sku}}') from store.jade I get the template in the route, chrome console error I get is `Error: $injector:unpr Unknown Provider _product <- productCtrl
product.ctr.js
.controller('productCtrl', ['_product', function ($rootScope, $http, $stateParams, productFactory, storeFactory, _product) {
If I send a(ui-sref="products({productSku: product.sku })") with resolve I get product params (shown in WebStorem snapshot above) NO view.
angular will not load jade templates, You will need an html template, The jade template is loaded by express. You might like to try using ui-view like this:
Store.jade
div(ui-view)
a(href='/products/{{product.sku}}')
Which should make angular look for the unnamed view when loading the route.
Your templateUrl's don't look to be pointing to files, perhaps you're missing the file extension?
Make sure you return a $promise in resolve as ui-router waits until they are resolved before rendering the view.
I'd recommend having named views with corresponding config in route too:
.state('store', {
url: '/store',
views: {
'#': {
templateUrl: 'store/store.html',
controller: 'storeCtrl'
}
}
})
.state('products', {
url: '/products/:productSku',
templateUrl: 'store/product',
controller: 'productCtrl',
resolve: {
_product: function ($stateParams, $state, $http) {
return $http.get('http://localhost:3000/api/products/' + $stateParams.productSku ).$promise;
}
}
})
See the docs here: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
This is the solution I found:
store.jade
a(href='/products/{{product.sku}}' ng-click='sku(product.sku)')
stroe.ctr.js
$rootScope.sku = function(value){
storeFactory.singleProduct.productSku = value;
storeFactory.singleProduct.saveSku();
}
store.fac.js
var singleProduct = {
productSku : '',
saveSku: function() {
sessionStorage.productSku = angular.toJson(singleProduct.productSku);
},
getSku: function() {
singleProduct.productSku = angular.fromJson(sessionStorage.productSku);
return singleProduct.productSku;
}
}
product.ctr.js
var sp = storeFactory.singleProduct.getSku();
productFactory.getProduct(sp).then(function (product) {
$rootScope.product = product.data;
});
product.fac.js
function getProduct(sku) {
return $http.get('http://localhost:3000/api/products/' + sku );
}
Basically I am storing productSku to sessionStorage and getting it back from there when product.jade view loads...
Thanx to all who tried...
You can pass it as $state.data in your controller.
toState = $state.get("some-state");
toState.data.foo = $scope.foo;
$state.go(toState);
You have not included dependancy of _products in storeCtrl. When you call that service you get an error.
Correct code is:
angular.module("storeApp")
.controller("storeCtrl", function($rootScope, $http, storeFactory, _products) {
var products;
storeFactory.getProducts().then(function(_products) {
products = _products.data;
$rootScope.products = products;
});
You can use console.log("something here" + _products.data); to see in your browser console

Page is loading twice when using angular ui-router and "resolve"

I'm using Angular 1.5.5, Angular-ui-router 0.2.18, angular-meteor-promiser 1.1.0 and meteor 1.3.3
In my application I use the following route definition:
(There are many other states) :
function routerConfig($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$stateProvider
.state('auditionExtExec', {
url: '/AuditionExtExecute/:id',
templateUrl: 'client/auditions/auditionExtExec.html',
resolve: {
auditionsubs: function($promiser) {
"use strict";
console.log('in resolve');
return $promiser.subscribe('auditions');
},
},
controller: 'AuditionExtExecCtrl',
controllerAs: 'vm'
})
The controller is :
angular
.module('brgo')
.controller('AuditionExtExecCtrl', function($state, $stateParams, $scope, $reactive, $promiser, auditionsubs) {
var vm = this;
$reactive(vm).attach($scope);
vm.auditionId = $stateParams.id;
vm.subscribe('auditions');
vm.start = false;
vm.auditionResults = {};
var currentDate = new Date();
vm.autorun(() => {
vm.audition = Auditions.findOne({
_id: vm.auditionId
});
});
vm.addNewExecution = function() {
vm.auditionResults.auditions = angular.copy(vm.audition.auditions);
AuditionExecs.insert(vm.auditionResults);
};
vm.startaudition = function() {
vm.start = true;
};
});
The purpose of this page is to display the contact of the record from audition MongoDb table and write results to AuditionExec MongoDb table
When the application run, everything looks OK.
However, when I'm in the auditionExtExec state and I copy the URL to a new window, the view in the new window is presented twice.
When I remove the $promiser from the resolve, it looks like it works fine
Remove promiser.subscribe and the problem disappear

Pass Json data to another route template in angular js

After login I want to pass the user details to dashboard?How it possible in angular js?
Login.js
mySchoolApp.controller('loginController', ['$scope', '$http', function($scope, $http) {
this.loginForm = function() {
let encodedString = 'uname=' +this.username +'&pwrd=' +this.password;
sessionStorage.user = encodedString;
console.log(sessionStorage.user)
window.location.href = 'dashboard.html';
}
}]);
In console I'm getting the value.
How to get the user details in dashboard.html page?
You should use ng-route to achieve this.Angular isn't designed to work like this
Here is sample
$stateProvider
.state('app', {
abstract: true,
url: "",
template: '<ui-view/>'
})
.state('app.home', {
url: "/",
templateUrl: "partials/main_page.html",
resolve: {
skipIfLoggedIn: skipIfLoggedIn
}
}).state('app.dashboard', {
url: "/dashboard",
templateUrl: "partials/dashboard.html",
controller: 'DashboardCtrl',
activePage:'dashboard',
resolve: {
loginRequired: loginRequired
}
You can store it in a localstorage.So you can use angular-local-storage Angular module for that.
How to set :
myApp.controller('MainCtrl', function($scope, localStorageService) {
//...
function submit(key, val) {
return localStorageService.set(key, val);
}
//...
});
How to Get :
myApp.controller('MainCtrl', function($scope, localStorageService) {
//...
function getItem(key) {
return localStorageService.get(key);
}
//...
});
You should use router module ui-router or ng-router in order to use angualrjs logic in that sense but then your pages are going to be loaded via ajax and regular session http authentication can not be applied.
If that's the case then use angular service provider and let me know to edit my answer.
If you'd like to keep data across pages and not using database or server.
Then what is left as options are: sessionStorage and localStorage.
The localStorage keeps data permanently until browser cache deletes it while the other one obviously for the session.
sessionStorage.setItem('myCat', 'Tom');
If you want to keep js collection like object or array first stringify it:
var user = {pass:'moo', name: 'boo'};
sessionStorage.setItem('userDetais', JSON.stringify(user));

Angularjs: how to revert back to the runtime loaded DOM elements in the previous view on going back (preserve state)

I have an angular application which has two views:
1) List view
2) Detail View
when you click on the thumbnail from list view you go to detail view, here is the route:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/list', {
templateUrl: 'partials/list.html',
controller: 'ListCtrl',
}).
when('/list/:id', {
templateUrl: 'partials/detail.html',
controller: 'DetailCtrl',
}).
otherwise({
redirectTo: '/list'
});
}]);
Now there is a function loadmore in 'listCtrl' controller which is used to load
myControllers.controller('ListCtrl', ['$scope', '$location', 'Troll', '$http',
function ($scope, $location, Troll, $http) {
$scope.Trolls = Troll.query();
$scope.orderProp = 'popular';
$scope.fromData = {};
//console.log($scope.Trolls);
//this will be used to fetch the data in json which is defined in services.js
$scope.loadmore = function () {
jQuery.ajax({
url: 'trolls/trolls.php?troll_index=' + $('#main-content #item-list .sub-item').size(),
type: 'GET',
async: false,
data: {},
dataType: 'json',
success: function (response) {
if (response != null) {
$.each(response, function (index, item) {
$scope.Trolls.push({
UID: response[index].UID,
id: response[index].id,
popular: response[index].popular,
imageUrl: response[index].imageUrl,
name: response[index].name,
tags: response[index].tags,
category: response[index].category
});
});
}
},
complete: function () {},
error: function () {
console.log('Failed!');
}
});
$scope.text = 'Hello, Angular fanatic.';
$http.get('trolls/trolls.php?troll_id=' + Troll);
}
}]);
PROBLEM:
now the problem is,
After clicking on loadmore if i go to detail view and i come back on list view my newly loaded divs are gone how do i preserve them??
When you change routes the controller that is in charge of that route is initialized when the route is loaded and destroyed when the route is changed. So the reason you lose your data is the controller is reinitialized and the previous data never exists.
There are two ways to fix this.
Higher level controller that is not destroyed - could potentially live on the body - this passes its scope to the children controllers. But this is not a true modularization of concerns. For this issue... Can be very useful for other issues - Authentication, Profile, etc.
The way I would advocate is to pull this into a service for example - listService - this will fetch and cache the data and pass it back to the listController when its reloaded, thus preventing the data from being lost.
First way to solve could be this...
So if you have a higher level controller in charge of fetching the data or move it into a service which is what I would do, then the data that is loaded from loadMore function will continue to be there, but it needs to be on a higher parent scope that is not destroyed on the route change.
HTML:
<body ng-controller="ApplicationController">
<!-- Code Here -->
</body>
Controller:
myControllers.controller('ApplicationController', function($scope) {
var data = [];
$scope.loadmore = function () {
// Use Angular here!!! $http not jQuery!
// Its possible to write a complete Angular app and not ever true jQuery
// jQuery Lite the Angular implementation will be used though
jQuery.ajax({
url: 'trolls/trolls.php?troll_index=' + $('#main-content #item-list .sub-item').size(),
type: 'GET',
async: false,
data: {},
dataType: 'json',
success: function (response) {
if (response != null) {
$.each(response, function (index, item) {
data.push({
UID: response[index].UID,
id: response[index].id,
popular: response[index].popular,
imageUrl: response[index].imageUrl,
name: response[index].name,
tags: response[index].tags,
category: response[index].category
});
});
}
return data;
}
error: function () {
console.log('Failed!');
}
});
}
});
However I dont really like this approach as its a bit hacky...and jQuery is used...
Second approach using a service to fetch/cache:
So lets pull it into a service instead.
myServices.factory('listService', function($http, $q) {
var//iable declaration
service = {},
list = []
;
/////////////////////
//Private functions//
/////////////////////
function loadMore(url) {
var deferred = $q.defer();
$http({ method: 'GET', url: url }) // Need to pass in the specific URL maybe from the DOM scoped function?
.success(function(data) {
deferred.resolve(data);
})
.error(function() {
deferred.reject();
//Do error things
});
return deferred.promise;
}
////////////////////
//Public Functions//
////////////////////
service.loadMore = function(url) {
// Used for loading more data
loadMore(url).then(function(data) {
list.push(data);
return list
});
}
service.getList = function() {
// Returns the currently loaded data
return list;
}
return service;
});
Then in your controller:
myControllers.controller('ListCtrl', ['$scope', '$location', 'Troll', listService
function ($scope, $location, Troll, listService) {
$scope.Trolls = Troll.query();
$scope.orderProp = 'popular';
$scope.fromData = {};
$scope.loadmore = function(subItemSize) { //add url specific params here
var url = 'trolls/trolls.php?troll_index=' + subItemSize;
return listService.loadMore(url);
};
}]);
use ui-router instead:
https://github.com/angular-ui/ui-router, you can have multiple ui-view and also you can use relative routes, you should define child states, this way the parent state remains unchanged when route changes to a child state, check out this video:https://egghead.io/lessons/angularjs-introduction-ui-router
i created a plunker:http://plnkr.co/edit/FA3DuHgngKD2CIPG5bHW?p=preview
You can store data in a service or cache it into the Angular cache, but your divs will still be gone when you route back to that view.
If you want to preserve the state of the DOM too, there's an extension to ui-router called ui-router-extras. It has a a nice demo with state transitions and full DOM preservation when switching among tabs.

Categories