AngularJS - Dynamic Default nested views from Json Data not appearing - angularjs

1. Html:
<html>
<head><title>MyApp</title></head>
<body data-ng-app="app">
<ul><li><a data-ui-sref="home" style="cursor:pointer;">Home</a></li></ul>
<div data-ui-view="header"></div>
<div data-ui-view="container"></div>
<div data-ui-view="footer"></div>
<script src="/Scripts/angular-1.2.9/angular.min.js"></script>
<script src="/Scripts/angular-1.2.9/animate/angular-animate.js"></script>
<script src="/Scripts/angular-1.2.9/ui-router/angular-ui-router.js"></script>
<script src="/Scripts/angular-1.2.9/ui-bootstrap/ui-bootstrap-custom-tpls-0.11.0.js"></script>
<script src="/_temp/app/app.js"></script>
</body>
</html>
2. App.js:
'use strict';
var $stateProviderRef = null;
var $urlRouterProviderRef = null;
var app = angular.module('app', ['ui.router']);
app.factory('menuItems', function ($http) {
return {
all: function () {
return $http({
url: '/_temp/app/jsonData/statesJson.js',
method: 'GET'
});
}
};
});
app.config(function($locationProvider, $urlRouterProvider, $stateProvider) {
$urlRouterProviderRef = $urlRouterProvider;
$stateProviderRef = $stateProvider;
$locationProvider.html5Mode(false);
$urlRouterProviderRef.otherwise("/");
});
app.run(['$q', '$rootScope', '$state', 'menuItems',
function ($q, $rootScope, $state', menuItems) {
menuItems.all().success(function (data) {
angular.forEach(data, function (value, key) {
$stateProviderRef.state(value.name, value);
});
**$state.go("home"); <--- SOLUTION**
});
}]);
3. Data:
[
{
"name": "root",
"url": "/",
"parent": "",
"abstract": true,
"views": {
"header": { "template": "header.html" },
"footer": { "template": "footer.html" }
}
},
{
"name": "home",
"url": "/home",
"parent": "root",
"views": {
"container#": { "template": "home page" }
}
}
]
PROBLEM:
The nested views were suppose to show up on load not the click event. I tried to play with the $urlRouterProviderRef.otherwise("/") to no avail.
What must I correct to have the 3 nested views appear on load not a click event.
Thanks.
UPDATE:
I made a Plunker to help show the problem PLUNKER LINK
Radim Köhler
Sweet!! I updated my plunker to reflect multiple states if anyone wants to see for ideas.

The issue here is: timing. The $urlRouterProviderRef.otherwise("/") instruction is used/executed too soon, before the $http.get() returns the state definition, i.e. before states are defined in foreach: $stateProviderRef.state(value.name, value);
If this would preceed the otherwise trigger, it would work (as you experienced for example with local variable, which is available soon enough).
But we can use another feature: $state.go (older doc link, but like it more than new)
So, with this "dynamic" state definition and $state.go ... we can achieve the desired
app.run(['$q', '$rootScope', '$state', 'menuItems',
function ($q, $rootScope, $state, menuItems) {
menuItems
.all()
.success(function (data)
{
angular.forEach(data, function (value, key) {
$stateProviderRef.state(value.name, value);
});
$state.go("home")
});
}]);
See working plunker.
NOTE: the above const "home" could be just anohter part of the passed JSON definition... e.g. instead of [] use an object with default : "home" and states : [...]. Because do not send array anyway. See Anatomy of a Subtle JSON Vulnerability, cite:
One common mitigation is to make sure that your JSON service always returns its response as a non-array JSON object.

Related

AngularJS router - parent controller loads when child is clicked

