AngularJS show data from api in form to update it - javascript

im not sure the title explains clear enough what im trying to do, so im gonna try to explain here. i did the blog app from the book angularjs - from novice to ninja. because the api that the book uses is not working anymore, there were some functionalities i wasn't able to test. so later i did another tutorial of phalcon and build and api to do what the one in the angularjs book was supposed to. the api works fine (tested with postman, and also i use it for the login, so the blog app reaches it but i was only able to make it work in the login so far). so i have some doubts about the data i get from the api and how to show it in the for when i try to update one of the posts.
so the services.js file is like this:
angular.module('spBlogger.admin.services', []).
factory('Post', ['$resource', 'API_ENDPOINT', function ($resource, API_ENDPOINT) {
return $resource(API_ENDPOINT, {id:'#_id'}, {
update: {
method: 'PUT'
}
});
}]).
service('popupService', ['$window', function($window){
this.showPopup = function(message){
return $window.confirm(message); //Ask the users if they really want to delete the post entry
}
}]).
value('API_ENDPOINT', 'http://testing-phalcon/api/posts/:id');
the controller.js file:
controller('PostUpdateController', ['$scope', 'Post', '$stateParams', function ($scope, Post, $stateParams) {
console.log('in PostUpdateController');
$scope.post = Post.get({id:$stateParams.id}); //Obtain the Post from backend. Search by Id
console.log($scope.post);
$scope.buttonText = "Update"; // Set initial label for button
$scope.updatePost = function(){
$scope.buttonText = "Updating. . ."; //Once clicked change button text
$scope.post.$update(function(){
$state.go('admin.postViewAll'); //Once updated go to state 'admin.postViewAll'
});
}
}])
the admin-update-post.html file that loads the form:
<div class="row">
<div class="col-xs-8">
<form name="postForm" ng-submit="updatePost()" class="form-horizontal" novalidate role="form">
<div ng-include="'modules/admin/views/_form.html'"></div>
</form>
</div>
</div>
and finally the _form.html:
<div class="form-group" ng-class="{'has-error':postForm.title.$dirty && postForm.title.$invlaid}">
<label for="title" class="col-sm-2 control-label">Post Title</label>
<div class="col-sm-10">
<input type="text" name="title" ng-model="post.title" ng-required="true" class="form-control" id="title" placeholder="Title">
<span>Permalink:<i>/posts/[id]/{{post.title | permalink}}</i></span>
<span class="error-message" ng-show="postForm.title.$dirty && postForm.title.$invalid">Title is mandatory</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error':postForm.content.$dirty && postForm.content.$invalid}">
<label for="content" class="col-sm-2 control-label">Content</label>
<div class="col-sm-10">
<textarea cols="8" rows="6" name="content" class="form-control" ng-model="post.content" ng-required="true" id="content" placeholder="Content"></textarea>
<span>{{post.content | wordcount}} words</span><br/>
<span class="error-message" ng-show="postForm.content.$dirty && postForm.content.$invalid">You need to have some content!</span>
</div>
</div>
<div class="form-group" ng-class="{'has-error':postForm.keywords.$dirty && postForm.keywords.$invalid}">
<label for="keywords" class="col-sm-2 control-label">Keywords</label>
<div class="col-sm-10">
<input type="text" name="keywords" class="form-control" id="keywords" ng-pattern="/^[\w,]+$/" ng-model="post.keywords" placeholder="Comma separated keywords" />
<span class="error-message" ng-show="postForm.keywords.$dirty && postForm.keywords.$invalid">Sorry! No special characters allowed here</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success" ng-disabled="postForm.$invalid">{{buttonText}}</button>
</div>
</div>
What i want to achieve is that when i select a post that i want to update, i want to display the form with the data from the post in the title input, the content in the textarea, and the keywords in the keywords input.
what i tried in the controller (forgive me if it looks bad, but im very new to angular) is:
$scope.thepost = {};
$scope.thepost.title = $scope.post.title;
but this obvioulsy doesn't work. i know the data is getting to the controller because of the console logs (i can see the api answers correctly), but im not sure first how to parse it and then how to pass it from the controller to the view. any help is welcome. thanks!
EDIT
the template that displays all the posts available is admin-all-posts.html:
<div class="row">
<div class="col-xs-8">
<table class="table">
<tr>
<td><h3>View All Posts</h3></td>
<td></td>
</tr>
<tr ng-repeat="post in posts | orderBy:'-_id'">
<td>{{post.title}}</td>
<td>
<a class="btn btn-primary" ui-sref="admin.postUpdate({id:post.id})">Edit</a>
<a class="btn btn-danger" ng-click="deletePost(post)">Delete</a>
</td>
</tr>
</table>
</div>
</div>
the controller that loads the posts from the api is in controllers.js:
controller('PostListController', ['$scope', 'Post', 'popupService', '$state', function ($scope, Post, popupService, $state) {
$scope.posts = Post.query(); //Obtain all the posts from backend
$scope.deletePost = function(post){
if(popupService.showPopup('Really delete this?')){ //Ask for confirmation
post.$delete(function(){
$state.go('admin.postViewAll', undefined, { //once deleted reload the state
reload: true
});
});
}
}
}])
i hope this helps

