Running Angular Script on page Load - javascript

I have trouble with calling a function when the page loads. I'm not sure what tag I should use within html to run Angular application.
I am trying to pull user data from a database and display it in a table. It works when I use a button to call the function but I would like it to be more automated. I've done some research and it always leads me to using the controller but I am sure there has to be a simpler solution.
<tbody>
<Button ng-click="fetchEveryone();">click</Button>
<tr ng-repeat="user in all_users">
<td>
<img src="/images/{{user.pic}}" style="height: 50px; width: 50px; border-radius: 100px;"/>
</td>
<td>{{user.name}}</td>
<td>{{user.email}}</td>
<td>
<select ng-change="" ng-model="selectedMeeting">
<option value="">Select Meeting Type...</option>
<option ng-repeat="meeting in meetings">{{meeting}}</option>
</select>
</td>
<td>
<button>request</button>
</td>
</tr>
</tbody>
Here is the Angular code. It makes a request to python server.
$scope.fetchEveryone = function(){
var req = {
verb: 'getPeople',
names: $scope.Allusers
}
$scope.callAPI(req, function(response){
$scope.all_users = response;
$scope.view = 'viewPerson'
});
}

You can call it using ng-init as haakon319 suggested in this post. Otherwise, you can call it in your controller after the function definition and it will run when the controller loads:
function myController($scope){
$scope.callAPI = function(req, callback){
//some function
};
$scope.fetchEveryone = function(){
var req = {
verb: 'getPeople',
names: $scope.Allusers
}
$scope.callAPI(req, function(response){
$scope.all_users = response;
$scope.view = 'viewPerson'
});
};
$scope.fetchEveryone();
}
If you have more than one thing that needs to happen, better practice might be to have a dedicated init function to call all needed functions:
function myController($scope){
$scope.callAPI = function(req, callback){
//some function
};
$scope.fetchEveryone = function(){
var req = {
verb: 'getPeople',
names: $scope.Allusers
}
$scope.callAPI(req, function(response){
$scope.all_users = response;
$scope.view = 'viewPerson'
});
};
function moreBackendCalls(){
//more backend calls
};
function init(){
$scope.fetchEveryone();
moreBackendCalls();
//init some variables
$scope.test1 = 'new test';
$scope.test2 = 73;
}
init();
}
Alternatively, you can add the init to scope with:
$scope.init = function(){
.....
}
and add to your HTML in the following way:
<tbody ng-init="init()">
.......
</tbody>
It will then run when the route with that html is loaded.

Related

Execute method in one of ng-repeat child

I'm using angular's ng-repeat to populate table with data from GitHub api and I want one of the <td> to execute method that will return data. the problem is that this makes the function execute infinitely.
HTML:
<table>
<thead>
<tr>
<th>Name</th>
<th>Languages</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="repo in repos">
<td>{{repo.name}}</td>
<td></td> <!-- Get languages for each repo here. -->
</tr>
</tbody>
</table>
Angular:
(function(){
var app = angular.module("githubViewer", []);
var MainController = function($scope, $http){
var onUserComplete = function(response){
$scope.user = response.data;
$http.get($scope.user.repos_url)
.then(onRepoComplete, onError);
};
var onRepoComplete = function(response){
$scope.repos = response.data;
};
var onError = function(reson){
$scope.error = "Could not fetch the data";
};
//search for user
$scope.search = function(username){
$http.get("https://api.github.com/users/" + username)
.then(onUserComplete, onError);
};
// These two execute infinately if executed in the ng-repeat's <td>
$scope.findLangs = function(langsUrl){
$http.get(langsUrl)
.then(onLangComplete, onError);
}
var onLangComplete = function(response){
return response.data;
};
};
app.controller("MainController", ["$scope", "$http", MainController]);
})();
I've tried using {{ findLangs(repo.languages_url) }} in the <td> but it causes me to get this error:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
and appears to be infinitely executing.
I don't think you can use an expression there because you don't actually return a value from the findLangs() function. That said, I don't think that would cause an infinite loop. I think you'll need to actually grab the language data for each repo within the onRepoComplete callback, then just use that data in your template:
var onRepoComplete = function(response){
$scope.repos = response.data;
$scope.repos.forEach(function(repo) {
$http.get(repo.languages_url)
.then(function(response) {
// change this to process the language data however you need to...
repo.languages = JSON.stringify(response.data);
},onError);
});
};
Then in your template you can use the languages property of the repo:
<tr ng-repeat="repo in repos">
<td>{{repo.name}}</td>
<td>{{repo.languages}}</td>
</tr>

