I have a list of articles in this tableofcontent.html file, and they are all clickable links. This page works correctly, and is showing a list of all of the titles. When I click on the links though, it goes to the right template file, but none of the right data is populated in the article.html file. How do I get the data for each link to show up correctly in the articles.html page?
tableofcontent.html
<div class="row" ng-controller="TableofContentController">
// non relevant code removed
<ul>
<li ng-repeat="article in data">
<a ui-sref="article">{{article.title}}</a>
</li>
</ul>
article.html
<div class="row" ng-controller="ArticlesController">
// non relevant code removed
<h3>{{article.title}}</h3>
<h6>Author: {{article.author}} on {{article.date}}</h6>
<p>{{article.body}}</p>
//etc
app.js
// service to retrieve and share data
articleApp.factory('Data', function ($timeout, $http) {
var articles = {
fetch: function() {
return $timeout(function() {
return $http.get('articles.json')
.then(function(response) {
return response.data;
});
}, 30);
}
}
return articles;
});
articleApp.controller('TableofContentController', function ($scope, Data) {
Data.fetch().then(function(data) {
$scope.data = data;
});
});
articleApp.controller('ArticlesController', function ($scope, Data) {
Data.fetch().then(function(data) {
$scope.data = data;
});
});
also in app.js
// table of content state
.state('tableofcontent', {
url: '/index',
templateUrl: 'templates/tableofcontent.html'
})
// main articles page state
.state('article', {
url: '/{{article.title}}',
templateUrl: 'templates/articles.html',
controller: 'ArticlesController',
controllerAs: 'articles',
})
There are actually a bunch of issues here, what you want to do is, when someone clicks on the list of these links, instead of doing
<a ui-sref="article">{{article.title}}</a>
You probably want to do
<a ui-sref="article({id : article.id})">{{article.title}}</a>
Now that you are passing the id of the article, the modules state definition needs to be modified to accept this article id as follows.
.state('article', {
url: 'article/:id',
templateUrl: 'templates/articles.html',
controller: 'ArticlesController',
controllerAs: 'articles',
})
So now when you click on the articles, you should see a network request that looks like this : article/1234 where 1234 is the id of the selected article.
In this example you always seem to be loading the same article.json data, but when interacting with the server you can access the selected article id by using the $stateParams service which you can just inject into your controller.
Related
Here is the code which i have written so as to change my view (without any change in the url)
<a ui-sref="chat.menu" ng-click="click1();">Latest</a>
<a ui-sref="chat.menu" ng-click="click2();">Mail</a>
<div ui-view="{{item}}"></div>
var app = angular.module('myApp',['ui.router'])
.run(['$rootScope','$state','$stateParams', function($rootScope,$state,$stateParams){
$rootScope.$state = $state;
$state.$stateParams = $stateParams;
}]);
app.config(function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home',{
url:'/',
templateUrl:'templates/main.html',
})
.state('chat',{
url:'/chat',
templateUrl:'templates/chatPage.html',
controller: 'chatController',
})
.state('chat.menu',{
views: {
'':{
template: '<h1>Blank</h1>',
},
'latest': {
template: '<h1>Latest</h1>',
},
'mail': {
template: '<h1>Mail</h1>',
}
}
});
});
app.controller('Controller', function($scope,$state, authentication){
$scope.item='';
$scope.click1 = function(){
$scope.item = "latest";
}
$scope.click2 = function(){
$scope.item = "mail";
}
});
Its working fine, but the problem is that I change the the view only once. Once a view is loaded in the ui-view, then another view is not getting loaded in it on the click of the button.
All i want is that, when the user click the button the view should be changed according to the click of the button without any change in the url.
So I'd trying something like this: having two states instead of one. If you want to keep only one state, you'd need to pass parameters to that state, but for a simple example like this, two states will work just fine:
myApp.config(function ($stateProvider){
$stateProvider
.state("latest", {
url: "#",
template: "<h1>Latest</h1>",
controller: "Ctrl1"
})
.state("mail", {
url: "#",
template: "<h1>Mail</h1>",
controller: "Ctrl2"
});
});
What we are doing here is either setting the URL to the same in both states, or setting no URL at all. Also, if you want to assign controllers to your views, here is were we do it. You can assign no controllers, two different controllers, or the same controller if you would like.
Then setup your view like this:
<nav>
<a ui-sref="latest">Latest</a>
<a ui-sref="mail">Mail</a>
</nav>
<div ui-view></div>
Here is a working JSFiddle
I am receiving an external object from WordPress, in one view I have the post.title, and if you click in that title you can go to another view and see te entire post.content.
So far, I can not see the entire post because I am getting a couple errors, posts is undefined.
I made a Plunkr, CodePen and one JSBin for you to understand easier. If you use JSBin is better because you can use the console which is integrated there. All you have to do is click on the title of the post, and you are going to realize that can not go to the other view.
Here is the code regarding my issue, which is the same you will see in the online editors I post above
.state('tabs', {
url: "/tabs",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.news', {
url: "/news",
views: {
'tab-news': {
templateUrl: "tab-news.html",
controller: 'NewsCtrl'
}
}
})
.state('tabs.post-detail', {
url: '/news/:postId',
views: {
'tab-news': {
templateUrl: 'tab-post-detail.html',
controller: 'PostDetailCtrl'
}
}
})
the html for the main view, news
<a ng-href="#/tabs/news/{{post.ID}}">
<h2 ng-bind-html="post.title"></h2>
<p>{{post.date | date}}</p>
</a>
and here is the view where you can not enter yet, the view where are redirected after clicking in the title on the main view
<div>
<h3>{{:: post.title}}</h3>
<p>{{:: post.content}}</p>
</div>
now the controller for the main view
.controller('NewsCtrl', function($scope, $ionicLoading, FreshlyPressed) {
$scope.posts = [];
$scope.doRefresh = function() {
$scope.posts = FreshlyPressed.getBlogs($scope);
}
});
here the service
angular.module('urbanet.app.service', [])
.service('FreshlyPressed', function($http) {
return {
getBlogs: function($scope) {
$scope.posts = [];
$http.jsonp('https://public-api.wordpress.com/rest/v1.1/freshly-pressed?callback=JSON_CALLBACK')
.success(function(result) {
$scope.posts = result.posts;
});
},
get: function(postId, $scope) {
console.log(postId);
console.log($scope.posts);
for (var i = 0; i < $scope.posts.length; i++) {
if ($scope.posts[i].id === parseInt(postId)) {
return $scope.posts[i];
}
}
return null;
}
}
})
and controller for the second view, the view of the entire post
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed) {
$scope.post = FreshlyPressed.get($stateParams.postId, $scope);
});
You are going to have to make a new request for individual posts
The freshly-pressed api returns to you a siteID and postId.
You then combine those to use the posts API to get the single post.
Since freshly-pressed is a constantly changing list, this is the only way you would ever be able to bookmark posts. Tomorrow you may not get the same set of main posts, so wouldn't be able to replicate links that were used today the way you are attempting to
Service method:
getPostById: function(siteId,postId ) {
var url ='https://public-api.wordpress.com/rest/v1.1/sites/'+siteId+'/posts/'+postId+'?callback=JSON_CALLBACK'
return $http.jsonp(url)
}
controller
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed) {
var postId = $stateParams.postId,
siteId = $stateParams.siteId;
FreshlyPressed.getPostById(siteId,postId).success(function(response){
$scope.post = response
})
});
Modify links accordingly
<a ng-href="#/tabs/news/{{post.site_ID}}/{{post.ID}}">
Working demo
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed) {
$scope.post = FreshlyPressed.get($stateParams.postId, $scope);
});
This is a problem. You are trying to pass the entire $scope object as a parameter?
What exactly do you want to pass to the get call?
You have this:
get: function(postId, $scope) { //<- that shouldn't be $scope, make it 'post' or something
console.log(postId);
console.log($scope.posts); //Notice that this comes back undefined?
for (var i = 0; i < $scope.posts.length; i++) { //<- length throws an error because there isn't anything there.
if ($scope.posts[i].id === parseInt(postId)) {
return $scope.posts[i];
}
}
return null;
}
You shouldn't be trying to pass $scope as a param to your service then access $scope inside the factory.
Have your service store your posts data and return that data to your controllers. You can do something like this:
How to make multiple http requests?
Don't try to pass in $scope. Just send the post id to the service and return the data you need.
As #tpie and #charlietfl already mentioned, the current approach (passing $scope as parameter to the service) doesn't work. Another alternative to what tpie suggested (caching the posts data in the service) might be using resolve and promises:
Service code
.service('FreshlyPressed', function($q, $http) {
return {
getBlogs: function() {
var deferred = $q.defer();
$http.jsonp('https://public-api.wordpress.com/rest/v1.1/freshly-pressed?callback=JSON_CALLBACK')
.success(function(result) {
deferred.resolve(result.posts);
});
return deferred.promise;
},
get: function (postId, posts) {
/* snipped, this wasn't the problematic part */
}
}
})
This won't cache the posts result in your service, which might be a downside if the $http call you are making is heavy, but using promises is a nice way of setting up asynchronous requests.
Then corresponding state config
.state('tabs', {
url: "/tabs",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.news', {
url: "/news",
views: {
'tab-news': {
templateUrl: "tab-news.html",
controller: 'NewsCtrl'
}
},
resolve: {
posts: function (FreshlyPressed) {
return FreshlyPressed.getBlogs();
}
}
})
.state('tabs.post-detail', {
url: '/news/:postId',
views: {
'tab-news': {
templateUrl: 'tab-post-detail.html',
controller: 'PostDetailCtrl'
}
},
resolve: {
posts: function (FreshlyPressed) {
return FreshlyPressed.getBlogs();
}
}
})
This will invoke the getBlogs function in your service and wait for the promise to be resolved. After that, you can inject the resolved posts variable into your controllers:
and finally controllers
.controller('NewsCtrl', function($scope, $ionicLoading, FreshlyPressed, posts) {
$scope.posts = posts;
$scope.doRefresh = function() {
FreshlyPressed.getBlogs()
.then(function (posts) {
$scope.posts = posts;
});
}
});
Here we can set up the $scope.posts variable directly using the resolved posts variable. The doRefresh function needs then to be modified so that it will invoke the service function and after the promise is resolved, set the $scope.posts data accordingly.
.controller('PostDetailCtrl', function($scope, $stateParams, FreshlyPressed, posts) {
$scope.post = FreshlyPressed.get($stateParams.postId, posts);
});
And here we give the service get function the resolved posts variable as parameter, as in this case the service isn't caching that data.
This isn't problem-free approach, for example consider following scenario: you get one set of posts data into your main controller. Then, there are new posts posted into the wordpress you are querying, before your user clicks on a title. This might cause the PostDetailCtrl to receive a new set of posts data, that doesn't any longer contain the certain post that your user clicked.
I think this is anyway a viable alternative, and at least a bit of food for thought.
Currently, main list html works
<div class="post row" ng-repeat="(postId, post) in posts">
{{ post.title }}
But when I click the item (one of the many in the list) and go to another page, the new page does not display the item in detail?
When I add the line below, including $stateParams in the dependencies, into the controller js file, {{ post.title }} appears but the data does not pass through.
$scope.post = $scope.posts[$stateParams.id]
UPDATE
This is the states code. (ignore missing syntax...im shortening it). Someone helped resolved the previous issue and provided the below codes for the viewing part (the last 2 states).
.state('tab.view', {
url: '/posts/:postId',
views: {
'tab-view': {
templateUrl: 'templates/tab-showpost.html',
controller: 'PostViewCtrl'
How the details are access after clicking on the list item.
app.controller('PostViewCtrl', function ($scope, $stateParams, Post) {
$scope.Post = Post.find($stateParams.postId); //i think this may be broken
One key piece that is missing in your question is how your stateProvider is configured. Please ensure that your states have the url set up correctly to send data though state parameters. I have a codepen here that shows one way to to have a list of items where clicking on one will take the user to it's details. Note how the states are set up...
angular.module('ionicApp', ['ionic'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.home', {
url: "/home",
views: {
'home-tab': {
templateUrl: "home.html",
controller: 'HomeTabCtrl'
}
}
})
.state('tabs.detailview', {
url: "/detailview/:index", //NOTE: :index is how you use $stateParams later on.
views: {
'home-tab': {
templateUrl: "detailview.html",
controller: 'DetailviewTabCtrl'
}
}
});
$urlRouterProvider.otherwise("/tab/home");
});
Then, in your controller...
.controller('DetailviewTabCtrl', function($scope,$stateParams) {
$scope.id = parseInt($stateParams.index);//NOTE: This is the same 'index'
$scope.previous = parseInt($stateParams.index) - 1;
$scope.next = parseInt($stateParams.index) + 1;
});
I don't know what {{post.url}} is in the ng-repeat; but I think you need a different $state that should handle the detail View.
Do something like:
<div class="post row" ng-repeat="(postId, post) in posts">
<a ui-sref="postDetail({id:post.id })">{{ post.title }}</a>
</div>
You then need a state definition in your app config's $stateProvider like this:
.state('postDetail', {
url: "/post/:id",
//NOTE: :id will be accessed from the controller using $stateParams later on.
templateUrl: "post_detail.html",
controller: 'PostDetailCtrl'
}) ...
That should do it.
I'm pretty new to Angular and I've been going round in circles on this one for a while now.
A bit of background first, I'm using the MEAN stack from mean.io which uses Angular UI Router.
I have a Post model in my DB which can have a category id assigned to it.
When I create a new post I want to load the existing categories from the DB and display them in a select box.
From what I can see I need to use resolve, first of all it doesn't feel right having logic in the resolve property which is in a file called config.js - so far I've placed the call to a service in there and im getting the categories back using the following code:
.state('create post', {
url: '/posts/create',
templateUrl: 'views/posts/create.html',
controller: 'PostsController',
resolve: {
loadCategories: function (Categories) {
Categories.query(function(categories) {
return categories;
});
}
}
})
The first problem is that I can't access the returned data in my controller or view.
Secondly I only want to load Categories that belong to a certain Organisation. I will be assigning an organisation id to each user so how can I access the currently signed in user when I'm in config.js - again this doesn't feel like the right place to be doing this sort of logic though.
Any help would be really appreciated.
Thanks
config.js:
register post state :
.state('post', {
url: '/posts/create',
templateUrl: 'views/posts/create.html',
controller: 'PostsController',
resolve: PostsController.resolve
})
register posts controller:
.controller({
PostsController: ['$scope', 'loadCategories', PostsController],
...
})
controller function:
function PostsController($scope, loadCategories){
$scope.categories = loadCategories;
};
PostsController.resolve = {
loadCategories: ['dependencies', function(dependencies){
return dependencies.query(...)
}]
};
Angular manage your dependency injection
Assuming Categories is an angular resource, you should be able to just
loadCategories: function (Categories) {
return Categories.query();
}
And then in your controller:
.controller('PostsController', function ($scope, loadCategories) {
$scope.categories = loadCategories;
});
Ok, reading your comments, it sounds like you'll have some issue because you want to inject this into the controller, but only in certain states. You could try:
.state('create post', {
url: '/posts/create',
templateUrl: 'views/posts/create.html',
controller: 'PostsController',
data: {
categories: Categories.query()
}
})
and then
.controller('PostsController', function ($scope, $state){
console.log($state.current.data.categories);
});
Which should work...
I'm using Restangular in my Angularjss. I have an index view that displays all messages. I can edit these messages and add to the list without problem. When I try to use remove() via the element, the index page displaying all the messages doesn't update. I must refresh the page. I do not need to refresh the page after an addition or edit.
Sorry for not including a plunker but I'm not sure how to get a working example since my API is local.
I inject the messages object via resolve:
$routeProvider.when('/', {
templateUrl: 'messages-index.html',
controller: 'MessagesController',
resolve: {
messages: function(Restangular) {
return Restangular.all('messages').getList();
}
}
});
And use ng-repeat To display the messages in the index view:
<li ng-repeat="message in messages | filter:query | filter:typeQuery class="messages__item">
<p>
{{ message.message }}
<a class="round secondary label">{{ message.type }}</a>
</p>
</li>
In the edit view, I inject the specific message via resolve:
$routeProvider.when('/messages/:messageId/edit', {
templateUrl: 'messages-edit.html',
controller: 'MessagesEditController',
resolve: {
message: function(Restangular, $route) {
return Restangular.one('messages', $route.current.params.messageId).get();
}
}
});
Lastly, my MessagesEditController looks like this:
.controller('MessagesEditController', ['message', '$scope', '$location', 'Restangular', function(message, $scope, $location, Restangular) {
var original = message;
$scope.message = Restangular.copy(original);
$scope.editMessage = function() {
$scope.message.put().then(function(){
$location.path('/');
}, function(response) {
$scope.formErrors = response.data.error.params;
});
};
$scope.deleteMessage = function() {
original.remove().then(function() {
$location.path("/");
});
};
}])
Anyone know why my messages object is not updated in the index view after removal?
Cheers!
Restangular for some reason leaves it up to the user to update their $scope after a remove operation. Depending on the use case it may be useful.
original.remove().then(function() {
$scope.messages = _.without($scope.messages, original);
});