Examine the $scope.post after the data arrives from the server:
controller('PostUpdateController', ['$scope', 'Post', '$stateParams', function ($scope, Post, $stateParams) {
console.log('in PostUpdateController');
$scope.post = Post.get({id:$stateParams.id}); //Obtain the Post from backend. Search by Id
̶c̶o̶n̶s̶o̶l̶e̶.̶l̶o̶g̶(̶$̶s̶c̶o̶p̶e̶.̶p̶o̶s̶t̶)̶;̶
$scope.post.$promise.then(function() {
console.log($scope.post);
}).catch(function(error) {
console.log("ERROR:", error);
});
$scope.buttonText = "Update"; // Set initial label for button
$scope.updatePost = function(){
$scope.buttonText = "Updating. . ."; //Once clicked change button text
$scope.post.$update(function(){
$state.go('admin.postViewAll'); //Once updated go to state 'admin.postViewAll'
});
}
}])
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.

Related

Get Array from Page 1 and use it on Page 2 using on-Click (AngularJS)

hope you guys are kicking and jumping. Thanks for your usual understanding.
Frameworks: AngularJS, NodeJS
I am designing a login page. But the data to be compared with is an array of items in testData.html. I want to call the data on the login.html and compare it with user's input.
The login form works properly but the data is not read. I tried compiling the dataobject.html file separately, and it did not run.
I do not want to store this data in a .json file.
Later I will learn how to use the MongoDB to read data and compare
Please check the codes below.
[LOGIN.HTML]
<div ng-app="myApp" ng-controller="loginCtrls" style="height:auto;">
<form name="lForm">
<div class="container">
<label><b>Username</b></label>
<input class="w3-round" type="text" name="username" placeholder="Username" ng-model="username" required>
<div align="right" style="width:550px;">
<span style="color:red" ng-show="lForm.username.$dirty && lForm.username.$invalid">
<span ng-show = "lForm.username.$error.required">Username is required.</span>
</span>
</div>
<label><b>Password</b></label>
<input class="w3-round" type="password" name="password" ng-model="password" placeholder="Password" required>
<div align="right" style="width:550px;">
<span style="color:red" ng-show="lForm.password.$dirty && lForm.password.$invalid">
<span ng-show = "lForm.password.$error.required">Password is required.</span>
</span>
</div>
<div align="center">
<button class="w3-btn w3-teal w3-round" style="height:45px; width:100%; font-size:16px;" ng-disabled = "lForm.username.$dirty && lForm.username.$invalid || lForm.password.$dirty && lForm.password.$invalid" ng-click="chkData()">Click to Login</button>
</div>
<input type="checkbox" checked="checked"> Remember me
</div>
<div class="container" style="background-color:#f1f1f1; margin-top:0;">
<span>Forgot password?</span>
</div>
</form>
<h4>{{result}} login attempt</h4>
</div>
<script src="js/loginCtrl.js"></script>
[LOGINCTRL.JS]
// JavaScript Document
var app = angular.module('myApp', []);
app.controller('loginCtrls', function($scope, $http) {
//get the file from the dataobject.html file
$http.get("dataobject.html").then(function (response) {
//parse the array object to $scope.users
$scope.users = response.data.records;
});
//this function checks the user's input and
//compares it with the any match in object array
//the object array data has been passed into $scope.users
$scope.chkData = function(){
$scope.users = $scope.data.records;
angular.forEach($scope.users, function(value, key){
if(angular.equals(value.Username, $scope.username) && (value.Password, $scope.password)){
$scope.result = "Successful ";//msg to be displayed
}else {
$scope.result = "Unsuccessful ";//msg to be displayed
}
});
}
});
[DATA OBJECT.HTML]
<script src = "js/angular.min.js" type="text/javascript"></script>
<div ng-app="myApp" ng-controller="mdata">
</div>
<script>
var app = angular.module('myApp', []);
app.controller('mdata', function($scope) {
$scope.data =
{ "records":[ {"Username":"Alfreds","Password":"alfred","Session ID":"1"}, {"Username":"Ana","Password":"ana","Session ID":"2"}, {"Username":"Moreno","Password":"moreno","Session ID":"3"}] };
});
});
</script>
I would recommend to do it using a service. Angular services are singletons. So, from one controller, you put the data in a service, switch pages, get the data from the service.
NOTE: if user refreshes the page, the data in the service will be lost, as services (or angular for that matter) does not persist state.
ofc, everyone will have their own solution. I see you are a beginner, so the answer is meant to help you get a grasp of angular.
You can store your data either on the $rootScope or by creating localStorage Services and u can access data anywhere in application but the best practices are creating some localStorage services.