javascript ajax not updating variable on time

I'm sure this is probably easy, but I am messing up ajax calls here. I am pretty new to javascript so I don't really know what I'm doing wrong. I've tried to look it up online and can't get my calls to work at the correct time. Any help is really appreciated.
All i am trying to do is get NHL player data from a json to a table i created using angularjs. Right now the table is displayed when $scope.players is undefined, but once the ajax completes it has data. I am not displaying it at the right time, my table is always empty
RosterController related code:
(function () {
'use strict';
angular.module("DevilsFanApp")
.controller("RosterController", RosterController);
function RosterController($rootScope, $scope, RosterService) {
$scope.players = RosterService.players;
$scope.addPlayer = addPlayer;
$scope.updatePlayer = updatePlayer;
$scope.deletePlayer = deletePlayer;
$scope.selectPlayer = selectPlayer;
$scope.fetchPlayers = fetchPlayers;
function init() {
fetchPlayers(function(res){
$scope.players = RosterService.players;
console.log("Goalies Now", $scope.players);
});
}
init();
function fetchPlayers(callback) {
var players = RosterService.updatePlayers(function(res){
callback(players);
});
}
}
})();
RosterService:
function RosterService($rootScope) {
var model = {
players: [],
updatePlayers: updatePlayers,
setCurrentPlayer: setCurrentPlayer,
getCurrentPlayer: getCurrentPlayer,
findPlayerByName: findPlayerByName,
findAllPlayers: findAllPlayers,
createPlayer: createPlayer,
deletePlayerById: deletePlayerById,
updatePlayer: updatePlayer,
findPlayerById: findPlayerById
};
return model;
function updatePlayers(callback){
$.ajax({
url: URL,
dataType: 'json',
type: 'GET',
}).done(function(response) {
var data = angular.fromJson(response);
for (var g = 0; g < data.goalie.length; g++) {
var player = model.findPlayerByName(data.goalie[g].name);
if (player == null) {
var newPlayer = {
_id: (new Date).getTime(),
name: data.goalie[g].name,
position: data.goalie[g].position,
height: data.goalie[g].height,
weight: data.goalie[g].weight,
birthday: data.goalie[g].birthdate,
age: 25,
birthPlace: data.goalie[g].birthplace,
number: data.goalie[g].number
};
model.players.push(newPlayer);
}
else{
callback(null)
}
}
return callback(model.players)
});
}
RosterView table code:
<tr ng-repeat="player in players">
<td>{{player.number}}</td>
<td>{{player.name}}</td>
<td>{{player.position}}</td>
<td>{{player.height}}</td>
<td>{{player.weight}}</td>
<td>{{player.birthday}}</td>
<td>{{player.age}}</td>
<td>{{player.birthPlace}}</td>
<td>
<div class="col-sm-4 btn-group">
<button ng-click="deletePlayer($index)" type="button" class="btn btn-table">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</button>
<button ng-click="selectPlayer($index)" type="button" class="btn btn-table">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</button>
</div>
</td>
</tr>
They way you're trying to implement it seems too much of the "JQuery way", which is very different from how angular works. First of all, avoid callbacks, use promises instead. Also, if it is an option, use $http or restangular instead.
An example of the service following my suggestions would be like this (the example is only for the fetchPlayers funcionality):
angular.module('myModule', [])
.service('playersService', ['$http', function($http){
this.fetchPlayers = function(){
return $http.get(url);
}
}])
.controller('playerCtrl', ['$scope', 'playersService', function($scope, playersService){
$scope.players = []; //Start as an empty array
this.fetchPlayers = function(){
playersService.fetchPlayers.then(function(players){
//here you can process the data as you need
$scope.players = players; //assign to a property in scope so template can see it
});
};
this.fetchPlayers(); //Invoke fetch on load
}])
Here you can find a controller in a project that performs CRUD operations with $http and handles the response to show them in a table, and here is the implementation of the service to perform the calls to the backend API.

$watch() isn't updating the $scope

I have the following controller, and when I call $scope.remove() it makes a request to the usercart, which makes a request to the api. The api returns json object which has an object with an array of cart items.
The html on the page uses an ng-repeat to loop through the items, but the page isn't updating for some reason, and I can not figure out why.
// Main controller
app.controller('Checkout', function($scope, usercart){
$scope.cart = [];
$scope.$watch(function(){
return usercart.cart;
}, function(newVal, oldVal){
if(newVal !== oldVal){
$scope.cart = newVal;
}
}, true);
$scope.remove = function(domain){
usercart.remove(domain);
};
});
This service makes a request to the api and saves the cart data.
// User cart service
app.service('usercart', function(cart){
this.remove = function(domain){
// cart is an api service to make http requests
cart.removeDomain(domain).success(function(data){
this.cart = data.cart;
});
};
});
Here is a json response example:
{
"message":"Success",
"cart":[{
"domain":"asdfsadfsadf.org",
"years":2,
"amount":9
},{
"domain":"asdsmembers.cc",
"years":2,
"amount":24.95
},{
"domain":"asdsmembers.tv",
"years":2,
"amount":39.95
}]
}
Here is the html:
<tr ng-repeat="i in cart">
<td data-th="Product">
{{i.domain}}
</td>
<td data-th="Price">${{i.amount|number:2}}</td>
<td data-th="Quantity">
<select ng-model="i.years" ng-options="y.value as y.name for y in selYears" ng-disable="isInCart(i.domain)" ng-class="{disabled: isInCart(i.domain)}" ng-change="update(i.domain, 'years', i.years)"></select>
</td>
<td class="actions" data-th="" align="center">
<button class="btn btn-default btn-sm" style="background: #333;" ng-click="remove(i.domain)"><span class="glyphicon glyphicon-remove-circle" aria-hidden="true" style="color:#fff;"></span></button>
</td>
<td data-th="Subtotal" class="text-center">${{i.years * i.amount|number:2}}</td>
</tr>
Also when the page loads the table displays fine. It is just when I run the remove function.
I haven't tried but i believe here is the problem
cart.removeDomain(domain).success(function(data){
this.cart = data.cart;
});
Since this is a pointer to the caller of the callback function you will create cart property onto your cart api service. In order to circumvent this issue you should create variable called (by convention) self and assign this to it (at the begining of the usercart service):
var self = this;
after that, change your code into this:
cart.removeDomain(domain).success(function(data){
self.cart = data.cart;
});
To get better understanding you can go through this post
Watching your local $scope for a value you change in your singleton usercart definitely wouldn't work, unless you explicitely passed in that local scope. We can simplify this by ridding the $watch and resolving a promise we can return from our service instead. This allows for generic re-use and alleviates watchers from polluting our controllers. Observe the following changes...
app.service('usercart', function(cart) {
this.remove = function(domain) {
return cart.removeDomain(domain) // return promise
};
});
app.controller('Checkout', function($scope, usercart) {
$scope.remove = function(domain) {
usercart.remove(domain).then(function(data) { // resolve promise
$scope.cart = data.cart;
});
};
});

AngularJS send updated data to php page via $http.post

I have just added the functionality to edit a table cell in my angularJS app. What I would like to do now is have the changes reflected in the database by sending the updated data to my PHP script, I'm a little stuck on how to actually resend the updated table.
My Angular Table in question:
<tr ng-repeat="c in resultValue=(data | filter:{'type':typeFilter} | filter:dateFilter | filter:proFilter | filter:cusFilter | filter:taskFilter | filter:nameFilter)">
<td class="jid" ng-hide="viewField">{{c.journal_id}}</td>
<td ng-show="modifyField"><input type="text" class="in1" ng-model="c.journal_id" /></td>
<td class="wda" ng-hide="viewField">{{c.work_date}}</td>
<td ng-show="modifyField"><input type="text" class="in1" ng-model="c.work_date" /></td>
</tr>
<button ng-hide="viewField" ng-click="modify(c)">Modify</button>
<button ng-show="modifyField" ng-click="update(c)">Update</button>
The controller thanks to a SO answer for the edit part:
journal.controller('dbCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.loadData = function () {
$http.get("http://localhost/slick/journalFetch.php").success(function(data){
$scope.data = data;
}).error(function() {
$scope.data = "error in fetching data";
});
}
$scope.modify = function(c){
$scope.modifyField = true;
$scope.viewField = true;
};
$scope.update = function(c){
$scope.modifyField = false;
$scope.viewField = false;
//AM I ABLE TO RESEND THE UPDATED (c) DATA HERE TO THE DATABASE ?
$http({
method: "post",
url: "update.php",
data: {
//if so how to retrieve updated c data here?
}
});
};
$scope.loadData();
}]);
Looks like you are trying to update the entire table with a single Update button click. Your current code is trying to access c outside the tr element which makes c out of scope for the button.
Try passing data variable to the update function.

