Switch among different templates/views - angularjs

I want to propose two layouts (ie, horizontal and vertical) for my contents. So switching in the selector will lead automatically to the corresponding layout. My current JSBin cannot accomplish this switching:
<html ng-app="flapperNews">
<head>
<script src="https://code.jquery.com/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router.js"></script>
<script type="text/ng-template" id="horizontal.tpl">
{{one}}, {{two}}
</script>
<script type="text/ng-template" id="vertical.tpl">
{{one}}<br>{{two}}
</script>
<script>
var app = angular.module('flapperNews', ['ui.router']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('entry', {
url: '/',
templateUrl: "vertical.tpl"
})
}]);
app.controller('MainCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.one = "one";
$scope.two = "two";
$scope.layouts = ["horizontal", "vertical"];
$scope.$watch('layout', function () {
$state.go('entry'); // need to amend this such that changing "layout" leads to different template
})
}])
</script>
</head>
<body ng-controller="MainCtrl">
<select ng-model="layout" ng-options="x for x in layouts"></select>
<br><br>
<ui-view></ui-view>
</body>
</html>
Additionally, I hope the solution would NOT display the layout information in the URL; users can only see and choose layout in the web page. Moreover, I don't want a solution by SHOWING/HIDING <ui-view="horizontal"></ui-view>" or <ui-view="vertical"></ui-view> based on the selection. I would prefer a solution that passes layout information to states to choose the corresponding template (but without disclosing it in URL).
Does anyone know how to do this?

Use a non-url parameter such as layout.
Choose the template using a templateUrl function.
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('entry', {
url: '/',
params: { layout: 'vertical' },
templateUrl: function(params) {
return params.layout + ".tpl";
}
})
}]);
then you can switch using state.go or ui-sref
ui-sref="entry({ layout: 'horizontal' })"

Related

AngularJS controller not accessed

I am learning AngularJS and I have a problem with the controller.
This example below already worked, but it suddenly stoped without any changes that could cause that. I am not sure what is wrong.
The /templates/index.html file does not load.
This is my main index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Todolist</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- ANGULAR JS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.3/angular-ui-router.min.js"></script>
<script src="app/app.js"></script>
<script src="app/controllers/todolists.js"></script>
</head>
<body ng-app="app">
<div ui-view></div>
</body>
</html>
This is my app.js:
var app = angular.module('app', ['ui.router']);
app.config(function ($stateProvider) {
$stateProvider
.state('/', {
url: "/",
templateUrl: "app/templates/index.html",
controller: 'todolists',
});
});
This is my todolists controller:
var app = angular.module('app');
var controllers = {};
console.log("test");
controllers.todolists = function ($scope, $http) {
console.log("test1");
$http.get('http://localhost:8888/todolist/rest/entries/index.json').
then(function(response) {
console.log(response.data);
$scope.data = response.data;
});
};
app.controller(controllers);
console.log("test") writes to console, but console.log("test1") does not, so the controller is not accessed.
Please take a look and let me know if you have and tips about what could be wrong.
Thanks, Grega
You need to register a name for the controller is the problem. Ive registered the name 'todolistCtrl' which is the first argument needed for the controller registration method. the second argument is the function controllers.todolists.
you can then reference todolistCtrl within you State Provider.
controller
var controllers = {};
console.log("test");
controllers.todolists = function ($scope, $http) {
$http.get('http://localhost:8888/todolist/rest/entries/index.json').
then(function(response) {
console.log(response.data);
$scope.data = response.data;
});
};
angular.module('app')
.controller('todolistCtrl', controllers.todolists);
app
var app = angular.module('app', ['ui.router']);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('/', {
url: "/",
templateUrl: "index.html",
controller: 'todolistCtrl',
});
});
Plunker
Example

Make two layouts share the same $scope