AngularJS: How to add newly POST'ed item to the $scope without a page refresh?

this is my controller:
app.controller('mainCtrl',function($scope,$http){
$http.post('/node/api/stickies', $scope.formData)
.then(function(data){
$http.get('/node/api/stickies').success(function(data){
$scope.stickies = data;
})
})
});
right now its definitely not working. and I feel that the $http.get inside of the $http.post may be messing things up. but how else do I update the $scope.stickies without a page refresh?
Desired behavior: I have a list of items called stickies in an ul on my page. There is a post form above the stickies that adds new stickies to the db. I want my ul to add that newly posted stickie without me having to refresh the page.
<form ng-submit = "createSticky()">
<input ng-model="formData.data" type="textarea" name="sticky_content" placeholder="add sticky text" required="true"/>
<input ng-model="formData.end" type="text" name="time_end" placeholder="expiration time (sec from current)" required="true"/>
<button type="submit" name="add_sticky" value="add a new stickie!">new sticky</button>
</form>
<div id = "stickies_list" class="row">
<ul id = "stickies">
<span class="sticky">
<li ng-repeat="stickie in stickies" ng-click="match(stickie)">
<h2>{{stickie.data}}</h2><br><br><br><br><br>
start time: <h10><mydate>{{stickie.start | date:'MM/dd/yyyy # h:mma'}}</mydate></h10><br>
stop time: <h10><mydate>{{stickie.end | date:'MM/dd/yyyy # h:mma'}}</mydate></h10><br><br>
Delete?: <input id="delete_checkbox" type="button" ng-click="deleteSticky(stickie.id)">
</li>
</span>
</ul>
</div>
You need to include the $http service by injecting it into your controller:
app.controller('mainCtrl', function($scope, $http){
...
});

$location.path does not change the route after form submit

