Problems with binding in Angular JS - angularjs

I am using Angular-JS with .net web api.
the program will display list of "Profiles", and when a user click on any it will navigate to the details.
The list part works fine, but the details doesn't display any data
the code is as follows:
var profileModule = angular.module('profileModule', ['ngResource', 'ngRoute']);
profileModule.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/profile', {
controller: 'profileController',
templateUrl: 'Partials/profileList.html'
})
.when('/profile/:profileId', {
templateUrl: 'Partials/profileDetail.html',
controller: 'profileDetailController'
})
.otherwise({ redirectTo: '/profile' });
}]);
profileModule.controller('profileController', function($scope, profileFactory) {
$scope.profiles = [];
function init() {
$scope.profiles = profileFactory.query();
}
init();
});
profileModule.controller('profileDetailController', function ($scope, $routeParams, profileFactory) {
$scope.profile = {};
function init() {
$scope.profile = profileFactory.get({ Id: $routeParams.profileId });
}
init();
});
profileModule.service('profileFactory', function ($resource) {
return $resource("api/profiles/:Id");
});
and the detailed page profileDetail.html looks like this
<div>
<h2>profile Details</h2>
<p>
Name: <br/>
{{profile.Name}}
</p>
{{doit}}
</div>
the list page profileList.html looks like this
<div class="container">
<h1>Profiles by sort order</h1>
<span data-ng-repeat="profile in profiles | orderBy: 'Sequence'">
<a data-ng-href="#/profile/{{profile.Id}}">
{{profile.Name}}
</a><br/>
</span>
</div>
The list part works fine, and when I click on the link, I can see the rest call to the web api as follows
/profiles/4
and I can see the JSON that is returning back
but no binding at all is happening on the detail page

get() function of $resource service can be used when your GET request to the resource returns just one object. Check your browser's developer tools, you might see an error in the console saying: "Error in resource configuration. Expected response to contain an object but got an array".
To make your code work, modify your init() function to use query instead of get:
function init() {
$scope.profile = profileFactory.query({ Id: $routeParams.profileId });
}
As far as I know, this should resolve your issue. If it doesn't, put a comment here.

Related

AngularJs, Passing optional parameters to the URL based on user input

