I am doing an online tutorial where they teach you to make a simple web-app using MEAN.The code below is for editing the given collection of JSON objects(Videos are JSON objects here)
The collection is at
/api/videos
So I have to click on a href="/#/video/{{video._id}} which takes me to form.html and I have the option of editing the 'title' and 'description' parameters of the JSON object.
What I can't seem to understand is:
a)Why is this (full code below in the question) required
var Videos = $resource('/api/videos/:id', { id: '#_id' },
{
update: { method: 'PUT' }
});
Since I am on href="/#/video/{{video._id}} can't I directly take the id from the URL
var Videos=$resource('api/videos)
Videos.get({ id: $routeParams.id }, function(video){
$scope.video = video;
});
b)Whait is the workflow(i.e when is the router.get() request exactly made and when is the router.put() request made)
According to me when I click on the save button the Controller makes a put request to the API but I can't figure out when the router.get() request is being made
I am trying to read up express and angular documentations but they don't seem to explain the workflow.
Could you also please tell me what should I read up to get a better understanding?
This is the form.html code
<h1>Add a Video</h1>
<form>
<div class="form-group">
<label>Title</label>
<input class="form-control" ng-model="video.title"></input>
</div>
<div>
<label>Description</label>
<textarea class="form-control" ng-model="video.description"></textarea>
</div>
<input type="button" class="btn btn-primary" value="Save" ng-click="save()"></input>
</form>
This is the controller code
app.controller('EditVideoCtrl', ['$scope', '$resource', '$location', '$routeParams',
function($scope, $resource, $location, $routeParams){
var Videos = $resource('/api/videos/:id', { id: '#_id' },
{
update: { method: 'PUT' }
});
Videos.get({ id: $routeParams.id }, function(video){
$scope.video = video;
});
$scope.save = function(){
Videos.update($scope.video, function(){
$location.path('/');
});
}
}]);
This is the API Endpoint Code
router.get('/:id', function(req,res){
var collection =db.get('videos');
collection.findOne({_id: req.params.id},function(err,video){
if(err) throw err;
res.json(video);
});
});
router.put('/:id', function(req, res){
var collection=db.get('videos');
collection.update({_id:req.params.id},
{title: req.body.title,
description: req.body.description
},
function (err,video)
{if (err) throw err;
res.json(video);
});
});
Well, according to AngularJS docs for $resouce, $resource is:
A factory which creates a resource object that lets you interact with
RESTful server-side data sources.
In other words is a shortcut for RESTful services operations. The code bellow creates an interface with an API endpoint to make REST operations more easy to do.
Once you have this:
var User = $resource('/user/:userId', {userId:'#id'});
Is much easier to do this:
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
Because RESTful is a standard, and $resource is the Angular's implementation of the consumption of API's in this standard. On his internals, is made an assynchronous request with the propper headers and method according to the operation you conigured and choosed.
Related
I'am trying to make simple web application using TMDB api but I'm having trouble with some request.
I'am successfully connecting to its api and getting all needed data and displaying it nicely. Problem is when i load my Homepage which has poster_path of movie, i get all posters nicely and displayed but in chrome dev tools I can see there is one more extra request sent (not sent by me... at least not on purpose) and its failing and wracking my app.
Chrome displays following for bad request:
GET file:///C:/Users/Ivan/Documents/testProject/fmdb-fjume-movie-database/app/%7B%7BimageBaseUrl%7D%7D/%7B%7Bmovie.poster_path%7D%7D net::ERR_FILE_NOT_FOUND
Status: failed
Initiator: other
Here's my code for getting information and html:
Home view
<div class="well main-frame">
<h1>MY MOVIE DATABASE</h1>
<div class="row row-centered">
<div class="col-sm-2 col-centered" style="text-align:center;" ng-repeat="movie in movies | limitTo:2">
<img id="homeThumbnailImg" src="{{imageBaseUrl}}/{{movie.poster_path}}"></img>
{{movie.original_title}}
</div>
</div>
</div>
Home controller
'use strict';
angular.module('home', ['services'])
.controller('homeCtrl', ['$scope', '$q', 'api', 'globals', function($scope, $q, api, globals) {
$scope.imageBaseUrl = globals.getImageBaseUrl();
$scope.getData = function() {
$q.all([
api.discover('movie')
]).then(
function(data) {
$scope.movies = data[0].data.results;
//console.log(data[0].data.results);
},
function(reason) {
console.log(reason);
});
}
$scope.getData();
}]);
API
'use strict';
angular.module('services', [])
.constant('baseUrl', 'http://api.themoviedb.org/3/')
.constant('apiKey', 'myKey...')
.factory('api', function($http, apiKey, baseUrl) {
return {
discover: function(category) {
var url = baseUrl + 'discover/' + category + '?certification_country=US&certification.lte=G&sort_by=popularity.desc&api_key=' + apiKey;
return $http.get(url).success(function(data) {
return data;
});
},
search: function() {
},
...
Thank you all for your time!
You should change your img tag to look like this:
<img id="homeThumbnailImg" ng-src="{{imageBaseUrl}}/{{movie.poster_path}}"></img>
Note the ng-src attribute that replaces the src attribute. This will prevent the browser from trying to fetch the literal string {{imageBaseUrl}}/{{movie.poster_path}} before angular has a chance to eval that expression.
I have an HTML which looks like -
<div ng-controller="PostsCtrl">
<ul ng-repeat="post in posts" style="list-style: none;">
<li style="padding: 5px; background-color: #f5f5f5;">
<h4>
{{post.postTitle}}
</h4>
<div class="post-details" ng-show="showDetails">
<p>{{post.postContent}}</p>
</div>
</li>
</ul>
</div>
Now the data is being populated from a JSON based REST URL and being displayed. I also have a form that will be adding new post to the database-
<form data-ng-submit="submit()"
data-ng-controller="FormSubmitController">
<h3>Add Post</h3>
<p>
Title: <input type="text" data-ng-model="postTitle">
</p>
<p>
Content: <input type="text" data-ng-model="postContent">
</p>
<p>
Tags: <input name="postTags" data-ng-model="postTags" ng-list
required>
</p>
<input type="submit" id="submit" value="Submit" ng-click="loadPosts()" /><br>
</form>
I basically want to achieve two things -
1. As soon as i add new post it shows up in the list of posts above.
2. As soon as i manually add a new post in the backend, front end automatically updates.
Is it possible to achieve both using angular and if yes how will i be able to do that.
Below is my controller code, which as of now is showing me existing posts as well as letting me add new post to DB.
<script>
var app = angular.module("MyApp", []);
app.controller("PostsCtrl", function($scope, $http) {
$http.get('http://localhost:8080/MyApp/posts')
.success(function(data, status, headers, config) {
$scope.posts = data;
}).error(function(data, status, headers, config) {
console.log("Error in fetching the JSON data.");
});
$scope.$watch('posts', function(newVal, oldVal){
console.log('changed');
alert('hey, myVar has changed!');
}, true);
/*$scope.$watch('posts', function() {
alert('hey, myVar has changed!');
console.log("test log");
$scope.$digest();
});*/
});
app.controller('FormSubmitController', [ '$scope', '$http',
function($scope, $http) {
$scope.loadPosts = function() {
$http.get('http://localhost:8080/MyApp/posts')
.success(function(data, status, headers, config) {
$scope.posts = data;
alert(JSON.stringify(data));
//$scope.posts_updated = data;
}).
error(function(data, status, headers, config) {
console.log("Error in fetching the JSON data.");
});
}
$scope.list = [];
$scope.submit = function() {
var formData = {
"postTitle" : $scope.postTitle,
"postContent" : $scope.postContent,
"postTags" : $scope.postTags,
"postedBy" : "admin"
};
var response = $http.post('addPost', formData);
response.success(function(data, status, headers, config) {
console.log("na");
});
response.error(function(data, status, headers, config) {
alert("Exception details: " + JSON.stringify({
data : data
}));
});
//Empty list data after process
$scope.list = [];
};
} ]);
</script>
Any help on this will be really appreciable.
1: on your success of post, you can just push the added object into your posts list. This will trigger the two-way-binding, and the object will "automatically" appear in your ng-repeater.
$scope.posts.push(element);
2: This one is a bit tricky, since angular is a client-side application, it doesn't recognize what happens on the server-side. What you have to do to make this work is to look at websockets (like SignalR or similar) that can make a push to your client whenever something gets added. This also depends on that your "manual" insert is done using a programatically method. Doing it directly from database-changes is going to be alot more painfull
Initialize $scope.posts before invoking $http request
$scope.posts = [];
Since you are using $http service, it should automatically repaint ng-repeat when new data found. So you don't need be to worried about it
Very important thing is that you don't need to call $digest when you use $http service. Using $digest blindly is a very bad practice and is major performance issue. In the end of $http service angular automatically call $digest so you don't need to call again
I am pretty new to Node.js, Express and angularjs. I am working on a simple Sign-in functionality that will redirect to another page if sign in success. I know I can use window.location for the redirect purpose, but I am trying to use res.render because I also want to pass some values to the new page.
However, the res.render doesn't seem to work, the result page never shows up.
Signin.ejs:
<div id="navbar" class="navbar-collapse collapse" ng-controller="signinController">
<form class="navbar-form navbar-right">
<div class="form-group">
<input type="email" ng-model="inputEmail" placeholder="Email" class="form-control">
</div>
<div class="form-group">
<input type="password" ng-model="inputPassword" placeholder="Password" class="form-control">
</div>
<button type="submit" ng-click="signIn()" class="btn btn-success">Sign in</button>
</form>
</div>
The javascript embedded is:
function signinController($scope,$http,$location) {
$scope.signIn = function() {
$http({
method: 'POST',
url: '/signin',
data: { "inputEmail": $scope.inputEmail, "inputPassword": $scope.inputPassword }
});
};
}
app.js
app.get('/result', home.result);
app.post('/signin', home.afterSignIn);
The home.js
function afterSignIn(req,res)
{
// check user already exists
var sqlStr="select * from users where email='"+req.param("inputEmail")+"' and password='"+req.param("inputPassword")+"'";
console.log("Query is:"+sqlStr);
res.render('result', { result: 'sqlStr' });
}
exports.result=function(req,res){
res.render('result');
}
exports.afterSignIn = afterSignIn;
result.ejs
<%= result %>
Any suggestions or working examples are highly appreciated :)
I think you are bit confused. Use express as the REST engine when it comes to routes. Angular routes will take care of the display logic and view routing on the client side.
I would suggest you to pass JSON data to front end angular and let it do the job for you. For example:
app.get('/', function (req, res) {
res.json({
title : "n562d",
strapline : "Please Log In"
})
});
You can access the API at the endpoint: http://localhost:3000/ Use $resource services to access the express endpoint.
example:
var MyResource = $resource('/');
var myResource = new MyResource();
myResource.$get(function(result){
//result holds -->{title : "n562d", strapline : "Please Log In"}
//use $location to change the uri, which is handled by Angular route config
$location.path('/')
});
For angular routing,i would suggest you to use ui-router.
example:
function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise("/");
$stateProvider
.state('index', {
url: "/",
templateUrl: 'app/authorization/index.tpl.html',
controller: 'AuthController'
})
.state('login', {
url: "/login/",
templateUrl: 'app/authorization/login.tpl.html',
controller: 'AuthController'
})
.state('signup',{
url: "/signup/",
templateUrl : 'app/authorization/signup.tpl.html',
controller: 'AuthController'
});
}
]);
Let me know if you need more detailed answer then i will update it. I hope it helps.
Feel free to look for similar implementation here.
I'm very new to Angular and am try to make a simple "Hello World" webservice call to a simple Rest web service that I've verified returns "Hello World" when you hit it.
I have 3 alerts in the method. I see the "In method" alert and then don't see any of the other alerts. I've attached fiddler and the web service request is never made. I've got to be overlooking something basic here I would think....any ideas on what am I may be missing?
Fiddler shows that the web service call is successful and I can see the results from the Web Service I expect but using Chrome's developer tools shows me that the call to the service is being cancelled by something inside of Angular.
Thanks in advance to any help provided.
(function () {
var app = angular.module("loginApp", ['ngResource'])
.config(function ($httpProvider) {
// Enable CORS
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
});
app.controller('LoginController', function ($scope, $http) {
this.loginImage = "images/ActiveView_ContentMgm_140x40.png";
this.loginUser = function () {
var token = "";
var loginUrl = "http://ten1.com/services/rest/demoservice.svc/Login";
delete $http.defaults.headers.common['X-Requested-With'];
var result = $http({ method: 'GET', url: loginUrl, params: { userName: this.userName, passWord: this.passWord } })
.success(function (data, status) {
$scope.status = status;
$scope.data = data;
})
.error(function (data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
token = data.Token;
};
});
})();
UPDATE:
Right now I'm clicking the submit button on a login form and just attempting to get back a string so I can verify basic communication with the web service. My goal is to pass the username/password in and get a token back. Here's the form:
<form ng-submit="loginCtrl.loginUser()" novalidate>
<div class="form-group">
<label for="exampleInputEmail1">Username</label>
<input type="text" class="form-control" style="border-radius:0px" ng-model="loginCtrl.loginForm.userName" placeholder="Enter username">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password </label>
<input type="password" class="form-control" style="border-radius:0px" ng-model="loginCtrl.loginForm.passWord" placeholder="Password">
</div>
<button type="submit" class="btn btn-sm btn-default">Log in</button>
Inject $scope and $http into your controller, not in your loginUser() function:
app.controller('LoginController', function ($scope, $http) {
I found that the service I was hitting was returning a "No 'Access-Control-Allow-Origin' " message which would allow the service to complete but would cause Angular to short circuit. I fixed this by adding an "Access-Control-Allow-Origin" to the Response header in the service.
Links:
Cross Origin Resource Sharing for c# WCF Restful web service hosted as Windows service
"Access-Control-Allow-Origin:*" has no influence in REST Web Service
I'm building a simple Contact Management app with Crud using AngularJS 1.0.0rc8.
Getting a list of currently existing contacts is no problem, but while attempting to save a new Contact to the server, a new row is created - complete with the correct id, created_at, and updated_at values - but the rest of the models data is ignored.
Here is a screenshot to show what I mean:
As you can see, numbers 4 and 5 were given the Id's but first_name, last_name, and phone_num were not saved to the database.
I am using a $scope.addContact function within the Controller that deals with the object.
Here is the entire code for the Contact List Controller:
'use strict';
function ContactListCtrl($scope, $http) {
$http.get('/contacts').success(function(data) {
$scope.contacts = data;
});
$scope.addContact = function(data) {
$http.post('/contacts/', data).success(function(data) {
console.log(data);
data.first_name = $("#new_contact_first_name").val();
data.last_name = $("#new_contact_last_name").val();
});
this.newFirstName = '';
this.newLastName = '';
};
};
After clicking 'Save' on the new-contact.html partial, the Object is logged to the Console, if I inspect its contents, than sure enough the values are collected - notice Jimi Hendrix is there:
Here is the form as it appears in the new-contact.html template:
<form id="contact_form" ng-submit="addContact()">
<input type="text" id="new_contact_first_name" name="newContactFirstName" ng-model="newFirstName" placeholder="First Name"></br>
<input type="text" id="new_contact_last_name" name="newContactLastName" ng-model="newLastName" placeholder="Last Name"></br>
<input type="button" id="contact_submit_btn" value="Add Contact" class="btn btn-primary">
</form>
The addContact() function is fired after the form is submitted with JQuery:
$(document).ready(function(){
$("#contact_submit_btn").click(function(){
$("#contact_form").submit();
});
});
(Something tells me that I may not be using the ng-model attributes correctly.)
Any ideas on where I am going wrong with this? Or ideas on how I can better go about implementing this design?
Thanks.
UPDATE BELOW:
Here is my entire updated controller code - with help from Sathish:
// contacts controllers
'use strict';
function ContactListCtrl($scope, $http, Contacts) {
$scope.contacts = Contacts.index();
$scope.addContact = function() {
var newContact = {
first_name: $scope.newContactFirstName,
last_name: $scope.newContactLastName
};
var nc = new Contacts({ contact: newContact });
nc.$create(function() {
$scope.contacts.push(nc);
// now that the contact is saved, clear the form data
$scope.newContactFirstName = "";
$scope.newContactLastName = "";
})
}
};
ContactListCtrl.$inject = ['$scope', '$http', 'Contacts'];
function ContactDetailCtrl($scope, $routeParams, Contacts) {
$scope.contact = Contacts.get( {contact_id: $routeParams.contact_id} );
}
ContactDetailCtrl.$inject = ['$scope', '$routeParams', 'Contacts'];
I am now receiving the error: Unknown Provider for Contacts. Here is a screenshot of the error
Ok, I managed to fix that error by providing a ngResource to the main App file. Here's what it looks like:
// main app javascript file
'use strict';
angular.module('contactapp', ['ngResource']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/contacts', {template: 'assets/app/partials/contact-list.html', controller: ContactListCtrl}).
when('/contacts/new', {template: 'assets/app/partials/new-contact.html', controller: ContactListCtrl}).
when('/contacts/:contact_id', {template: 'assets/app/partials/contact-detail.html', controller: ContactDetailCtrl}).
otherwise({redirectTo: '/contacts'});
}]);
I am receiving a new error: WARNING: Can't verify CSRF token authenticity
Alright, managed to fix that problem too by adding a callback to the API controller:
Rails shows "WARNING: Can't verify CSRF token authenticity" from a RestKit POST
Now I am back to the original problem. When the create method is called, a new row is saved to the database, but the models data is not.
Awesome... finally got this thing working.
The problem was the $scope.addContact function. It was using the 'name' of the input instead of the ng-model binding called 'newFirstName' and 'newLastName' that resides in the template.
Here's what the updated function looks like:
$scope.addContact = function() {
var newContact = {
first_name: $scope.newFirstName,
last_name: $scope.newLastName
};
var nc = new Contacts({ contact: newContact });
nc.$create(function() {
$scope.contacts.push(nc);
// now that the contact is saved, clear the form data
$scope.newFirstName = "";
$scope.newLastName = "";
})
}
This can be better implemented using a Contacts service. Please define a Contacts service in app/assets/javascripts/services.js.erb as shown below:
var servicesModule = angular.module('<your app name>',
[<list of modules needed by this app>]);
servicesModule.factory('Contacts', function($resource) {
var ContactsService = $resource('/contacts/:contact_id', {}, {
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
});
return ContactsService;
});
Change the addContact method in the controller as shown below:
function ContactListCtrl($scope, $http, Contacts) {
...
...
...
$scope.addContact = function () {
var newContact = {
first_name: $scope.newContactFirstName,
last_name: $scope.newContactLastName
};
var nc = new Contacts({ contact: newContact });
nc.$create(function() {
$scope.contacts.push(nc);
// now that the contact is saved, clear the form data.
$scope.newContactFirstName = "";
$scope.newContactLastName = "";
});
};
...
...
...
}
ContactListCtrl.$inject = ['$scope', '$http', 'Contacts'];
In addition to this, you can simplify the $http.get(...) part also. You can use Contacts.index();
Note:
If you gave ng-app="MyAppName" then please replace <your app name> with MyAppName.
<list of modules needed by this app> needs to the replaced by a comma-separated list of strings representing any modules needed by your application.
Check the attr_accessible on your model. With new 3.2.3 rails all model attributes became protected from mass-assignment by default.