I am having strange results working with AngualarJS states. Here is app code:
/* myApp module */
var myApp = angular.module('myApp', ['ui.router'])
.config(function ($stateProvider) {
$stateProvider.state('home', {
url: "home",
template: '<div ui-view><h3>Home</h3><a ui-sref="home.child({reportID:1})">Child</a></div>',
params: { reportID: null },
controller: function ($scope) {
$scope.homeCtrlVar = "home";
console.log("Home controller loaded");
}
}).state('home.child', {
template: '<div><h3>Child</h3><a ui-sref="home">Back</a></div>',
controller: function ($scope) {
$scope.childCtrlVar = "child";
console.log("Child controller loaded");
}
});
})
.controller('MainCtrl', function ($scope, $state) {
console.log("MainCtrl initialized!");
$state.go("home");
});
And main page:
<div ng-app="myApp" ng-controller="MainCtrl">
<h2>My app</h2>
<div ui-view></div>
What's happening is that as long as there parameters for the home state and reportID value doesn't match between a parameter being sent and the state default the home controller is loaded when I click on Child. Can someone please explain why that's happening?
Fiddle
Here is updated code which works as you expect it to:
var myApp = angular.module('myApp', ['ui.router'])
.config(function ($stateProvider) {
$stateProvider.state('home', {
url: "home",
template: '<div ui-view><h3>Home</h3><a ui-sref="home.child({reportID:1})">Child</a></div>',
controller: function ($scope) {
$scope.homeCtrlVar = "home";
console.log("Home controller loaded");
}
}).state('home.child', {
url: "/:reportID",
params: { reportID: null },
template: '<div><h3>Child</h3><a ui-sref="home">Back</a></div>',
controller: function ($scope) {
$scope.childCtrlVar = "child";
console.log("Child controller loaded");
}
});
})
Problem with your approach:
specifying params reportID in home state instead of home.child state.
When user clicks on home.child({ reportId: 1}) it should load home.child, which is fine, and was working with old approach.
However, If you take notice, as you click on home.child({ reportId: 1}), you are sending new parameter reportID(old value was null). reportID belongs to home state, hence its controller is also loaded.
Note that url: "/:reportID" in state home.child is optional.

ng-repeat dynamic menu angularJS

im trying to make a dynamic menu using ng-repeat, ui router and data from a json file.
this is my navbar.html
<ul class="nav navbar-nav" ng-controller="NavBarCtrl">
<div ng-repeat="item in navbarlist">
<li><a ui-sref="item.title" ui-sref-active="active">{{item.title}}</a></li>
</div> <!--ng-repeat-->
</ul>
navbar.js
var app = angular.module("catalogue", ['ui.router'])
app.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider) {
$stateProvider
.state('navbar', {
url: '/navbar',
templateUrl: 'navbar/navbar.html',
controller: 'NavBarCtrl'
})
}])
app.controller('NavBarCtrl', ['$scope', '$http', function($scope, $http, $stateParams) {
$http.get('navbar.json').then(function(response){
// $scope.navbar = response.data;
// $scope.navbar = "navbar.data" [];
// $scope.navbarlist = "navbarlist" [];
$scope.navbarlist = response.data;
});
}])
and navbar.json
{
"navbarlist": [
{
"title": "home"
},
{
"title": "category1"
},
{
"title": "category2"
},
{
"title": "category3"
},
{
"title": "category1"
},
{
"title": "category2"
}
]
}
and i have in index.html
<navbar></navbar>
but my nav bar does not show. Im assuming the problem is with the controller. Where have i gone wrong?
I think your problem not in controller.
You mentioned your tag in index.html, but there is no definition of it.
<navbar></navbar>
To using custom tags like this, read more about directives:
https://docs.angularjs.org/guide/directive
You need to make a directive and specify template or templateUrl indside of it.
For this code example, just put your mark up directly in index.html, it will solve the problem.
Your working code within directive on JSFiddle: https://jsfiddle.net/ukulikov/424z6o2x/

can we add dynamic states to $stateprovider with already existing states in ui-router angular.js