I am trying to consume my ASP.NET Web API using AngularJs. The problem is that i want to pass optional parameters to the url based on the user input(2 Html Text Boxes) but i don't know how.
This is my ASP.NET Web API Controller
[Route("api/JobShow/{keyword}/{location}")]
public class JobShowController : ApiController
{
[HttpGet]
public PageResult<sp_JobSearch_Result> Get(ODataQueryOptions<sp_JobSearch_Result> options, string keyword = null, string location = null)
{
ODataQuerySettings settings = new ODataQuerySettings()
{
PageSize = 20
};
JobWindow obj = new JobWindow();
IQueryable results = options.ApplyTo(obj.showJobs(keyword, location).AsQueryable(), settings);
return new PageResult<sp_JobSearch_Result>(
results as IEnumerable<sp_JobSearch_Result>,
Request.GetNextPageLink(),
Request.GetInlineCount());
}
}
And this is my AngularJS controller
angular.module('JobSearch.SiteController', []).controller('JobSearchCtrl', ['$scope', '$http', function ($scope, $http) {
$http.get('/api/JobShow').success(function (data) {
$scope.model = data;
});
}]);
Example of the url then would be .../api/JobShow/Java/Toronto. Thank you all.
You can try ngResource !
You first need to include ng-resource
<script src="angular.js">
<script src="angular-resource.js">
You can get it via Bower or CDN, or whichever way you got AngularJS.
HTML:
<body ng-app="MyApp">
<div ng-controller="MyCtrl">
<label>Keyword: <input type="text" ng-model="keyword" /></label>
<label>Location: <input type="text" ng-model="location" /></label>
<button ng-click="getJobShowPage(keyword, location)">Search</button>
</div>
</body>
Controller:
angular
.module('MyApp', ['ngResource']) // Include the ngResource module here
.controller('MyCtrl', ['$scope', '$resource', function($scope, $resource){
// Create the $resource
var JobShowPage = $resource('/api/JobShow/:keyword/:location', {keyword: "#keyword", location: "#location"})
// Make a scope function to use the resource properly
$scope.getJobShowPage = function(keyword, location) {
var parameters = {};
if (keyword) {
parameters["keyword"] = keyword;
if (location) {
parameters["location"] = location;
}
}
return JobShowPage.get(parameters);
};
}]);
Input/Outputs:
When the user enters nothing and clicks 'Search', the HTTP request would be /api/JobShow
If only the keyword is entered, the HTTP request would be /api/JobShow/{{keyword}}
If both the keyword and location is entered, the HTTP request would be /api/JobShow/{{keyword}}/{{location}}
If only the location is entered (no keyword), the HTTP request would be the vanilla one /api/JobShow
You can consume the return value of the $resource query like a promise:
JobShowPage.get(parameters).$promise.then(function(response){
// Do Stuff
$scope.model = response.data;
});
by callbacks:
JobShowPage.get(parameters, function(err, response){
// Do Stuff
$scope.model = response.data;
});
Or auto unwrap it:
// This works, but it's asynchronous
// Useful if consuming directly from the Angular Template
$scope.model = JobShowPage.get(parameters);
Based on your code, I'm going to assume you have 2 textboxes and a search button, and when the search button is pressed, you want to call your GET endpoint. For this scenario, what you'll want to do is bind the textbox inputs to your scope and bind the search button using ng-click to a function in your scope that will call your endpoint. It might look something like this:
controller
angular.module('JobSearch.SiteController', [])
.controller('JobSearchCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.getResults= getResults;
function getResults() {
$http.get('/api/JobShow/' + $scope.keyword + '/' + $scope.location).success(function (data) {
$scope.model = data;
});
}
}]);
html
<div ng-controller="JobSearchCtrl">
<input type="text" ng-model="keyword">
<input type="text" ng-model="location">
<button type="button" ng-click="getResults()">Search</button>
</div>

Partial view doesn't refresh after $scope change

I have a classic scenario: filters on the left side, list of products on the right. User clicks on a filter, list of products refreshes.
I'm using 2 partial views, both views share the same controller.
<div class="row">
<div ng-include="'/views/filters.html'"></div>
<div ng-include="'/views/products.html'"></div>
</div>
The controller makes calls to a nodejs backend that talks to elasticsearch.
.controller('AppCtrl', ['$scope', '$http', '$timeout', function($scope, $http, $timeout){
$http.get('/getAll')
.success(function(data) {
$scope.products = data.hits.hits;
})
.error(function(data) {
console.log('Error: ' + data);
});
$scope.filterClick = function($event, filterClicked) {
$http.get('/getSingleFilter', {
params: {
filter: filterClicked
}
})
.success(function(data) {
$timeout(function(){
$scope.products = data;
console.log($scope.products);
});
})
.error(function(data) {
console.log('Error: ' + data);
});
}
}]);
The call to getAll works fine, products.html displays all products.
getSingleFilter doesn't refresh the list though. I do see the correct list printed in the console, but the view doesn't refresh.
What am I missing?
Any specific reason why you are using $timeout? Judging by the code you provided, you dont need to use it. $digest cycle will be triggered by $http, and your model should be successfully updated as with your $http.get('/getAll') example
.success(function(data) {
$scope.products = data;
})
Ok, I found the problem. I put the 2 partials in the same partial, and put the code inside a
<div ng-controller='AppCtrl'>
and it worked.
Before I had the ng-controller='AppCtrl' directive show up in both partials and I think that was the issue. I may just leave it like this now, or create 2 controllers and then have a service to share the data.

How to open link in new window using angularjs