I want to propose two layouts (ie, horizontal and vertical) for my contents. So switching in the selector will lead automatically to the corresponding layout. Here is the JSBin:
<html ng-app="flapperNews">
<head>
<script src="https://code.jquery.com/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router.js"></script>
<script type="text/ng-template" id="horizontal.tpl">
<textarea ng-model="one"></textarea>, <textarea ng-model="two"></textarea>
<br><br>{{one}}+{{two}}
</script>
<script type="text/ng-template" id="vertical.tpl">
<textarea ng-model="one"></textarea><br><textarea ng-model="two"></textarea>
<br><br>{{one}}+{{two}}
</script>
<script>
var app = angular.module('flapperNews', ['ui.router']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('entry', {
url: '/',
params: { tpl: 'vertical' },
templateUrl: function (params) {
return params.tpl + ".tpl"
}
})
}]);
app.controller('MainCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.one = "one";
$scope.two = "two";
$scope.layouts = ["horizontal", "vertical"];
$scope.$watch('layout', function () {
$state.go('entry', {tpl: $scope.layout});
})
}])
</script>
</head>
<body ng-controller="MainCtrl">
<select ng-model="layout" ng-init="layout = 'horizontal' || layouts[0].value" ng-options="x for x in layouts"></select>
<br><br>
<ui-view></ui-view>
</body>
</html>
However, with the above code, each time we change the view, $scope.one and $scope.two are reset to their initial values. I would hope the change in their textarea would remain regardless of the change of layout.
Does anyone know how to solve this?
Easy sharing same data between different views by using factories (AngularJS factory documentation). Try this example, it uses a simple factory named myFactory to share data between controllers. This also does work on the same controller as in your case.
var myApp = angular.module("myApp",[ "ui.router"]);
myApp.config(function ($stateProvider, $urlRouterProvider){
$stateProvider.state("state1", {
url: "#",
template: '<p>{{ aValue }}</p><button ng-click="bindValue(\'its me\')">Bind value</button>',
controller: "myController"
}).state("state2", {
url: "#",
template: '<p>{{ aValue }}</p><button ng-click="bindValue(\'its me\')">Bind value</button>',
controller: "myController"
});
});
myApp.controller( "myController", function($scope, myFactory) {
$scope.aValue = myFactory.myValue;
$scope.bindValue = function (value) {
$scope.aValue = value;
myFactory.myValue = value;
}
});
myApp.factory('myFactory', function () {
return {
myValue: ''
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<div ng-app="myApp">
<nav>
<a ui-sref="state1">State 1</a>
<a ui-sref="state2">State 2</a>
</nav>
<div ui-view></div>
</div>
I think that you should use nested views - you can define main controller on parent route state and define two nested states corresponding to two views. This way parent controller will remain (it's not re-initialised when child states are switched) and only nested states views will be changed. Something like this:
$stateProvider
.state('myState', {
url: '/test/',
template: '<div ui-view></div>',
controller: function() {
//main controller shared by child states
...
}
})
.state('myState.view1', {
url: '/test/view1'
templateUrl: 'tpl-1.hmtl',
...
})
.state('myState.view2', {
url: '/test/view2'
templateUrl: 'tpl-2.hmtl',
...
})

angularJS- initiate HTTP Call from a controller failed

I am building a website using angularJS and PHP. Website has many pages like- Home, About Us etc.
So, I have created a common header for the website which I included in my HTML view like this:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="angular.min.js"></script>
<script src="angular-route.min.js"></script>
<script src="commonController.js"></script>
<script src="homeController.js"></script>
<script src="loginController.js"></script>
<script src="app.js"></script>
</head>
<body>
<div class="header" ng-controller="CommonController"
ng-include="'header.html'"></div>
<div class="main" ng-view></div>
</body>
</html>
and the header (header.html) page looks like this:
<nav class="navbar-inverse navbar-fixed-top" role="navigation">
Home
<a href="#/notifications" >{{vm.commonId}}</a>
</nav>
and header controller has a http call which fetches user id and I am trying to show this id as a label for one of the links mentioned above (commonController.js)
(function() {
angular
.module('myApp.common', ['ngRoute'])
.factory('myCommonService', function($http) {
var baseUrl = 'api/';
return {
getBasicUserInfo:function() {
return $http.get(baseUrl + 'getBasicUserInformation');
}
};
})
.controller('CommonController', function($scope, $routeParams, myCommonService) {
var vm = this;
myCommonService.getBasicUserInfo().success(function(data) {
vm.commonId = data.id;
});
});
})();
But when I navigate through pages, header remains same. So, I'm not able to initiate that http call. There are many pages in my website.
Can this be done? I'm pretty much new to this platform.
Route (app.js)
(function() {
angular.module('myApp', [
'ngRoute',
'myApp.login',
'myApp.home',
'myApp.common'
])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/login', {
controller: 'LoginController',
templateUrl: 'loginView.html',
controllerAs: 'vm'
})
.when('/home', {
controller: 'HomeController',
templateUrl: 'homeView.html',
controllerAs: 'vm'
})
.otherwise({
redirectTo: '/login'
});
}]);
})();
P.S: I excluded other pages to reduce complexity and make my query easy to understand.
Thanks!
Seems to me all that is missing is letting the template know the scoped alias for the controller
<div class="header" ng-controller="CommonController as common"
ng-include="'header.html'"></div>
and in header.html
{{common.commonId}}
Note that I've used common instead of vm as some of your routes are using vm and it's best to avoid conflicts.
If you want to trigger an update on page navigation (ie, route change), change your CommonController to this
.controller('CommonController', function($scope, myCommonService) {
var ctrl = this;
var update = function() {
myCommonService.getBasicUserInfo().then(function(res) {
ctrl.commonId = res.data.id;
});
};
update();
$scope.$on('$routeChangeSuccess', update);
});
You should be able to do this:
.when('/login', {
controller: 'LoginController',
templateUrl: 'loginView.html',
resolve: function(){ //your code here }
controllerAs: 'vm'
})
instead of getting it from the controller
I think that your issue might be that the include can't read the controller, so instead try this:
<nav class="navbar-inverse navbar-fixed-top" role="navigation" ng-controller="CommonController as vm">
Home
<a href="#/notifications" >{{vm.commonId}}</a>
</nav>
Note that you're also missing the controllerAs syntax so I added that.
And remove ng-controller from here:
<div class="header" ng-include="'header.html'"></div>

Angular UI-Router is not inserting templates into ng-view element

I'm trying to build a very simple app using angular UI-Router.
My index.html file:
<!DOCTYPE html>
<html ng-app="elara">
<head>
<script type="text/javascript" src="/bower_components/angular/angular.js"></script>
<script type="text/javascript" src="/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
<script type="text/javascript" src="/js/app.js"></script>
<script type="text/javascript" src="/js/ctrl/indexCtrl.js"></script>
<script type="text/javascript" src="/js/ctrl/registerCtrl.js"></script>
</head>
<body>
<ng-view> </ng-view>
</body>
</html>
app.js file
angular.module('elara', ['ui.router'])
.run(
['$rootScope', '$state', '$stateParams',
function($rootScope, $state, $stateParams) {
// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.For example,
// <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
// to active whenever 'contacts.list' or one of its decendents is active.
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
]
)
.config(
['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/',
templateUrl: '/tmpl/index.html',
controller: 'indexCtrl'
})
.state('register', {
url: '/register',
templateUrl: '/tmpl/register/register.tmpl.html',
controller: 'registerCtrl'
})
}
]);
The indexCtrl.js file
app = angular.module('elara', []);
app.controller('indexCtrl', ['$scope', function($scope){
$scope.message = "index"
}])
and registerCtrl.js is same. Just controller name and message is different.
in my x.tmpl.html files i just want to write message variable.
{{message}}
Problem is, when I navigate to url / or #/register I am always getting empty page. message variable is not in the page or my template htmls are not loaded.
Also there are not errors in the console.
The target for states is ui-view
// <ng-view> </ng-view>
<div ui-view=""></div>
In case, we use named views : {'myName': { ... } } we provide the name like this
<div ui-view="myName"></div>