Knockout.js 2.2.1 can't find observable array

Not sure what's going wrong here, but KnockoutJS is having some issues finding my observable array that's inside my MasterViewModel. Using 2.2.1 with jQuery 1.8.x as well as not my first KJS app. Here it is:
Initialize
$(function() {
window.vm = new MasterViewModel();
ko.applyBindings(vm);
});
ViewModel
function MasterViewModel(data) {
var self = this;
self.currentAppView = ko.observable();
// Users
self.userList = ko.observableArray([]);
self.templateListGetter = ko.computed(function() {
$.getJSON("/user/list"), function(data) {
var mapped = $.map(data, function(item) { return new userModel(item) });
self.userList(mapped);
};
});
self.goToAppView = function(appView) {
location.hash = '!/' + appView;
};
Sammy(function() {
this.get('#!/:appView', function() {
self.currentAppView(this.params.appView);
$('.appview').hide();
ko.applyBindings(new window[this.params.appView+'VM']());
});
this.notFound = function(){
location.hash = "!/dashboard";
}
//this.raise_errors = true;
}).run();
}
The View
<table class="table table-bordered table-striped">
<tbody data-bind="foreach: userList">
<tr>
<td data-bind="text: guid"></td>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
<td data-bind="text: email"></td>
<td data-bind="text: updated"></td>
<td data-bind="text: suspended"></td>
</tr>
</tbody>
</table>
I have a simple table that I am loading
Even after double-checking a couple things like adding defer="defer" to my JS tag and ensuring the userList exists, it simply cannot find the observableArray. It gives the error:
Message: ReferenceError: userList is not defined;
Bindings value: foreach: userList Error {}
Anyone have any idea what's going on?
Update
For those wondering what gets called every time the hash changes:
function usersVM() {
// Data
var self = this;
// Behaviours
$('#users').show();
}
It looks like you're initializing knockout with an undefined viewmodel?
ko.applyBindings(new window[this.params.appView+'VM']());, yet your actual viewmodel is window.vm. Case sensitivity ftw. Also, the viewmodel on window is already created / initialized. So you don't need the new operator.
So, change the applyBindings line to be
ko.applyBindings(window[this.params.appView+'vm']());
Updated Answer: By Poster
There was no necessity to keep running ko.applyBindings every time the route changed since it was already applying bindings on page load. So Sammy.js was changed to:
Sammy(function() {
this.get('#!/:appView', function() {
self.currentAppView(this.params.appView);
$('.appview').hide();
window[this.params.appView+'Route']();
});
this.notFound = function(){
location.hash = "!/dashboard";
}
//this.raise_errors = true;
}).run();
It does look like ko.computed or a regular function call to window.vm.getUserList() isn't running properly, but this will be saved for a different question.
function usersRoute() {
// Data
var self = this;
// Behaviours
$('#users').show();
window.vm.getUserList();
}

Categories

Resources