I have seen many issues regarding the $location.path in angular js on stackoverflow but none of them would solve the issue I have.
I have defined below routes in my app.js:
angular.module(
'hotPosts',
[ 'hotPosts.filters', 'hotPosts.services',
'hotPosts.directives',
'hotPosts.controllers', 'ngRoute', 'ngSanitize' ]).config(
[ '$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider.when('/hot-posts', {
templateUrl : 'gptarin/partials/hot-posts.jsp',
controller : 'ListPostsCtrl'
});
$routeProvider.when('/list-users', {
templateUrl : 'gptarin/partials/list-users.jsp',
controller : 'ListUserCtrl'
});
$routeProvider.when('/edit-user/:userId', {
templateUrl : 'gptarin/partials/edit-user.jsp',
controller : 'EditUserCtrl'
});
$routeProvider.when('/create-user', {
templateUrl : 'gptarin/partials/edit-user.jsp',
controller : 'EditUserCtrl'
});
$routeProvider.otherwise({
redirectTo : '/hot-posts'
});
$locationProvider.html5Mode(false);
} ]);
The list-users.jsp partial will show a list of users where I can select a record to update. When I click ong update button the ngRoute successfully routes the app to edit-user.jsp partial. However, when I click the Save button in that page, it does not change the route to "/list-users", even though I used the $location.path('/list-users') in Controller EditUserCtrl. It redirects me to "[app_url]/?".
Here is my controllers:
app.controller('EditUserCtrl', [
'$scope',
'$routeParams',
'UserListFactory',
'UserFactory',
'$location',
function($scope, $routeParams, UserListFactory, UserFactory,
$location) {
// callback for ng-click 'updateUser':
// force it to refresh in the client
$scope.saveUser = function() {
if (angular.isUndefinedOrNull($scope.user)) {
$scope.user = {};
UserListFactory.create($scope.user, function() {
$location.path('/list-users');
});
} else if (angular.isUndefinedOrNull($scope.user.id)) {
UserListFactory.create($scope.user, function() {
$location.path('/list-users');
});
} else {
UserFactory.update($scope.user, function() {
$location.path('/list-users');
});
}
};
// callback for ng-click 'cancel':
$scope.cancel = function() {
$location.path('/list-users');
};
if ($routeParams.userId !== undefined) {
$scope.user = UserFactory.show({
userId : $routeParams.userId
});
}
} ]);
The update function (saveUser) uses a service which makes Restful requests to the backend server via ngResource. The service works fine (all tests are passed).
I have enclosed the $location.path in the success callback function when calling the resource actions.
I have tried catching "$locationChangeSuccess" and "$locationChangeError" and saw that in this case a $locationChangeError is thrown but I do not know which promise object is rejected causing this error.
Any help is highly appreciated.
Edit:
Here is the edit-user.jsp partial
<div class="container-fluid">
<div class="panel panel-primary">
<div class="panel-heading">
<h2 class="panel-title label">Edit User</h2>
</div>
<div class="panel-body">
<form role="form" action="#" class="form-horizontal" ng-submit="saveUser()">
<div class="form-group col-sm-11">
<div class="form-group">
<label for="accountId" class="col-sm-1 control-label">G+
Id: </label>
<div class="col-sm-4">
<input type="text" id="accountId" class="form-control"
ng-model="user.gpId"></input>
</div>
<label for="accountName" class="col-sm-2 control-label">Name:
</label>
<div class="col-sm-5">
<input type="text" id="accountName" class="form-control"
ng-model="user.name"></input>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<input type="checkbox" id="showPosts" class="form-control"
ng-model="user.listPosts">ShowPosts</input>
</div>
</div>
</div>
<div class="form-group col-sm-1">
<div class="row">
<div class="col-sm-offset-1 col-sm-12 pull-right">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
<div class="row">
<div class="col-sm-offset-1 col-sm-12 pull-right">
<button type="button" class="btn btn-primary" ng-click="cancel()">Cancel</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
Well After a few days of trying everything and not finding any help over internet I sort of fixed this issue.
I decided to share it with whoever reads this post so that it does not take several days of them as well.
When I traced my app more carefully I figured out that my cancel button worked just fine and $location.path was successfully sending me back to the /list-users page.
Further investigations showed that the difference between my Cancel and Save buttons was that the Cancel button uses the ng-click whereas I defined the type of my Save button to be "submit".
I then changed my html code so that instead of providing the function call saveUser() in ng-submit of the form, I used an ng-click for the Save button and changed its type to "button".
Here is the working version of my html partial. I did not need to change anything in js files.
<form role="form" action="#" class="form-horizontal">
<!-- ng-submit="saveUser()"> -->
<div class="form-group col-sm-11">
<div class="form-group">
<label for="accountId" class="col-sm-1 control-label">G+ Id: </label>
<div class="col-sm-4">
<input type="text" id="accountId" class="form-control" ng-model="user.gpId"></input>
</div>
<label for="accountName" class="col-sm-2 control-label">Name: </label>
<div class="col-sm-5">
<input type="text" id="accountName" class="form-control" ng-model="user.name"></input>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<label>
<input type="checkbox" id="showPosts" ng-model="user.listPosts"> Show Posts
</label>
</div>
<div class="col-sm-12">
<button type="button" class="btn btn-primary" ng-click="saveUser()">Save</button>
<button type="button" class="btn btn-primary" ng-click="cancel()">Cancel</button>
</div>
</div>
</div>
</form>
I still still do not now the mechanics of form submit in angular and why it causes one of the promise objects that the $location.path expects to fail (and hence causing an error in routing).
Any clarifying comment in this regard is much appreciated.
Well after more than a year of experience with Angular, I now know what was the problem in the first instance.
Reading Angular's documentation for ng-submit I found out this:
Additionally it prevents the default action (which for form means sending the request to the server and reloading the current page), but only if the form does not contain action, data-action, or x-action attributes.
Looking at the code it is evident that I mistakenly added an action attribute when I was defining the form element:
<form role="form" action="#" class="form-horizontal" ng-submit="saveUser()">
Removing it will fix the problem:
<form role="form" class="form-horizontal" ng-submit="saveUser()">
you can add $event.preventDefault(); before saveUser(); it will works.

AngularJS ng-include on ng-click