I am developing system using angularjs with codeigniter.
What I want to do:
There is anchor tag [edit] for every user (user list is shown using ng-repeat)
on click of edit i want to open new window.Opening new window is not an issue. I want to pass user_id to that new widow. So issue is: passing user_id.
When I go to new window, after edit-update done, (edit update is not an issue), I want to refresh the current application (previous widow:from where I switched).
Hope you got my issue(s).
Sample Code:
<div ng-repeat="user in allUsers">
Only displaying : {{user.user_id}}, It is OK.
<a title="Edit in new window" href='javascript:window.open(\"".base_url()."phpcontroller/loadingview/user_id \",\"User Edit\", \"width=\"+screen.width+\",height=\"+screen.height+\",fullscreen=yes,location=no\");' >Test edit</a>
</div>
This HTML/php PAGE is loaded through angularjs. So it is partial, thats why I cant use php functionality(eg. base_url(), php variable) here. How can I give basepath in partial. Is there any way to declare base url globally in app.js or controllers.js, so that I can use it in partials?
Please try to give suggestions, solutions. If you not get my issues clearly, please comment. Thanks.
UPDATE : Question is not full duplicate of any question on stackoverflow.
if you wanted to do it in an angular way you could do something like this
angular.module('myApp', [])
.controller('myController', ['$scope', '$window', function($scope,$window) {
$scope.openWindow = function(greeting) {
$window.open("http://www.google.com", "", "width=640, height=480");
};
}]);
HTML Code
<div ng-controller = "myController">
<a title="Edit in new window" ng-click="openWindow() >Test edit</a>
</div>
In angular js you can do something like,
$scope.newWindow = function(value) {
$window.open("http://your_url/routing_path/?key=value", "");
};
where routing_path will be ur routing url and value is the part where you actually send your value.
In your script you can have like
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope, $location, $http, $timeout) {
$rootScope.$on('$routeChangeStart', function(event, next) {
if (next.originalPath === 'your_routing_url') {
var value = next.params.key;
}
});
});
Also you can use Web Storage object like,
var myApp = angular.module('myApp', []);
myApp.factory('anyService', function($http, $location) {
return {
set: function(key,value) {
return localStorage.setItem(key,value);
},
get: function(key) {
return localStorage.getItem(key);
},
destroy: function(key) {
return localStorage.removeItem(key);
}
};
});
inject the service any where and get the data.

Restangular response and ng-repeat

