Been following through some Angular JS tutorials and I'm trying to translate them into the Ionic framework but running into some problems. I'm trying to write a reusable HTML control but the model is not being bound to the view. Here is my code:
//App.js
angular.module('starter', ['ionic', 'starter.controllers', 'starter.directives'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html'
})
.state('app.playlists', {
url: '/playlists',
views: {
'menuContent': {
templateUrl: 'templates/playlists.html',
controller: 'PlaylistsCtrl'
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/app/playlists');
});
//Controller.js
angular.module('starter.controllers', [])
.controller('PlaylistsCtrl', function($scope) {
$scope.playlists = [
{ title: 'Reggae', id: 1 },
{ title: 'Chill', id: 2 },
{ title: 'Dubstep', id: 3 },
{ title: 'Indie', id: 4 },
{ title: 'Rap', id: 5 },
{ title: 'Cowbell', id: 6 }
];
})
//Directives.js
angular.module('starter.directives', [])
.directive('testInfo', function(){
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'templates/test_view.html'
};
});
//Test View
<button class="item ion-item" >
The playlist title is + {{playlist.title}}
</button>
//App View
<ion-content>
<ion-list>
<ion-item ng-repeat="playlist in playlists" >
<div ng-click="playListSelected($index)">
<test-info info="playlist"></test-info>
</div>
</ion-item>
</ion-list>
</ion-content>
</ion-view>
//Index.html
I know I'm linking my js files correctly, however in the custom view the playlist.title never has a value. The controller never seems to bind to the html element. Double checking some angular tutorials I was going through, I'm following a similar approach and can't seem to figure out what the problem is.
In your directive you are defining a value on the directive's scope named info. So inside the template for the directive, you need to reference that with the name info, not playlist.
<button class="item ion-item" >
The playlist title is + {{info.title}}
</button>
Related
https://plnkr.co/edit/VV13ty8XaQ20tdqibmFy?p=preview
Expected
After login the dashboard state renders dashboard.html, and all components and ui-views should render: tickers, tags, social(named ui-view) and feed.
Results
After login the dashboard state renders dashboard.html however only the components tickers,tags and feed show up, but not the social (named-ui-view)
I feel that my problem lies somewhere around where I transition from the login state to the dashboard state. Once you hit the dashboard state, it serves up the default template which is the component element tag: <dash-module></dash-module>. This will then render the dash.component template: dashboard.html and controller. However I've lost access to the social view in the dashboard state object.
dashboard.html
<div class="jumbotron text-center">
<h1>The Dashboard</h1>
</div>
<div class="row">
<tickers-module></tickers-module>
<tags-module></tags-module>
// Expecting the social-module-template.html to show below:
<div ui-view="social"></div>
<feed-module></feed-module>
</div>
The routerApp module with the dashboard component full code in Plnkr
// RouterApp module
////////////////////////////////////////////////////////////////////////////////
var routerApp = angular.module('routerApp', ['ui.router', 'tickers', 'tags', 'feed']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
const login = {
name: 'login',
url: '/login',
templateUrl: 'login.html',
bindToController: true,
controllerAs: 'l',
controller: function($state) {
this.login = function() {
$state.go('dashboard', {});
}
}
}
const dashboard = {
name: 'dashboard',
url: '/dashboard',
params: {
ticker: {},
tags: {}
},
template: '<dash-module></dash-module>',
views: {
'' : {
templateUrl: 'dashboard.html',
},
'social' : {
templateUrl: 'social-module-template.html',
controller: function($state) {
console.log('Social init', $state.params);
}
}
}
}
$stateProvider
.state(login)
.state(dashboard);
})
tags.component('dashModule', {
templateUrl: 'dashboard.html',
controller: function($scope, $state) {
console.log('dashModule loaded!');
}
})
This is the part that should render the social html content in the <div ui-view="social"></div>
views: {
'' : {
templateUrl: 'dashboard.html',
},
'social' : {
templateUrl: 'social-module-template.html',
controller: function($state) {
console.log('Social init', $state.params);
}
}
}
I made changes to your plunker here You were missing # here.
const dashboard = {
name: 'dashboard',
url: '/dashboard',
params: {
ticker: {},
tags: {}
},
template: '<dash-module></dash-module>',
views: {
'' : {
templateUrl: 'dashboard.html',
},
'social#dashboard' : {
templateUrl: 'social-module-template.html',
controller: function($state) {
console.log('Social init', $state.params);
}
}
}
}
In order for these components to appear under the home state, we must define them using absolute naming. Specifically, we must use the # syntax to tell AngularJS that these components of our application should be mapped to a specific state. This follows the viewName#stateName syntax and tells our application to utilize named views from an absolute, or specific state. You can read more about relative vs. absolute names here.
See this for more information.
The problem you have is named view has to render in same state i.e Dashboard.
Change the following and it should work.
social#dashboard
Check this Plunkr
Named Views UI router
https://plnkr.co/edit/PWuKuVw9Dn9UJy6l8hZv?p=preview
I have 3 modules, routerApp, tickers and tags. Basically trying to rebuild my app out of smaller mini-apps.
The routerApp template contains the 2 directives for the other modules.
Expected
When you login, then click on the Count button in the tickers.component, I want to send the counter var into the tags.component $scope via $state.go.
Result
Nothing happens. No $state/variable update in the tags.component
Plnkr app.js code:
// TICKERS app module
var tickers = angular.module('tickers', ['ui.router'])
tickers.config(function($stateProvider) {
const tickers = {
name: 'tickers',
url: '/tickers',
parent: 'dashboard',
templateUrl: 'tickers-list.html',
bindToController: true,
controllerAs: 'tik',
controller: function() {
}
}
$stateProvider
.state(tickers);
})
tickers.component('tickersModule', {
templateUrl: 'tickers-list.html',
controller: function($scope, $state) {
console.log('Tickers init')
$scope.counter = 0;
$scope.increase = function() {
$scope.counter++;
console.log('increase', $scope.counter)
$state.go('dashboard.tags', { counter: $scope.counter });
}
}
})
// TAGS app module
var tags = angular.module('tags', ['ui.router'])
tags.config(function($stateProvider) {
const tags = {
name: 'tags',
url: '/tags?counter',
parent: 'dashboard',
params: {
counter: 0
},
templateUrl: 'tags-list.html',
bindToController: true,
controllerAs: 'tags',
controller: function($state) {
}
}
$stateProvider
.state(tags);
})
tags.component('tagsModule', {
templateUrl: 'tags-list.html',
controller: function($scope, $state) {
// Expecting this to update:
console.log('Tags init', $state)
$scope.counter = $state.params.counter;
}
})
// MAIN ROUTERAPP module
var routerApp = angular.module('routerApp', ['ui.router', 'tickers', 'tags']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
const login = {
name: 'login',
url: '/login',
templateUrl: 'login.html',
bindToController: true,
controllerAs: 'l',
controller: function($state) {
this.login = function() {
$state.go('dashboard', {})
}
}
}
const dashboard = {
name: 'dashboard',
url: '/dashboard',
templateUrl: 'dashboard.html',
controller: function() {
}
}
$stateProvider
.state(login)
.state(dashboard);
})
dashboard.html
<div class="jumbotron text-center">
<h1>The Dashboard</h1>
</div>
<div class="row">
<tickers-module></tickers-module>
<tags-module></tags-module>
</div>
The function in the tickers component that is trying to update the $state of the tags component:
$scope.increase = function() {
$scope.counter++;
console.log('increase', $scope.counter)
$state.go('dashboard.tags', { counter: $scope.counter });
}
Also tried: $state.go('tags', { counter: $scope.counter });
Finally the tags.component.
Note that here I'm not seeing the $scope.counter update nor the controller for the component getting refreshed due to a state change.
tags.component('tagsModule', {
templateUrl: 'tags-list.html',
controller: function($scope, $state) {
console.log('Tags init', $state)
$scope.counter = $state.params.counter;
}
})
Is the way I am architecting this going to work? What am I missing?
Update: Added some $rootScope events to watch for $state changes, hope this helps:
This is after clicking the login button and going from the login state to the dashboard state, but still nothing for clicking on the Count button.
So, it looks like you're properly passing the parameters in your $state.go call.
I think the issue here is that you're not properly handling the state parameters you're passing from the tickers component to the tags component.
Try injecting $stateParams into your tags component and pull the parameters off that object, for example (in your tags controller):
$scope.counter = $stateParams.counter;
Figured it out!
https://plnkr.co/edit/CvJLXKYh8Yf5npNa2mUh?p=preview
My problem was that in the $state object tags, I was using the same template as the tags.component.
Instead I needed to change it to something like <p>{{ counter }}</p>
var tags = angular.module('tags', ['ui.router'])
tags.config(function($stateProvider) {
const tags = {
name: 'tags',
url: '/tags',
parent: 'dashboard',
params: {
counter: 0
},
template: '<p>{{ counter }}</p>',
bindToController: true,
controller: function($scope, $state) {
console.log('tags state object', $state)
$scope.counter = $state.params.counter;
}
}
$stateProvider
.state(tags);
})
tags.component('tagsModule', {
templateUrl: 'tags-module-template.html',
controller: function($scope, $state) {
}
})
Then in my tags-module.template.html I needed to add a <div ui-view></div>
<div class="jumbotron text-center">
<h2>Tags list</h2>
<div ui-view></div> // <- here and this is where the $state object updates
{{ counter }}
</div>
Would this result in presenting the page with header, footer and content block filled with content.list view?
$stateProvider
.state('contacts', {
abstract: true,
url: '/contacts',
views: {
header: { templateUrl: 'admin/header.html'},
content: {
templateUrl: 'contacts.html',
controller: function($scope){
$scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
}
},
footer: { templateUrl: 'admin/footer.html'}
}
})
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html'
})
.
<!-- index.html -->
...
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
...
.
<!-- contacts.html -->
<h1>Contacts Page</h1>
<div ui-view></div>
.
<!-- contacts.list.html -->
<ul>
<li ng-repeat="person in contacts">
<a ng-href="#/contacts/{{person.id}}">{{person.name}}</a>
</li>
</ul>
Yes, this will work. There is a working plunker.
The parent view's $scope (the view, defined in state 'contacts' views as a 'content') and its scope, will be a source for prototypical inheritance.
And that means, that its properties will be available in the child state 'contacts.list', because it is injected into that 'content' view
There is in detail more about it:
How do I share $scope data between states in angularjs ui-router?
To prove, it, we can extend the code snippet above with a list controller and inject some more contacts
...
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html',
controller: 'listCtrl', // new controller
})
}
])
// we get already initiated contacts... coming from parent view
.controller('listCtrl', ['$scope', function($scope) {
$scope.contacts
.push({ id: 2, name: "from a child" });
}])
Check it here
I'm new to angular and I'm trying to understand nested views concept.
Based on the example provided in their documentation: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
//home.html
<body>
<div ui-view="header"></div>
<div ui-view="settings"></div>
<div ui-view="content"></div>
</body>
I have settings.html which has a check box. If it's checked it will load in the view(not named) the advanced settings template if not it will load the basic template
//settings.html
<input type="checkbox" ng-change="change()" ng-model="advancedSettings" />
<div ui-view></div>
so far I have defined something like this:
$stateProvider
.state('home', {
views: {
'header': {},
'settings': {
templateUrl: 'settings.html'
},
'content': {},
}
})
since I have 2 templates basicSettings.html and advancedSettings.html that I need to load in the view from settings.html based on that checkbox, I thought I have to declare something like this:
.state('settings#home.basic',(){
templateUrl: 'basicSettings.html'
});
but it's not working, instead I receive a lot of errors on console. How is the best way to implement this, without removing names from homepage views(header,settings,content), also how do I change the view based on the check box?
Thanks
There is a working plunker
Solution here could be with states defined like this:
$stateProvider
.state('home', {
abstract: true,
url: "/home",
views: {
'header': {
template: "This is HEADER"
},
'settings': {
templateUrl: 'settings.html',
controller: 'HomeCtrl',
},
'content': {
template: "This is CONTENT"
},
}
})
.state('home.basic', {
url: "/basic",
templateUrl: 'basicSettings.html'
})
.state('home.advanced', {
url: "/advanced",
templateUrl: 'advancedSettings.html'
})
we have parent state "home" and two children. These are triggered on change by 'HomeCtrl', e.g. like this:
.controller('HomeCtrl', ['$scope', '$state',
function($scope, $state) {
$scope.advancedSettings = false;
$scope.change = function(){
var childState = $scope.advancedSettings
? "home.advanced"
: "home.basic";
$state.go(childState);
}
}])
So, based on the setting, the view target "settings" and its ui-view="" (unnamed one) is filled with a child state - basic or advanced
Check it here
I have a problem with ui-router for angular.js.
I am currently working on a project with angular.js and using ui-router as router.
The problem now is, that I want to have a nested view as like this:
views/settings/index.html (also an previously created template in ui-router)
<div class="settings">
[...]
<div class="settings-content" ui-view="content"></div>
</div>
app.js
$stateProvider.state('settings', {
url: '/settings',
views: {
main: {
templateUrl: 'views/settings/index.html',
controller: 'SettingsController'
},
"content": {
templateUrl: 'views/settings/privacy.html',
controller: 'SettingsController'
}
},
ncyBreadcrumb: { label: 'Settings' }
})
Now the problem I have is, that the defined content template is not injected into the ui-view="content" div. The main content (views/settings/index.html) is loading properly. And in nested states it's also possible to add a template into the ui-view="content" with the same "string": Object.
How does this come?
Thanks in advance!
You must use the viewName#stateName syntax as stated here.
Try this
$stateProvider.state('settings', {
url: '/settings',
views: {
main: {
templateUrl: 'views/settings/index.html',
controller: 'SettingsController'
},
"content#settings": {
templateUrl: 'views/settings/privacy.html',
controller: 'SettingsController'
}
},
ncyBreadcrumb: { label: 'Settings' }
})
I have used the ui-router module in my demo application.
You need to specify the hierarchy in your view.
The parent view is not specified to child view so you must use ViewName#parentState
https://github.com/singhmohancs/angularDemo/tree/master/app/modules