Understanding Angular Promises and API Calls -- Creating User Dashboard from API - javascript

I'm trying to create a simple dashboard that mimics the functionality shown here. I'd like to stick within an angular construct, but I've successfully confused myself into oblivion.I'm pretty new to Angular in general.
I am creating a simple dashboard based off of data from the Github API. I would like to reference the following data sources (where user is the value from the search bar and repo is the value clicked on from a repo list -- see linked example in 1st paragraph):
"https://api.github.com/users/" + user ----> returns general user info
"https://api.github.com/users/" + user + "/repos" ----> used for repo list
"https://api.github.com/repos/" + user + "/" + repo + "/events" ----> list of events per repo
Essentially, the app is supposed to work the following way:
The user types in the Github username in the search bar.
An API call is made to return the user information and repo list
(the first two urls I listed)
So far, I have this working.
THEN, based on the first selected repo in the returned dropdown list OR the selected value, the 3rd url will be called to return more data.
As far as I can tell, I need to incorporate Angular promises, since my 3rd Get request is not being recognized.
Can someone help me restructure my app.js code to ensure that:
- I have a set "repo" on page render (i.e. the 1st listed repo will be the default selected)
- The events api is called again after user interaction with the repo list
I was trying to follow something as explained here but I was a little confused at how to incorporate the username and selected repo. If someone could walk me through how I could add in those parameters (specified by the user) in my code, I would really appreciate it!
Here is my current code, for reference:
app.js
angular.module('myApp', ['ui.router'])
.controller('DashboardCtrl', function($scope, $state, $http){
// Set search model to 'mbostock' and the fetch function to contact the
// remote API and ensure the view is initialized. Load results when the search box changes.
$scope.$watch('search', function() {
initialFetch();
});
$scope.search = "mbostock";
// Make calls to the API for Users and Repo List
function initialFetch(){
$http.get("https://api.github.com/users/" + $scope.search)
.then(function(response){ $scope.userinfo = response.data; });
$http.get("https://api.github.com/users/" + $scope.search + "/repos")
.then(
function(response){ $scope.repolist = response.data;
// Create call for events listing based on repo choice
var repo = "";
// console.log(document.getElementById("repo1").value);
$(function() {
//For showing default url
MakeUrl();
// On repository selection, call events
$('#repo-select').on('change', function () {
if ($(this).val() == 0) {
repo = document.getElementById("repo1").value;
} else {
repo = $(this).val();
}
MakeUrl();
return false;
});
});
function MakeUrl() {
var finalUrl = "https://api.github.com/repos/" + $scope.search + "/" + repo + "/events";
console.log(finalUrl);
$http.get(finalUrl)
.then(function (response) { $scope.eventinfo = response.data; });
}
});
}
// Function select which ensures that the entire
// text is selected when the user clicks in the text input.
$scope.select = function(){
this.setSelectionRange(0, this.value.length);
}
})
index.html
<body>
<div class="container-fluid outerdiv" ng-app="myApp" ng-controller="DashboardCtrl">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand"><b>Github User Information</b> <span class="span-style"></span></a>
</div>
<div class="input-group search-bar">
<input type="text" ng-model="search" ng-model-options="{ debounce: 800 }" onclick="select()" class="form-control" placeholder="Enter Github user login" autofocus />
<span class="input-group-addon bar-style"><i class="glyphicon glyphicon-search"></i></span>
</div>
</div>
</nav>
<noscript>
<div class="nojs">Javascript is either disabled or not supported in your browser. Please enable it or use a Javascript enabled browser.</div>
</noscript>
<div class="animated zoomInRight">
<div id="user-bio" class="col-sm-4 col-md-4">
<div>
<div class="avatar">
<img src="{{ userinfo.avatar_url }}" class="thumbnail animated flip movie-poster">
</div>
<span class="span-outer">
{{ userinfo.login }}
</span><br>{{ userinfo.name }}
<p><strong>Joined:</strong><br> {{ userinfo.created_at }}</p>
<p><strong>Last Updated:</strong><br> {{ userinfo.updated_at }}</p>
<p>{{ userinfo.bio }}</p>
<p class="outer-p">
<div class="inner-p">
<span class="label label-primary">Public Repos :</span> {{ userinfo.public_repos }}
</div>
<div class="inner-p">
<span class="label label-primary">Followers :</span> {{ userinfo.followers }}
</div>
<div class="inner-p">
<span class="label label-primary">Following :</span> {{ userinfo.following }}
</div>
</p>
</div>
<div ng-if="userinfo.message==='Not Found'">
No results found.
</div>
</div>
<div class="col-sm-8 col-md-8">
<h5><strong>Repositories:</strong></h5>
<select id="repo-select">
<option ng-repeat="repo in repolist" id="repo{{ $index + 1 }}" value="{{ repo.name }}" onchange="MakeUrl();">{{ repo.name }}</option>
</select>
<h5><strong>Events:</strong></h5>
<ul class="event-results" id="event-select" style="height:400px; overflow-y:auto;">
<li ng-repeat="event in eventinfo">
<a id="{{ $index + 1 }}" value="{{ event.type }}">{{ event.type }}
</a>, {{ event.created_at }} <!--ng-click="update(movie)"-->
</li>
</ul>
</div>
</div>
</div>
</body>
EDIT
Here is the error I'm seeing -- again, they seem to indicate I need to implement promises. Then again, I'm not sure why I can't specify a default selected repo.
Possibly unhandled rejection: {"data":{"message":"Not Found","documentation_url":"https://developer.github.com/v3"},"status":404,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","url":"https://api.github.com/repos/mbostock//events","headers":{"Accept":"application/json, text/plain, /"}},"statusText":"Not Found"}
UPDATE AND EDIT
Via #mikwat 's suggestion, I tried using ng-model to bind the repo variable.
My new app.js file looks like this:
angular.module('myApp', ['ui.router'])
.controller('DashboardCtrl', function($scope, $state, $http, DataService){
// Set search model to 'mbostock' and the fetch function to contact the
// remote API and ensure the view is initialized. Load results when the search box changes.
$scope.$watch('search', function() {
initialFetch();
// .then(MakeUrl);
});
var user = $scope.search;
$scope.search = "mbostock";
$scope.repo = "array-source";
// Make calls to the API for Users and Repo List
function initialFetch(){
$http.get("https://api.github.com/users/" + $scope.search)
.then(function(response){ $scope.userinfo = response.data; });
$http.get("https://api.github.com/users/" + $scope.search + "/repos")
.then(
function(response){ $scope.repolist = response.data; },
$http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
.then(function (response) { $scope.eventinfo = response.data; })
);
}
// Function select which ensures that the entire
// text is selected when the user clicks in the text input.
$scope.select = function(){
this.setSelectionRange(0, this.value.length);
}
});
While this is getting the data to render, I cannot figure out how to dynamically assign the 1st repo list value as my default value (I tried document.getElementById("repo1").value but I got 'undefined') AND the function does not call the API again on dropdown change.
UPDATE 5/5/2017 -- Personal Solution
Big thanks to #mikwat for all the help. I ended up using a slightly different solution than he did below, but both work.
angular.module('myApp', [])
.controller('DashboardCtrl', function($scope, $http){
// Set search model to 'mbostock' and the fetch function to contact the
// remote API and ensure the view is initialized. Load results when the search box changes.
$scope.$watch('search', function() {
initialFetch();
// .then(MakeUrl);
});
// NOTE: watch for changes to repo
$scope.$watch('repo', function() {
$http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
.then(function (response) {
$scope.eventinfo = response.data;
});
});
var user = $scope.search;
$scope.search = "mbostock";
// Make calls to the API for Users and Repo List
function initialFetch(){
$http.get("https://api.github.com/events")
.then(function(response){ $scope.publicevents = response.data; console.log(response.data);})
.catch(function (err) {
console.log(err)
});
$http.get("https://api.github.com/users/" + $scope.search)
.then(function(response){ $scope.userinfo = response.data; })
.catch(function (err) {
console.log(err)
});
$http.get("https://api.github.com/users/" + $scope.search + "/repos")
.then(
function(response){
$scope.repolist = response.data;
// NOTE: select first repo
if ($scope.repolist && $scope.repolist.length > 0) {
var repo = $scope.repolist[0].name;
} else {
console.log("Something went wrong here!");
var repo = "undefined"
}
$scope.repo = repo;
return repo
}).then(function (repo) {
$http.get("https://api.github.com/repos/" + $scope.search + "/" + repo + "/events")
.then(function (response) { $scope.eventinfo = response.data; console.log(response.data);})
return repo;
}).then(function (repo) {
$http.get("https://api.github.com/repos/" + $scope.search + "/" + repo + "/languages")
.then(function (response) { $scope.languages = response.data; console.log(response.data);})
}).catch(function (err) {
console.log("Here!" + err);
});
};
// Function select which ensures that the entire
// text is selected when the user clicks in the text input.
$scope.select = function(){
this.setSelectionRange(0, this.value.length);
}
});

Here's a working solution. I removed some of the dependencies just to get it to work in this sandbox. I used NOTE: comments to help describe the important changes.
angular.module('myApp', [])
.controller('DashboardCtrl', function($scope, $http){
// Set search model to 'mbostock' and the fetch function to contact the
// remote API and ensure the view is initialized. Load results when the search box changes.
$scope.$watch('search', function() {
initialFetch();
// .then(MakeUrl);
});
// NOTE: watch for changes to repo
$scope.$watch('repo', function() {
$http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
.then(function (response) {
$scope.eventinfo = response.data;
});
// NOTE: additional request to fetch languages
$http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/languages")
.then(function (response) {
console.log(response.data);
// TODO: display results
});
});
var user = $scope.search;
$scope.search = "mbostock";
// Make calls to the API for Users and Repo List
function initialFetch(){
$http.get("https://api.github.com/users/" + $scope.search)
.then(function(response){ $scope.userinfo = response.data; });
$http.get("https://api.github.com/users/" + $scope.search + "/repos")
.then(
function(response){
$scope.repolist = response.data;
// NOTE: select first repo
if ($scope.repolist && $scope.repolist.length > 0) {
$scope.repo = $scope.repolist[0].name;
}
},
$http.get("https://api.github.com/repos/" + $scope.search + "/" + $scope.repo + "/events")
.then(function (response) { $scope.eventinfo = response.data; })
);
}
// Function select which ensures that the entire
// text is selected when the user clicks in the text input.
$scope.select = function(){
this.setSelectionRange(0, this.value.length);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="container-fluid outerdiv" ng-app="myApp" ng-controller="DashboardCtrl">
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand"><b>Github User Information</b> <span class="span-style"></span></a>
</div>
<div class="input-group search-bar">
<input type="text" ng-model="search" ng-model-options="{ debounce: 800 }" onclick="select()" class="form-control" placeholder="Enter Github user login" autofocus />
<span class="input-group-addon bar-style"><i class="glyphicon glyphicon-search"></i></span>
</div>
</div>
</nav>
<noscript>
<div class="nojs">Javascript is either disabled or not supported in your browser. Please enable it or use a Javascript enabled browser.</div>
</noscript>
<div class="animated zoomInRight">
<div id="user-bio" class="col-sm-4 col-md-4">
<div>
<div class="avatar">
<img src="{{ userinfo.avatar_url }}" class="thumbnail animated flip movie-poster">
</div>
<span class="span-outer">
{{ userinfo.login }}
</span><br>{{ userinfo.name }}
<p><strong>Joined:</strong><br> {{ userinfo.created_at }}</p>
<p><strong>Last Updated:</strong><br> {{ userinfo.updated_at }}</p>
<p>{{ userinfo.bio }}</p>
<p class="outer-p">
<div class="inner-p">
<span class="label label-primary">Public Repos :</span> {{ userinfo.public_repos }}
</div>
<div class="inner-p">
<span class="label label-primary">Followers :</span> {{ userinfo.followers }}
</div>
<div class="inner-p">
<span class="label label-primary">Following :</span> {{ userinfo.following }}
</div>
</p>
</div>
<div ng-if="userinfo.message==='Not Found'">
No results found.
</div>
</div>
<div class="col-sm-8 col-md-8">
<h5><strong>Repositories:</strong></h5>
<!-- NOTE: use ng-model and ng-repeat and don't clobber repo variable on scope -->
<select id="repo-select" ng-model="repo">
<option ng-repeat="r in repolist" id="repo{{ $index + 1 }}" ng-value="r.name" onchange="MakeUrl();">{{ r.name }}</option>
</select>
<h5><strong>Events:</strong></h5>
<ul class="event-results" id="event-select" style="height:400px; overflow-y:auto;">
<li ng-repeat="event in eventinfo">
<a id="{{ $index + 1 }}" value="{{ event.type }}">{{ event.type }}
</a>, {{ event.created_at }} <!--ng-click="update(movie)"-->
</li>
</ul>
</div>
</div>
</div>

Related

How to display a returned json in angular view?

I am implementing a search in the github repository.
I need to display the information that i get from here: https://api.github.com/search/repositories?q=bootstrap . for instance into a view or HTML
<div ng-app="newsearchApp">
<div ng-controller="MainCtrl">
<form action="#/about" method="get">
<input ng-model="searchText" />
<button ng-click="search()">Search</button>
</form>
</div>
</div>
the code for searching the Github repository;
angular.module('newsearchApp')
.controller("MainCtrl", ["$scope", function($scope) {
$scope.searchText = "";
$scope.search = function() {
console.log($scope.searchText);
var item = $scope.searchText;
// console.log(item)
var GithubSearcher = require('github-search-api');
var github = new GithubSearcher({username: 'test#something.com', password: 'passwordHere'});
var params = {
'term': $scope.searchText
};
//i am not certain about the 'userData'
github.searchRepos(params, function(data) {
console.log(data);
$scope.userData = data; //i am not certain about the 'repoData'
});
} }]);
the problem is here, when populating the json object to HTML
<div ng-repeat="repo in userData | filter:searchText | orderBy:predicate:reverse" class="list-group-item ">
<div class="row">
<div class="col-md-8">
<h4>
<small>
<span ng-if="repo.fork" class="octicon octicon-repo-forked"></span>
<span ng-if="!repo.fork" class="octicon octicon-repo"></span>
<small>{{repo.forks_count}}</small>
</small>
<a href="{{repo.html_url}}" target="_blank" >
{{repo.name}}
</a>
<small>{{repo.description}}</small>
<small>{{repo.stargazers_count}}</small>
<a href="{{repo.open_issues_count}}" target="_blank" >
Open Issues
</a>
<small>{{}}</small>
</h4>
</div>
</div>
</div>
the results are null on the HTML but are not null on the console.
thanks in advance
the results are null
The problem is, that Angular doesn't notice that the GitHub server has answered and doesn't update the view. You have to tell Angular manually to re-render the view. Try calling $scope.$apply():
github.searchRepos(params, function(data) {
console.log(data);
$scope.userData = data;
$scope.$apply();
});
If you'd make your request to the GitHub API with Angulars $http service, then this would not be needed - you'll only need $scope.$apply() if something asynchronous happens which doesnt live in the "Angular world" - for example things like setTimeout, jQuery ajax calls, and so on. That's why there are Angular wrappers like $timeout and $http.
More details: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
The GitHub API can be accessed using the AngularJS $http service:
app.controller("myVm", function($scope,$http) {
var vm = $scope;
var url = "https://api.github.com/search/repositories?q=bootstrap"
$http.get(url).then(function onSuccess(response) {
vm.data = response.data;
console.log(vm.data);
})
})
HTML
<div ng-app="myApp" ng-controller="myVm">
<div ng-repeat="item in data.items">
{{item.full_name}}
</div>
</div>
The DEMO on JSFiddle
Since you're not using the Angular $http service, angular is not aware of the changes. You need to manually tell Angular to re-render and evaluate by using
$scope.$apply();

the form doesn't send updated input value

On my page I have bs-table. The data comes from api call. By click on row I send new api call to retrieve row data from db. Then I set scope variable for res data and render it on a page
$('#table').on('click-row.bs.table', function (e, row, $element) {
$('.success').removeClass('success');
$($element).addClass('success');
var indexId = $table.find($element).data('index');
var rowData = $table.bootstrapTable('getData')[indexId];
$scope.id = rowData.id;
$http.get(url + $scope.id)
.then(function (res) {
$scope.rowData = res.data.model;
console.log($scope.rowData);
$scope.rowKeys = Object.keys($scope.rowData);
});
$scope.$apply();
});
html:
<form style="padding: 15px" ng-submit="submitForm()">
<div class="form-group row">
<div ng-repeat="k in rowKeys | filter: '!id'" ng-model="rowValue">
<label for="rowValue" class="col-sm-2">
<!--{{k | hide:'.name'}}:-->
{{k }}:
</label>
<div class=" col-sm-2">
<input class="form-control rowValue" id="rowValue" value="{{rowData[k]}}"/>
</div>
</div>
</div>
<button type="submit" class="btn btn-default" ng-if="rowData" >Submit</button>
Then thru ng-submit I want to send back changes which have been made in that form
$scope.submitForm = function() {
$scope.$watch('rowData', function(newValue, oldValue) {
console.log('being watched oldValue:', oldValue, 'newValue:', newValue);
}, true);
$http({
method : 'PUT',
url : url + $scope.id,
data : $scope.rowData, //form
headers : {'Content-Type': 'application/json'}
})
.then(function (res) {
//console.log(res);
//console.log($scope.rowData);
return res;
});
};
As you can see I've set $watch that to follow for changes in scope variable, but the problem is the console log returns same values for oldValue and newValue. Could anybody explain me where is my mistake? I appreciate any help
You're not binding the values to anything. Instead of using value="{{rowData[k]}}" on your input element, use ng-model: ng-model="rowData[k]".
You're calling $scope.$apply(); before the request has a chance to finish. You need to put $scope.$apply(); within the then() callback.
$http.get('stuff').then(function(response){
//stuff
$scope.$apply();
});

AngularJS : Communication between directives - ng-repeat not refresh

I apologize of a mess but this is the first time on stackoverflow ;)
Link to jsfiddle
http://jsfiddle.net/1u1oujmu/19/
I have problem with communication between directives and refresh ng-repeat.
I have two pages homePage and dashboardPage - on these page I have directive when I refresh page (dashboardPage) everything is working, but when I switch on homePage and I will back to dahsboardPage my problem starts occurs.
Step reproduce:
dashboardPage - reload - add new link - list-link directive is refresh new link is on list
go to homePage
back to dashboard page
try to add new link - when link is added (on server and I receives response) I call factory to store a data:
dataFactory.editData("userLinksList", result.data);
//part of factory to edit and propagation data
editData: function(name, data){
dataArray[name] = data;
$rootScope.$broadcast(name);
},
Then in directive controller I have condition to listen propagation "userLinksList" checkRootScope this is flag for only one register listener
Problem is in line:
$scope.data.links = dataFactory.getData("userLinksList");
In $scope.data.links I receives new data but I don't know why ng-repeat is not refresh
when I go to homePage and back to dashboard new link will be on list
if(checkRootScope){
$rootScope.$on("userLinksList", function () {
$scope.data.links = dataFactory.getData("userLinksList");
});
checkRootScope = false;
}
homePage - on the page I have list-link directive:
<div class="columns marketing-grid">
<div class="col-md-6">
<list-link hp="true"></list-link>
</div>
</div>
dashboardPage - on the page I have this same directive without parameter:
<div class="row">
<div class="col-sm-12 col-md-8">
<list-link></list-link>
</div>
</div>
template of list-link:
<ul ng-if="data.links">
<li ng-repeat="link in data.links | filter: search" class="link-list-item" data-id="{{link.id}}">
<div class="row">
<div class="col-md-9">
<a ng-href="link.url"><h3>{{link.title}} <span>{{link.host}}</span></h3></a>
</div>
<div class="col-md-3 link-list-time text-right">
{{link.date | date : 'd/MM/yyyy' }}
</div>
<div class="col-md-12">
<blockquote ng-show="link.comment">{{link.comment}}</blockquote>
</div>
<div class="col-md-2">
<span class="link-list-counter all" title="Number of links">{{link.counterAll}}</span>
</div>
<div class="col-md-6 link-list-tags">
<span>tags:</span>
<ul ng-if="link.tags">
<li ng-repeat="item in link.tags">#{{item}}</li>
</ul>
</div>
<div class="col-md-4 text-right link-list-buttons">
<button class="btn btn-default btn-xs" title="Edit" ng-click="edit(link.id);">Edit <span class="glyphicon glyphicon-edit" aria-hidden="true"></span></button>
<button class="btn btn-default btn-xs" title="Delete" ng-click="delete(link.id);">Delete <span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
</div>
</div>
</li>
</ul>
Directive list-link:
app.directive("listLink", ['path', function(path){
var path = path.url(),
checkRootScope = true;
return {
restrict : "E",
scope : {
hp : "="
},
templateUrl: path.template.listlink,
replace : true,
transclude : false,
controller : ['$rootScope', '$scope','conn', 'auth', 'loaderService','stringOperation','dataFactory', function($rootScope, $scope, conn, auth, loaderService, stringOperation,dataFactory){
var dataConenction = function(){
conn.getData(path.server.link, { params : $scope.data })
.then(function(result){
if($scope.data.all == true){
dataFactory.addData("popularLinksList",result.data);
$scope.data.links = dataFactory.getData("popularLinksList");
} else{
dataFactory.addData("userLinksList",result.data);
$scope.data.links = dataFactory.getData("userLinksList");
}
}, function(msg){
console.log(msg);
});
};
$scope.hp = (typeof $scope.hp === "undefined" ? false : $scope.hp);
$scope.path = path;
$scope.userInfo = auth.getUserInfo();
$scope.data = {
auth : $scope.userInfo,
check : false,
all : $scope.hp
};
dataConenction();
if(checkRootScope){
$rootScope.$on("userLinksList", function () {
$scope.data.links = dataFactory.getData("userLinksList");
});
checkRootScope = false;
}
$scope.edit = function(id){
$rootScope.$broadcast("editLink", {"id": id});
};
$scope.delete = function(id){
var check = confirm("Are you sure you want to remove?");
if (check == true) {
conn.deleteData(path.server.link, {"params" : {auth : $scope.userInfo, id : id}})
.then(function(result){
dataFactory.editData("userLinksList",result.data.links);
$scope.data.links = dataFactory.getData("userLinksList");
dataFactory.editData("userTagsList",result.data.tags);
}, function(msg){
console.log(msg);
});
}
};
}]
}
}]);
Not sure if you already fixed it but I had a crack at it.
First the "why not working" part -
Page1 creates a new scope, lets say scope1.
Page2 creates a new scope, say scope2.
When the Page1 is clicked the data.link is set to 5 items and below code is run [scope1.data.link = 5 items] -
if(checkRootScope){
$rootScope.$on("userLinksList", function () {
$scope.data.links = dataFactory.getData("userLinksList");
});
checkRootScope = false;
}
When the Page2 is clicked, it set 7 items to dataFactory and it is broadcasted to and $rootScope.on is executed to update scope2.data.links to 7 items. However scope2.data.links is still set to 5 items. This is because when $rootScope.on is executed first time the "$scope" variable within the "on" function refers to closure scope i.e scope1 and NOT scope2. So essentially when scope.data.links is set to 7 then scope.data.links is set to 7 and scope2.data.links is still set to 5.
Basically ng-view creates a new scope and if directive is part of each of the views, you would always end up having different data.link value in each of the views.
Solution:
You can fix it in two ways:
Option 1: You would be better off setting the value in scope as soon the promise is resolved instead of setting in factory and getting from it in $on listener. Atleast in this case.
http://plnkr.co/edit/IdrsO1OT9zDqdRiaSBho?p=preview
Option 2: If broadcast is really essentially I think you would have to bind the data.link to rootscope (which might not be a good practice).
http://plnkr.co/edit/VptbSKRf7crU3qqNyF3i?p=preview
and may be there are other options...

Scope not updating changes in the model

I have an expandable form that generates an object with two attributes, a title and description. This object successfully submits to my database as a json object. I'm currently using an Angular (1.3.2) front end that interacts with Tastypie as the interface layer with my Django (1.7) backend. The problem is that I never observe updates to my home page after adding a new object to the db. I need to refresh the page for the object to appear which is not ideal.
home.html
<div class="protocol-list-container">
<div ng-app="protocolApp"
id="protocol-list">
<div class="new-protocol-container" ng-controller="protoCtrl">
<h4>Add New Protocol</h4>
<button type="button"
ng-click="toggle()"
id="id_new">
<span class="glyphicon glyphicon-plus"></span>
</button>
<div ng-hide="visible" class="protocol-new">
<form name="newProtocolForm" novalidate>
<input type="text"
id="id_new_title"
placeholder="Title"
ng-model="protocol.title"
required /><br>
<input type="text"
id="id_new_desc"
placeholder="Description"
ng-model="protocol.description"
required /><br><br>
<input type="submit"
id="id_submit_new_protocol"
value="New Protocol"
ng-click="submit(protocol)"
ng-disabled="newProtocolForm.$invalid">
</form>
{% verbatim %}
<pre>form = {{ protocol | json}}</pre>
{% endverbatim %}
</div>
<div class="protocol">
<h4>My Protocols</h4>
<li ng-repeat="protocol in protocols">
{% verbatim %}
<div><span ng-bind="protocol.title"></span></div>
{% endverbatim %}
<div> - <span ng-bind="protocol.description"></span>
</li>
<br>
</div>
</div>
</div>
app.js
angular.module('protocolApp', [])
.factory('protocolFactory', ['$http', function($http) {
var urlBase = '/api/v1/protocol/';
var protocolFactory = {};
protocolFactory.getProtocols = function() {
console.log('getProtocols called');
return $http.get(urlBase);
};
protocolFactory.addProtocol = function(protocol) {
console.log('addProtocol called');
return $http.post(urlBase, protocol);
};
return protocolFactory;
}])
.controller('protoCtrl', ['$scope', 'protocolFactory',
function ($scope, protocolFactory) {
$scope.visible = true;
var self = this;
getProtocols();
function getProtocols() {
protocolFactory.getProtocols()
.success(function(data) {
$scope.protocols = data;
})
.error(function(error) {
console.log('error retrieving protocols');
});
}
$scope.toggle = function() {
$scope.visible = !$scope.visible;
var self = this;
var protocol = {};
self.submit = function() {
var protocol = {title: self.title, description: self.description};
console.log('clicked submit with ', self.protocol);
protocolFactory.addProtocol(self.protocol)
.success(function(response) {
console.log('protocol added');
$scope.protocol = null;
})
.error(function(error) {
console.log('post to api failed');
});
// gives the behavior I want, but ultimately crashes chrome
// $scope.$watch('protocols', function(newVal, oldVal) {
// protocolFactory.getProtocols()
// .success(function(data) {
// $scope.protocols = data;
// console.log('watcher data', data);
// });
// }, true);
};
};
}]);
I've done some testing with a $scope.$watch function (commented out), but this either shows the new object and never stops (true removed) or does not update (but tells me that there is an extra object in the data based on the console statement) (true present).
Any help would be appreciated.
When the database gets updated, how does the front end know that it should get the latest data unless we tell it to ? You don't have some kind of sockets between the server and front end, looking for events and making the front end to get the latest data...
So, When you post the data to backend and database got updated, make a call to getProtocols(), in the success callback of submit.
In your case of using $watch(), you are repeatedly getting the protocols from backend, which updated the scope variable, which again fired the callback repeatedly and browser crashed.

Logic problems with AngularJS and multiple $http queries

I have an app that will make 2 $http queries to an external API and get 2 different JSON responses. These responses will populate a ng-repeat, headers, etc.
My problem is that I want to include a 3rd query, dependent on the first two.
Like so:
I get artist JSON and release JSON, and I use artist.name and release.title to populate the URL of the third $http query.
So far I've managed to get the two first queries, and once the results they are displaying in the ng-repeat, with ng-click I launch the 3rd query and populate an img ng-src.
Buuut, my problem is that I want the img ng-src to be populated automatically without ng-click, so the function that triggers the 3rd query has to get launched right after the 2 first queries. And also, in my working version right now, the img that I fetch with ng-click, will populate all items in ng-repeat. Meaning that every item should get their own image, and right now they don't.
I've created a working Plunker, if you search for a music artist and click on a result and then on an album, you'll see what I mean.
Basically, I think I'm missing a piece of logic that will put everything together and in proper trigger order.
Any thoughts?
My JS:
angular.module('myApp', ['ngResource'])
function Ctrl($scope, $http) {
var search = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=5').
success(function(data3) {
$scope.clicked = false;
$scope.results = data3.results;
});
}
$scope.reset = function () {
$scope.sliding = false;
$scope.name = undefined;
}
}
$scope.$watch('name', search, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=100').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.clicked = true;
$scope.sliding = true;
$scope.getImages = function (title, name) {
$http.get('http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=e8aefa857fc74255570c1ee62b01cdba&artist=' + name + '&album='+ title +'&format=json').
success(function(data4) {
$scope.images = data4;
});
}
}
};
My directive:
angular.module('myApp', ['ngResource'])
.directive('artistData', function() {
return{
restrict: 'E',
template: '<div class="col-md-8 col-md-offset-2"> \
<h1 ng-show="artist.name" class="artist-name">{{artist.name}}</h1> \
<div class="header-border" ng-show="artist.name"></div> \
<input ng-show="artist.name" class="form-control" ng-model="album" /> \
<div class="col-md-3" ng-click="getImages(release.title, artist.name)" ng-repeat="release in releases | filter:album | filter:{ role: \'main\' }"><div class="release">{{release.title}}<img class="img-responsive" ng-src="{{images.album.image[2][\'#text\']}}" /></div></div> \
</div>',
replace: true
};
})
And my HTML:
<div class="container">
<div class="row" ng-controller="Ctrl">
<div class="col-md-8 col-md-offset-2">
<div class="intro">
<div class="intro-text" ng-class="{'slide':sliding}">
<h1>Howdy stranger!</h1>
<h3>Use the form below to search for an artist and start building your record collection!</h3>
</div>
<input type="text" ng-model="name" class="form-control input-lg" ng-class="{'slide':sliding}" ng-focus="reset()" placeholder="Artist name"/>
</div>
<ul ng-hide="clicked" class="search-results">
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
</div>
<artist-data></artist-data>
</div>
</div>
I would use "Chaining Promises" in this case.
In basic words you call new async task on response of previous.
You can read this POST that might help you

Categories

Resources