I would like to find a way to insert HTML (which is optimalized for the controller) into alert div. However I couldn't find a way to do it...
<script type="text/ng-include" id="login.html">
<form data-select="exeption" class="loginBox shadowed" onclick="event.stopPropagation();" novalidate name="login">
<h2>Login alert</h2>
<!--inputs and such, they need to be controlled by the controller-->
</form>
</script>
<script type="text/ng-include" id="bug.html">
<form data-select="exeption" class="bugBox shadowed" onclick="event.stopPropagation();" novalidate name="report">
<h2>Bug report</h2>
<!--inputs and such, they need to be controlled by the controller-->
</form>
</script>
This two templates should be evoked by the JS itself or by user. Those templates should get into this div, but I can't use innerHTML since in templates are some ng-models and such things...
<div id="alert" data-ng-click="empty()" data-ng-controller="alert" role="navigation"></div>
Usually what I do is use ng-if / ng-show .
I'm not sure I understood your request correctly, so I'll write a little example; let's say you have a simple login form:
<form>
<label>
username:
<input name="username" type="text" ng-model="username"/>
</label>
<label>
password:
<input name="password" type="password" ng-model="password"/>
</label>
<button type="submit" ng-click="login()">login</button>
<div class="message" ng-if="message">
</div>
</form>
Inside the controller:
$scope.username = '';
$scope.password = '';
$scope.message = '';
$scope.login = function() {
// login example function with ajax request and success/error promises
myLogin($scope.username, $scope.password)
.success(function() {
$scope.message = 'Logged in!';
})
.error(function(errorMessage) {
$scope.message = errorMessage;
})
}
This way your div is empty when $scope.message is empty and you can show it automatically just giving $scope.message a value.
If you need to have an ng-include, simplest thing you could do is to use it inside a div that you show when you need it:
<div ng-if="showMessage">
<div ng-include="template.html"/>
</div>
UPDATE: following my last example, if you wanted to include a different type of message for every situation, you could use multiple ngIf, including different template; example:
<div ng-if="showMessage">
<div ng-if="message.type == 'alert'" ng-include="'alert.html'"/>
<div ng-if="message.type == 'info'" ng-include="'info.html'"/>
<div ng-if="message.type == 'warning'" ng-include="'warning.html'"/>
<div ng-if="message.type == 'error'" ng-include="'error.html'"/>
</div>
This way you can also do an ngInclude for a login form, or another kind of popup.
UPDATE 2: same last example, but with another solution:
<div ng-if="showMessage">
<div ng-include="templatePath"/>
</div>
then you can give in the controller the whole path to the partial:
$scope.templatePath = 'alert.html';

Angular form submitting twice (ajax get request)

I have a controller that for some reason is submitting a form twice via a get request in AngularJs. I can see in my database that the form is being submitted twice, and also in the console network tab, it is logging the two submission, however, the first submission has the "Request Method" of OPTIONS, and the 2nd is GET. I think this may be a clue. I'm a bit confused, because i'm not passing in any 'options' into the get method, just the URL I am submitting to.
Html:
<div class="row">
<div ng-controller="groupEditCtrl">
<form class="span11" name="" novalidate ng-submit="createArtifact()">
<legend>Create a new group</legend>
<div class="row">
<div class="span5">
<div class="control-group">
<div class="controls">
<input name="text" type="text" placeholder="Group Name" required ng-model="artifact.group_name" />
</div>
</div>
</div>
<div class="span5">
<p>
<small>What your artifact will look like:</small><br />
{{artifact.group_name}}
</p>
</div>
</div>
<input name="token" type="hidden" required ng-model="window.token" />
<div class="control-group">
<div class="controls controls-row">
<button type="submit" class="btn" value="Submit" title="Submit">
<span>Submit</span>
</button>
</div>
</div>
</form>
</div>
</div>
Controller:
'use strict';
function groupEditCtrl($scope, $http, $routeParams, $cookie) {
$scope.createArtifact = function(){
var requestURL = window.base_url + "/Group/CreateGroup?callback=JSON_CALLBACK&token=" + window.token + "&group_name=" + $scope.artifact.group_name;
$http.get( requestURL ).
success(function(data, status, headers, config) {
console.log('You have successfully submitted a Cause/CreateCause');
}).
error(function(data,status,headers,config){
console.log('You have FAILED submitting a Cause/CreateCause');
});
}
};
A HTTP Options request is asking the server for the allowed methods that it can communicate with the server upon (amongst other things) so it's a normal thing to happen.
The Options request shouldn't change anything in your backend but it may well be logged as you're seeing. Double check that it isn't changing anything (and if it is then your backend may be configured wrong, and you should ask another question if it is!).
There's nothing wrong with your angular setup/use regarding the OPTIONS then GET.
For me headers: {'Content-Type': undefined} Work, And I don't see it call twice again.
$http.post("index.php", {id : $scope.A},{
headers: {'Content-Type': undefined}})
.success(function (response) {
$scope.OK = response.POLL;
}
}
Turns out this was an issue with the custom api being built that I wasn't aware of.

Categories