I have recently started to learn angularjs using restangular to talk to my restfull API (sails). The problem I have stumbled upon is that the ng-repeat does not update after I change the list in the scope.
Controller:
app.controller('UsersCtrl', ['UsersSvc', '$scope', function(UsersSvc, s) {
UsersSvc.getList().then(function (new_users) {
s.users = new_users;
})
s.destroy = function (user) {
user.remove().then(function () {
s.users = _.without(s.users, user);
});
}
}]);
Service:
app.factory('UsersSvc', function(Restangular) {
return Restangular.all('users');
});
Template:
<div ng-controller="UsersCtrl">
...
<tr ng-repeat"user in users">
<td>{{user.firstName}}</td>
<td>{{user.lastName}} </td>
<td>{{user.emailAddress}}</td>
<td>{{user.age}}</td>
</tr>
...
</div>
When I inspect the scope the array of restangular objects is correctly assigned to the scope of the users controller but the template refuses to update.
Thanks in advance
AngularJS (and javascript) care about references vs. overwrites. So to be safe I always set my scope variables initially, and then update using angular.copy() or Restangular.copy() (if it's a Restangular object being set).
Below is how I'd refactor your controller to ensure bindings + digest cycles stay connected.
(Please note I renamed s to the "traditional" $scope for easier reading for everyone else)
app.controller('UsersCtrl', ['$scope', 'UsersSvc', 'Restangular', function($scope, UsersSvc, Restangular) {
// we're expecting a list, so default as array
$scope.users = [];
UsersSvc.getList().then(function (new_users) {
// In normal $resource/ng projects use: angular.copy(src, dst) but
// Restangular has an issue when using angular.copy():
// https://github.com/mgonto/restangular/issues/55
// so use their version of copy():
Restangular.copy(new_users, $scope.users);
});
$scope.destroy = function (user) {
user.remove().then(function () {
$scope.users = _.without($scope.users, user);
});
}
}]);

session username not showing inside angularjs ngView template but does outside of ngView

So I am able to show the username via mongoose passport-local within my successful redirect:
app.get('/profile', isLoggedIn, function (req, res) {
res.render('profile.html', {
user: req.user
});
console.log(req.user.local.username) //correct username for the session
});
...and then use it to display within profile.html:
<p><%= user.local.username %></p>
...yet when trying to display the same within my ng-view:
<div ng-view ng-class="slide" class="slider"></div>
...via my route example.html template:
<p>welcome <%= user.local.username %></p>
..it is displayed as seen: not the correct username...
Is it not possible to include this information within an ngView? Is there a solution to include this info within my template? I have attempted to setup a factory with $http:
angular.module('userService', [])
.factory('Users', function($http) {
return {
get : function() {
return $http.get('/profile');
}
}
});
...but it was returning the entire html, so not a correct solution.
Any guidance is as always welcomed and appreciated, so thanks in advance!
Edit in response:
Getting the value and routing it isnt really the issue. The issue is just getting it to display correctly within the ngView.
heres my updated code:
Rules.get()
.success(function (data) {
$scope.rules = data;
console.log('Rules.get()');
console.log(data);
Users.get()
.success(function (username) {
Users.username = username;
console.log('Users.get()')
console.log(username)
});
});
...and...
angular.module('userService', [])
.factory('Users', function ($http) {
return {
get: function () {
console.log('userService')
return $http.get('/profile/user');
}
}
});
...which returns from:
app.get('/profile/user', function (req, res) {
console.log('profile/user')
console.log(req.user.local.username)
res.json(req.user.local.username);
});
This gives me the same and correct username, but alas what do I call it to get it to display in the ngView? If placed outside of the actual ngView div it displays fine.
From the above {{username}} {{rules.username}} = nothing. What should be the name of the service param within the ngView template (which I am assuming is an entirely new $scope)?
Question is possible duplicate of Angular and Express routing?
You could include ejs templating (like <%= value %>) inside ng-views (see link above), but here are three other options.
Many people pass server data to Angular and let Angular template their views (e.g. in the MEAN stack). This would mean you wouldn't need to have ejs templating in your angular partials. Here are three ways to pass data from your server to Angular (Credit to this blog post: http://mircozeiss.com/how-to-pass-javascript-variables-from-a-server-to-angular/):
1. the MEAN method
The first is what is used in the MEAN stack. You could send all of the data your Angular app needs in the wrapper around your ng-view:
<!-- head -->
<!-- body -->
<!-- ng-view element -->
<script type="text/javascript">window.user = <%- user %>;</script>
<!-- rest of js -->
<!-- /body -->
This way the data can be accessed by your Angular app via a provider like so:
angular.module('myApp')
.factory('UserData', [
function() {
return {
user: window.user
};
}
]);
2. using $http and an api
The second is providing this data via a restful api and requesting the data via a provider using $http or $resource:
angular.module('myApp')
.factory('UserData', [
function($http) {
$http.get('/userAPI').success(function(data){
return data;
});
}
]);
3. Using ng-init
Finally you could use server-side templating inside an ng-init.
<div ng-init="user = <%- data %>"></div>
This would put the data on $scope.user for your Angular app to use.
So here was my solution for anyone else that may be searching:
users.js:
angular.module('userService', [])
.factory('Users', function ($http) {
return {
get: function () {
console.log('userService')
return $http.get('/profile/user');
}
}
});
route.js
$scope.username = {};
.controller('cntrl', function ($scope, Users) {
getCurrentUser(Users, $scope);
})
function getCurrentUser(Users, $scope) {
Users.get()
.success(function (username) {
$scope.username = username.user;
});
}
service.js:
app.get('/profile/user', function (req, res) {
console.log('profile/user')
console.log(req.user.local.username)
res.jsonp({ user:req.user.local.username});
});
then within html (within any ngView template):
<span class="username">{{username}}</span>
the key being:
$scope.username = {};

Resources