Data-Binding not working in view page - Angular.js

I have shown a simplified version of my problem below:
The directive 'name' is not understood when it is in the view 'loginView.html', however it works fine when It's in the main page 'index.html'
loginView.html
<p>Welcome : {{name}}</p>
<input type="text" data-ng-model="name"><br>
<button type="submit" data-ng-click="loginUser()">Login</button>
index.html
<!doctype html>
<html data-ng-app="vla">
<head>
<title>Video Learning application</title>
<link rel="stylesheet" type="text/css" href="css/style.css"/>
</head>
<body>
<div class = "navBar">
<div class = "leftButton"></div>
<div class = "title">VLA</div>
<div class = "rightButton"></div>
</div>
<div data-ng-view=""></div>
<script src="scripts/angular.js"></script>
<script src="scripts/angular-route.js"></script>
<script src="controllers/controllers.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
var app = angular.module('vla', ['ngRoute']);
app.config(function ($routeProvider){
$routeProvider
.when('/view1',
{
controller: 'controllers/controllers.js',
templateUrl: 'partials/loginView.html'
})
.otherwise({ redirectTo: '/view1' });
});
controller.js
app.controller('loginController', function ($scope)
{
$scope.loginUser = function () {
alert("test");
}
});
The {{name}} just prints itself out when in the view 'loginView', but works fine and prints he contents of the input field when placed into index.html.
I'm very new at this and would appreciate any help.
Thanks in advance
You should write controller name but not path in controller option of routeProvider:
$routeProvider
.when('/view1',
{
controller: 'loginController',
templateUrl: 'partials/loginView.html'
})
.otherwise({ redirectTo: '/view1' });
And attach file with controller (controllers/controllers.js) with tag <script>.
Just realised the problem was the ordering of the script declarations in the index.php page
The app reference should come before the controller:
<script src="app.js"></script>
<script src="controllers/controllers.js"></script>

Resources