I am trying to add states to my app dynamically and tried using ui-router.
I tried following this thread. AngularJS - UI-router - How to configure dynamic views
In my case, there are some existant states already and i need to append to that list with the dynamic states being read from json
For some reason, i get injector error on $urlRouterProvider when trying to use for deferIntercept() method. In my case, i am using angular 1.3 and the ui-router version is 0.2.10. I see that you can create states synamically. But can we add to the existing list of states already configured statically
Here is my code any help is appreciated,
MY modules.json,
[{
"name": "applications1",
"url": "^/templates/applications1",
"parent": "authenticated",
"abstract": false,
"views": [{
"name": "",
"templateUrl": "html/templates/basicLayout.html"
}, {
"name": "header#applications1",
"templateUrl": "html/templates/header.html"
}],
{
"name": "login",
"url": "/login",
"abstract": false,
"views": [{
"name": "",
"templateUrl": "html/admin/loginForm.html"
}]
}]
My app.js
var $stateProviderRef = null;
var $urlRouterProviderRef = null;
var aModule = angular.module('App', [
'ui.bootstrap','ui.router'
]);
adminModule.run(['$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}])
adminModule.run(['$q', '$rootScope','$http', '$urlRouter',
function ($q, $rootScope, $http, $urlRouter)
{
$http
.get("modules.json")
.success(function(data)
{
angular.forEach(data, function (value, key)
{
var state = {
"url": value.url,
"parent" : value.parent,
"abstract": value.abstract,
"views": {}
};
angular.forEach(value.views, function (view)
{
state.views[view.name] = {
templateUrl : view.templateUrl,
};
});
$stateProviderRef.state(value.name, state);
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
$urlRouter.listen();
});
}]);
aModule.config(['$locationProvider', '$stateProvider', '$urlRouterProvider', '$httpProvider', function ($locationProvider, $stateProvider, $urlRouterProvider, $httpProvider) {
// XSRF token naming
$httpProvider.defaults.xsrfHeaderName = 'x-dt-csrf-header';
$httpProvider.defaults.xsrfCookieName = 'X-CSRF-TOKEN';
$httpProvider.interceptors.push('httpInterceptor');
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'html/XXX/loginForm.html',
controller: 'AController'
})
.state('sAgree', {
url: '/serviceAgreement',
templateUrl: 'html/xxx/s.html',
controller: 'SController'
});
$urlRouterProvider.deferIntercept();
$urlRouterProvider.otherwise('/login');
$locationProvider.html5Mode({enabled: false});
$stateProviderRef = $stateProvider;
$urlRouterProviderRef = $urlRouterProvider;
}]);
There is a working plunker, with all the above snippets.
In case that we want add some states, which are not already existing, we should check the $state.get('stateName')
$http
.get("modules.json")
.success(function(data) {
angular.forEach(data, function(value, key) {
// here we ask if there is a state with the same name
var getExistingState = $state.get(value.name)
// no need to continue, there is state (e.g. login) already
if(getExistingState !== null){
return;
}
var state = {
"url": value.url,
"parent": value.parent,
"abstract": value.abstract,
"views": {}
};
angular.forEach(value.views, function(view) {
state.views[view.name] = {
templateUrl: view.templateUrl,
};
});
$stateProviderRef.state(value.name, state);
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.sync();
$urlRouter.listen();
});
Check that here in action

Flotcharts not rendering with angularjs

I am having issues rendering Flotcharts with data populated from an AngularJS (v1.2.5) service call. When the data is hard-coded, the charts render as expected but not when assigned the value from a $resource call (console logging shows "undefined" as the value).
The odd thing is that the same variable I use to populate the chart data is displayed in the view and works fine which makes me think this could be some sort of race condition or scoping issue.
I have tried assigning the the $scope variables as a result of resolved promises without success to remedy race conditions as well as tried things such as $scope.$apply() from the chart directive.
Below are code excerpts from my application. Any help is appreciated. Thank you.
HTML wrapper:
<!DOCTYPE html>
<html data-ng-app="app" data-ng-controller="app">
<head>
<title>FlotChart Example</title>
</head>
<body>
<main data-ng-view></main>
</body>
</html>
Template from Overview route:
<div class="chart-title">{{getCount.count}} Total Transactions</div>
<div id="deposits" data-chart="pie" data-flot-chart data-ng-model="model" data-ng-controller="OverviewCtrl"></div>
Main Module ("app"):
;(function (angular) {
'use strict';
angular.module('app', ['ngRoute', 'ngResource', 'ngTouch', 'services.Localization', 'Login', 'Overview'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
})
.when('/overview', {
templateUrl: 'views/overview.html',
controller: 'OverviewCtrl'
})
.otherwise({
redirectTo: '/'
});
}])
.controller('App', ['$rootScope', 'Localize', function ($rootScope, Localize) {
// initialize localization
$rootScope.labels = Localize.get();
}]);
}(angular));
Overview Controller (used in charts and view):
;(function (angular) {
'use strict';
angular.module('Overview', ['services.DepositsSvc'])
.controller('OverviewCtrl', ['$rootScope', '$scope', 'DepositCount', 'DepositCountGET', function ($rootScope, $scope, DepositCount, DepositCountGET) {
$scope.getCount = DepositCount.get();
$scope.getGETCount = DepositCountGET.get();
$scope.model = {};
$scope.model.data = [
{
label: $rootScope.labels.gets,
data: $scope.getGETCount.count,
color: "#e4d672"
},
{
label: $rootScope.labels.puts,
data: $scope.getCount.count,
color: "#c2cfeb"
}
];
$scope.model.options = {
series: {
pie: {
show: true,
radius: 1,
label: {
radius: 2/3,
formatter: function (label, series) {
return '<div class="pie">' + label + ': ' +
series.data[0][1] + '<br>(' + Math.round(series.percent) + '%)</div>';
}
}
}
},
legend: {
show: false
}
};
}]);
}(angular));
Services (output from both calls is { count: number }):
;(function (angular) {
'use strict';
angular.module('services.DepositsSvc', ['ngResource', 'ngRoute'])
.factory('DepositCount', ['$resource', function ($resource) {
return $resource('/rest/deposits/count', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}])
.factory('DepositCountGET', ['$resource', function ($resource) {
return $resource('/rest/deposits/countgetdeposits', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}]);
}(angular));
Charts directive:
;(function (angular, $) {
'use strict';
angular.module('directives.FlotCharts', [])
.directive('flotChart', function () {
return {
restrict: 'EA',
controller: ['$scope', '$attrs', function ($scope, $attrs) {
var plotid = '#' + $attrs.id,
model = $scope[$attrs.ngModel];
$scope.$watch('model', function (x) {
$.plot(plotid, x.data, x.options);
});
}]
};
});
}(angular, jQuery));
You should try to distill this down to a smaller example that fits in a jsFiddle, so we can try it out.
One thing that I notice, though, is that you're doing a shallow watch, then setting model.data and model.options. So unless I'm missing something, the watch will not fire when those change; only when model itself changes. Try passing true as the third argument to watch.
I'm not sure that would solve your problem (because this maybe the result of numerous problem) but I think you got the directive scope and model attachment wrong:
*. I'm not sure why you encapsulate the directive as a self invoking function (never saw this style before, and Sure it isn't needed.
*. you are creating a new module here with the ('directives.flotCharts',[]) syntax, without the [] you could attach the directive to any existing module. the important part is that this module isn't injected into app! you should include it in the app module dependency array. without it, angular doesn't know about this directive (and also include the js file in index.html... I sometimes forget and wonder why is this not working)
*. I would suggest rewriting the directive as (and notice the comments also):
something like this:
angular.module('directives.FlotCharts', []) // creaating a new module here
.directive('flotChart', function () {
return {o
restrict: 'EA',
scope:{model:"=ngModel"},//creating a two ways binding to ngModel
controller: ['$scope', '$attrs','$element', function ($scope, $attrs,$element) {
var plotid = '#' + $attrs.id, // you are doing an ugly hack instead of using $element, which comes with the directive, $element is a jquery (or jquery lite) object (if you included jQuery **before** angular.js in index.html it is a jQuery object)
$scope.$watch(function(){return $scope.model}, function (x) {
if (!x){
return;
}
$.plot(plotid, x.data, x.options);//may need to wrap this in an $apply, depends.. //surly this should be called on the $element object - something like: $element.plot(x.data, x.options) although I don't now the specifics of this lib.
}, true); //the true is needed for a deep equality and not just shallow check, which sometimes has problems with objects.. not sure you need this here.
}]
};
});
I also suspect you are treating promises wrong. but not sure this is the problem here (although when everything else would work this probabely could and would be a problem. to scope this answer and question I think you should refine the promises issue to a different and specific question.
Good luck!
Solution documented at AngularJS Google Group.

All variables in $scope undefined in Angular.js

I have a very simple website which uses Angular.js to display its content. I started learning it 2 days ago, and following the official tutorial gave no issues at all.
This is my js file:
var Site = angular.module('Website', []);
Site.config(function ($routeProvider) {
$routeProvider
.when('/home', {templateUrl: 'parts/home.html', controller: 'RouteController'})
.when('/who', {templateUrl: 'parts/who.html', controller: 'RouteController'})
.when('/what', {templateUrl: 'parts/what.html', controller: 'RouteController'})
.when('/where', {templateUrl: 'parts/where.html', controller: 'RouteController'})
.otherwise({redirectTo: '/home'});
});
function AppController ($scope, $rootScope, $http) {
// Set the slug for menu active class
$scope.$on('routeLoaded', function (event, args) {
console.log(args);
$scope.slug = args.slug;
});
}
function RouteController ($scope, $rootScope, $routeParams) {
// Getting the slug from $routeParams
var slug = $routeParams.slug;
var pages = {
"home": {
"title": "Samuele Mattiuzzo",
},
"who": {
"title": "All you'll get, won't blog"
},
"what": {
"title": "Shenanigans about this website"
},
"where": {
"title": "Where can you find me on the net?"
}
};
$scope.$emit('routeLoaded', {slug: slug});
$scope.page = pages[slug];
}
As you can see, it's very simple, it just need to return a page title based on the page slug. In the template (where I load my app with <body ng-controller="AppController">), inside the <ng-view> directive I have one of those partial templates loaded (which is currently working and displaying static content) but I cannot see the content of {{page.title}}.
I have Batarang enabled on my browser and I'm testing my website with web-server.js, but I've read that Batarang has some issues with variables and scopes and always returns undefined, so that's why I added that console.log statement. Doesn't matter what I try to print (args, slug or page, obviously in different parts of the js), it's always undefined.
What am I exactly doing wrong here? Thanks all
None of your controllers are being associated with your "Site".
I believe if you change your free functions to be associated with Site this should get you on the right track. Also, you can simplify your code slightly since the information you're looking for is contained in the $location and not $routeParams.
Site.controller("RouteController", function($scope, $location) {
var slug = $location.path();
var pages = {
"/home": {
"title": "Samuele Mattiuzzo",
},
"/who": {
"title": "All you'll get, won't blog"
},
"/what": {
"title": "Shenanigans about this website"
},
"/where": {
"title": "Where can you find me on the net?"
}
};
$scope.page = pages[slug];
});
Additionally, in your AppController you can watch for $routeChangeSuccess instead of notifying on a location change from your RouteController:
Site.controller("AppController", function($rootScope) {
$rootScope.$on("$routeChangeSuccess", function() { \\do something }
});

Resources