I know there are many questions regarding the angular $scope by after a couple of hours I still can't figure out what is wrong. I'm using Angular.js with Ionic but the problem should be angular related.
In my App I need a "add to wishlist" function. In the productDetail view when someone tapps "add to wishlist" I add this product with a service and webSQL to the database. Now when the user goes to the wishlist view I get all saves products but the view doesn't update.
I have a file menu.html with an Ionic side-menu:
<ion-side-menu side="left" expose-aside-when="large" width="72">
<ion-content>
<ion-list ng-controller="LeftMenuCtrl">
<ion-item nav-clear menu-close ng-href="#/app/wish" ng-click="updateWishList()" ng-class="{'current': isActive('/app/wish')}">
<span class="icon icon-menu-wish"></span>
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
The "LeftMenuCtrl" handels the hover for the active menu item.
In my WishListView I would like to show the current added products:
File wishList.html
<ion-view hide-nav-bar="true" view-title="WISHLIST" ng-controller="WishlistCtrl">
<div class="bar bar-header bar-stable">
<div class="header-wrapper">
<!--heading-->
<div class="dash-slider-wrapper"" >
<div class="header-text-main">WHISH</div> <div class="header-text-sub">LIST</div>
</div>
</div>
</div>
<ion-content class="has-header">
<ion-item class="product-card" ng-repeat="product in products">
<div class="card">
EAN: {{product.ean}}
</div>
</ion-item>
</ion-content>
</ion-view>
And the controller:
app.controller("WishlistCtrl", function($scope, $state, productService) {
//gets called when the controller is loaded
productService.getWishlistProducts().then(function (products) {
$scope.products = products;
});
//gets called when the user clicks on the navi but does not
//update the $scope (or it updates the wrong $scope)
$scope.updateWishList = function () {
productService.getWishlistProducts().then(function (products) {
$scope.products = products;
});
};
})
Can anyone tell me whats wrong here please?
Edit:
This is the state for the menu link:
.state('app.wish', {
url: "/wish",
views: {
'menuContent': {
templateUrl: "templates/wish.html"
}
}
})
Edit2:
When I refresh the page, I see all added products. But not immediately. It's like the
$scope.updateWishList = function () ...
creates another $scope...
Edit3:
app.controller('ProductDetailCtrl', function($stateParams ,$state, $scope, productService, $ionicHistory, $ionicPopup) {
$scope.addToWishlist = function(ean) {
productService.addProductToWishlist(ean).then(function(response) {
if(response == "OK") {
var alertPopup = $ionicPopup.alert({
title: 'product added to wishlist',
template: 'You can go to your wishlist to see all your marked products'
});
}
});
};
})
Edit4:
I added a Plunker here: http://plnkr.co/edit/0woPfN
It´s not working but you can see the code there (I deleted unnecessary stuff)
Your problem is that you have 2 declaration of the WishlistCtrl.
Once in the app.js:
.state('app.wish', {
url: "/wish",
views: {
'menuContent': {
templateUrl: "wish.html",
controller: 'WishlistCtrl' //here is the first declaration
}
}
})
Second in the HTML:
<ion-view hide-nav-bar="true" view-title="WISHLIST" ng-controller="WishlistCtrl">
Remove either one and you'll be fine.
Finally, I got it working.
First in the StateProvicer I added a resolve method:
.state('app.wish', {
url: "/wish",
views: {
'menuContent': {
templateUrl: "templates/wish.html",
controller: 'WishlistCtrl',
resolve: {
products: function(productService) {
return productService.getWishlistProducts()
}
}
}
}
})
And to update the model when a new product gets added to the wishlist:
.controller("WishlistCtrl", function($scope, $state, productService, products) {
//set wishlist products on controller load
$scope.products = products;
//updated wishlist when a new product gets added
$rootScope.$on('updatedWishList', function() {
console.log('List has been updated. ');
productService.getWishlistProducts().then(function (products) {
$scope.products = products;
});
});
})
Easy... when you know how :D
Related
Although there are a few of these questions rocking about, none of them seem to have a good working example and i cant seem to figure out how to apply it to my project.
What i want to do is pull the data through from the list item into another view to use as a profile page.
JS
var app = angular.module('passDataDemo', ['ngRoute']);
// Configure our routes
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
// Route for the global controller
.when('/', {
templateUrl: 'list.html'
})
// Route for profile
.when('/:item.firstname', {
templateUrl: 'listItemProfile.html'
})
// Fallback
.otherwise({
redirectTo: '/'
});
}]);
// Main controller
app.controller('mainCtrl', ['$scope', '$http', function($scope, $http) {
$http.get('items.json').success(function(data) {
$scope.items = data;
});
}]);
View 1 html
<ul ng-repeat="item in items">
<li>
<p>{{item.firstname}}</p>
<p>{{item.surname}}</p>
<button ng-click="viewTheProfileView()">View Profile</button>
View 2 html
<h1>ITEM PROFILE</h1>
<p>{{item.firstname}}</p>
<p>{{item.surname}}</p>
Here is my Plunker - https://plnkr.co/edit/gZj5gOZEcCOLNbdxwduw?p=preview
After looking at your plunker, I see that you are only using one controller for all of your views. Because of this, you would only need to set the value of $scope.item and it will show up in your listItemProfile.html. Therefore, I would change your button to pass in the current item to the controller: <button ng-click="viewTheProfileView(item)">View Profile</button>.
Then in your controller:
$scope.viewTheProfileView = function(item) {
$scope.item = item;
};
This will allow clicking the ITEM PROFILE button in index.html to display the correct information.
I am working on an Ionic app where, I need to pass an item (json object) to a view and bind the items there.
I've gone through a lot of similar questions, likely:
Unable to access $scope variable within same controller
AngularJS: Pass an object into a state using ui-router
But, seems like none is working in my case.
Here is my HTML markup of index.html page (from where I'm passing item):
<div class="list card slide-in" ng-repeat="item in posts track by $index">
<h2 ng-bind="item.title" class="assertive"></h2>
//comes inside ng-repeat loop; I'm passing the whole json object as parameter to next view
<button class="button button-full" ng-click="getPostDetail(item);"> Read More </button>
</div>
Html Markup of posts.html page (where I need to access item):
<ion-view view-title="Read More ">
<ion-content class="card-background-page ">
<div class="list padding-20">
<h2 ng-bind="selectedPost.title" class="assertive"></h2>
</div>
</ion-content>
</ion-view>
My Config:
app.config(function ($stateProvider, $urlRouterProvider, localStorageServiceProvider) {
$stateProvider
.state('tabs.posts', {
url: "/posts",
params: {
obj: null // as per other suggestion, added a param here
},
views: {
'home-tab': {
templateUrl: "templates/post.html",
controller: 'main'
}
}
})
My Controller:
app.controller('main', function ($scope, $http, $state, $ionicModal, $location, $stateParams, localStorageService) {
$scope.getPostDetail = function (data) {
var selectedPost = data;
$state.go("tabs.posts", { obj: selectedPost });
}
})
On click of button, the Posts View loads successfully, but no content is bound i.e. the view is blank
Unless I'm missing something, selectedPost needs to be on $scope. Otherwise it cannot be accessed from the view. So something like this.
$scope.getPostDetail = function (data) {
$scope.selectedPost = data;
$state.go("tabs.posts", { obj: selectedPost });
}
I tried many things to pass data to my view, but i don't know what is wrong.
I tried with $scope and pass data with $state.go, but nothing happens.
.state('app.ler',
{
views: {
'menuContent': {
templateUrl: 'mensagens/mensagemRead.html',
controller: 'MensagensCtrl',
params: {'assunto': null}
}
},
url: '/ler'
})
$scope.read = function (id) {
MensagensService.ler(id).then(function (response) {
// $scope.mensagem_ler = response.data[0];
// alert(response.data[0].assunto);
$state.go('app.ler', {assunto:response.data[0].assunto});
console.log($stateParams);
});
};
<ion-view title="Mensagens2">
<ion-content class="has-header">
<div class="list">
<a class="item item-avatar" href="#">
<img src="venkman.jpg">
<h2>Gustavoaaa</h2>
<!--<p>{{mensagem_ler.assunto}}</p>-->
<p>{{assunto}}</p>
</a>
</div>
</ion-content>
</ion-view>
You can add the state parameter using a : in the url of your state.
.state('app.ler',
{
views: {
'menuContent': {
templateUrl: 'mensagens/mensagemRead.html',
controller: 'MensagensCtrl'
}
},
url: '/ler/:assunto'
});
This will make the property assunto available in $stateParams
params option of state is available in root level of state definition, you could not have them in leaf level of namedView.
.state('app.ler', {
views: {
'menuContent': {
templateUrl: 'mensagens/mensagemRead.html',
controller: 'MensagensCtrl',
}
},
url: '/ler/{assunto}', //here is parameter mentioned in URL
params: {
'assunto': null //parameter default value
}
})
You can get this values in $stateParams object inside your controller.
Try this in your state configurations
url: '/ler?assunto'
If you want to pass more parameters then just add them like this
url: '/ler?assunto¶m2¶m3....'
To use the state param in your view you can save it in a $scope variable in your controller and then use it.
For example: $scope.assunto = $stateParams.assunto;
<ion-view title="Mensagens2">
<ion-content class="has-header">
<div class="list">
<a class="item item-avatar" href="#">
<img src="venkman.jpg">
<h2>Gustavoaaa</h2>
<!--<p>{{mensagem_ler.assunto}}</p>-->
<p>{{assunto}}</p>
</a>
</div>
</ion-content>
</ion-view>
I am a newbie to angular JS and IONIC.
I have left side menu:
- Menu 1
- Menu 2
- Menu 3
And right side menu dynamically change based on the selected left menu.
I successfully did this. But, the problem is when i change to other Menu, my right side menu won't be updated before i refresh the page (using F5).
Whats wrong with my code:
controller.js
angular.module('starter.controllers', [])
.controller('AppCtrl', function ($state, $scope, $stateParams) {
var source = $stateParams.source;
$scope.title = source;
$scope.data = {items: []};
if (source == 'Menu1') {
$scope.data.items.push({url: source + '/AAA', label: "AAA"});
$scope.data.items.push({url: source + '/BBB', label: "BBB"});
$scope.data.items.push({url: source + '/CCC', label: "CCC"});
}
if (source == 'Menu2') {
$scope.data.items.push({url: source + '/EEE', label: "EEE"});
}
if (source == 'Menu3') {
$scope.data.items.push({url: source + '/FFF', label: "FFF"});
}
})
.controller('NewsListCtrl', function ($scope, $stateParams) {
})
;
app.js
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: "/app/:source/:channel",
templateUrl: "templates/layout.html",
controller: 'AppCtrl'
})
.state('app.newsList', {
url: "/news-list",
views: {
'menuContent': {
templateUrl: "templates/news-list.html",
controller: 'NewsListCtrl'
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/app/Menu1/AAA/news-list');
});
layout.html
<ion-side-menus enable-menu-with-back-views="false">>
<ion-side-menu-content>
...
</ion-side-menu-content>
<ion-side-menu side="left">
<ion-content>
<ion-list>
<ion-item menu-close href="#/app/Menu1/AAA/news-list">
AAA
</ion-item>
<ion-item menu-close href="#/app/Menu2/EEE/news-list">
EEE
</ion-item>
<ion-item menu-close href="#/app/Menu3/FFF/news-list">
FFF
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
<ion-side-menu side="right">
<ion-content>
<ion-list>
<ion-item menu-close ng-repeat="item in data.items" href="#/app/{{item.url}}/news-list">
{{item.label}}
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
Since the parent controller doesn't get re-created when the child state changes, you could have the parent controller listen for $stateChangeSuccess and update the menu based on the state params:
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){ ... })
https://github.com/angular-ui/ui-router/wiki#state-change-events
I'm playing with this ionicView events that are fired whenever the views become active, and I'm using the side menu template that you can re-use when creating the project. It seems that if I put a listener for the $ionicView.entered event in the AppCtrl (the one used by the side menu template, which belongs to an abstract state in the ui-router configuration) it gets called twice in a row for any of the subviews (like, when using app.someotherview as a state).
I don't know if this is the expected behavior because from the documentation I would expect it to only fire once, no matter if I change the subview (the menuContent view).
I'd like to know if this is expected behavior and if so, how do I get to have an event fired only once every time it gets to show the side menu template.
This is what I've got written:
This is from the application module:
.config(function($stateProvider, $urlRouterProvider, $httpProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('app.overview', {
url: "/overview",
views: {
'menuContent': {
templateUrl: "templates/overview.html",
controller: 'OverviewCtrl'
}
}
})
.state('login', {
url: "/login",
templateUrl: "templates/identificationscreen.html",
controller: "IdentificationCtrl"
})
.state('app.agenda', {
url: "/agenda",
views: {
'menuContent': {
templateUrl: "templates/agenda.html",
controller: 'AgendaCtrl'
}
}
});
$httpProvider.interceptors.push('credentialsInjector');
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/login');
Then the AppCtrl is this:
angular.module('dashboard.controllers.app', [])
.controller('AppCtrl', function($scope, $ionicModal, $timeout, $ionicSideMenuDelegate, authService, $state) {
$scope.logout = function() {
authService.logout();
$state.go('login');
};
$scope.$on('$ionicView.enter', function(){ //This is fired twice in a row
console.log("App view (menu) entered.");
console.log(arguments);
});
$scope.$on('$ionicView.leave', function(){ //This just one when leaving, which happens when I logout
console.log("App view (menu) leaved.");
console.log(arguments);
});
});
The menu template:
<ion-side-menus enable-menu-with-back-views="false">
<ion-side-menu-content edge-drag-threshold="true">
<ion-nav-bar class="bar-stable">
<ion-nav-back-button>
</ion-nav-back-button>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" menu-toggle="left">
</button>
</ion-nav-buttons>
</ion-nav-bar>
<ion-nav-view name="menuContent"></ion-nav-view>
</ion-side-menu-content>
<ion-side-menu side="left">
<ion-header-bar class="bar-stable">
<h1 class="title">APPoint!</h1>
</ion-header-bar>
<ion-content>
<ion-list>
<ion-item nav-clear menu-close href="#/app/overview">
Overview
</ion-item>
<ion-item nav-clear menu-close href="#/app/agenda">
Agenda
</ion-item>
<ion-item nav-clear menu-close ng-click="logout()">
Logout
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
A simple workaround is:
$scope.$on('$ionicView.enter', function(ev) {
if(ev.targetScope !== $scope)
return;
// Your code which should only run once
});
The best option that I found to simulate onEnter event was to use in view controller :
$scope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
if (toState.name == "stateName")
doSomething();
}
You can globally disable the caching mechanism used by ionic by doing:
$ionicConfigProvider.views.maxCache(0);
I haven't tried that on my own though.
Else, the best way that worked for me was doing
$scope.$on("$ionicView.afterLeave", function () {
$ionicHistory.clearCache();
});
This is to clear the cache before leaving the view to re-run controller every time you enter back again.
Try $ionicView.afterEnter. That fires only once for me.
URL
At first I used Florian's answer but noticed that in my case it's just a symptom of a root problem, because this doesn't happen to all controllers, just this one.
In my case, it happens because I was trying to incrementally migrate from Ionic/AngularJS v1 to Ionic/AngularJS v2.
I have added controllerAs in my template:
<ion-view view-title="Avatar » Instruments" ng-controller="AvatarInstrumentsCtrl as vm">
but I forgot to remove the controller reference in my app.js :
.state('app.avatar-instruments', {
url: "/avatar/instruments",
views: {
'menuContent': {
templateUrl: "templates/avatar/instruments.html",
controller: 'AvatarInstrumentsCtrl'
}
}
})
So in my case the solution is:
.state('app.avatar-instruments', {
url: "/avatar/instruments",
views: {
'menuContent': {
templateUrl: "templates/avatar/instruments.html"
}
}
})