Is it possible queuing ng-init?
Generally, in first init I want to add JSON file to prototype vars (array) and in another init depending on the params I want to skip getJsonData() or add other JSON file to prototype.
function init(param) {
console.log("startInit");
// big JSON file
var promise = getJSON(param);
return promise.then( function() {
//some func
console.log("finish");
return true;
});
};
function getJSON(param) {
var deferred = $q.defer();
console.log("startInitDataInner");
someService.getJsonData(param).then(function(data) {
// some code
console.log("endInitDataInner");
deferred.resolve();
}, function(error) {
deferred.reject();
});
return deferred.promise;
};
in view ng-init
ng-init="init(param)"
ng-init="init(param)"
// ...
and log:
startInit
startInitDataInner
startInit
startInitDataInner
endInitDataInner
finish
endInitDataInner
finish
//..
Edit:
Generally, I want to create something like plugin in jQuery. I have this code:
<div ng-controller="parentController as parent">
<div ng-controller="childController as child" ng-init="child.init(parent.data)"></div>
</div>
<div ng-controller="parentController as parent">
<div ng-controller="childController as child" ng-init="child.init(parent.data2)"></div>
</div>
and configurable part by user:
angular.module('myApp').controller('parentController', ['$scope', function($scope) {
this.data = {
config: {
lang: "en",
title: "title"
}
};
this.data2 = {
config: {
lang: "pl",
title: "title2"
}
};
}]);
ng-init update api:
angular.extend(this, parent.data);
Do you have any ideas how I should do it differently?
Well, if you are working with angular, you use controllers. What is controller itslfmin general meaning? Right, its a constructor function. The main word here is function. What does function in general? Run the code inside.
So, just place your initial logic at the beggining of controller code (but without wrapping it as a separate function) and it will run just in time your controller will be resolved by angular resolver.
var controller = function () {
// vars, costs, etc.
console.log("startInit");
// big JSON file
var promise = getJSON(param);
return promise.then( function() {
//some func
console.log("finish");
return true;
});
};
Related
im working with AnuglarJS 1.4.8. I want give out the data with ng-repeat.
I have the following problem and i have no more ideas to solve it. I tried the solutions from AnuglarJS but i doesnt work.
Could someone help me please.
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.4.8/$rootScope/infdig?p0=10&p1=%5B%5D
Service:
.service('database', function ($http) {
self = this;
this.url = 'http://localhost:3001';
this.getPersons = function(cb){
return $http({
method: 'GET',
url: self.url + '/loadperson'
}).success(function (data) {
cb(data);
});
};
});
Controller:
angular.module('myApp')
.controller('personCtrl', function ($scope, database) {
$scope.people = function(){
return{
getAll: function () {
database.getPersons(function (data) {
return data;
// should return a Object(id, name)
});
}
}
};
HTML:
<div ng-repeat="people in people().getAll()">
<p>{{people.name}}</p>
</div>
You are missing the non-blocking way of javascript. Try following, it should work
Controller:
angular.module('myApp')
.controller('personCtrl', function ($scope, database) {
$scope.loadPeoples = function(){
return{
getAll: function () {
database.getPersons(function (data) {
$scope.peoples = data;
// should return a Object(id, name)
});
}
}
};
$scope.loadPeoples();
})
HTML:
<div ng-repeat="people in peoples">
<p>{{people.name}}</p>
</div>
Try that.
Service:
.service('database', function ($http) {
self = this;
this.url = 'http://localhost:3001';
this.getPersons = function(){
return $http({
method: 'GET',
url: self.url + '/loadperson'
});
};
});
Controller:
angular.module('myApp')
.controller('personCtrl', function ($scope, database) {
database.getPerson().success(function(data) {
$scope.people = data;
});
});
HTML:
<div ng-repeat="person in people">
<p>{{person.name}}</p>
</div>
You should also be aware that you shouldn't return each time a NEW array for iterating. Otherwise angular will keep calling that function for retrieving a "stable" value for the array.
You've made a common error in javascript when running asynchronous queries. The pattern goes:
function outerFunction() {
invokeInnerFunction(function() {
return 3;
}
}
What does outerFunction() return? An error is to think it returns 3, but the answer is actually that outerFunction doesn't return anything.
Likewise, in your example getAll isn't actually returning anything; it's just calling an asynchronous method. This asynchronous method invoked $http, which triggers a digest loop which will result in getAll being called again, and so on for ever. Be thankful that angular can detect this problem.
You only want to call the database query once on startup, and initialize the list of people. Simply store this list in a variable so it won't query the database again on the next digest loop.
angular.module('myApp')
.controller('personCtrl', function ($scope, database) {
$scope.allPeople = [];
database.getPersons(function(data) {
$scope.allPeople = data;
});
};
An then for your HTML
<div ng-repeat="people in allPeople">
<p>{{people.name}}</p>
</div>
Much simpler.
Have you tried making a separate function to fetch the entities from the data base, then put this data in a variable, that you then will pass to the ngRepeat ?
your controller
angular.module('myApp')
.controller('personCtrl', function ($scope, database) {
$scope.people = [];
$scope.getPeople = function(){
return{
getAll: function () {
database.getPersons(function (data) {
$scope.people = data;
return;
// should return a Object(id, name)
});
}
}
//load the list of people
$scope.getPeople();
};
your view
<div ng-repeat="person in people">
<p>{{person.name}}</p>
</div>
I'm writing an angular 1.5.0-rc0 application using bootstrap for a nav bar component.
I want to show the user an added items to his navigation bar if his user group id is 1.
first I created a service:
app.factory('UserService', function() {
return {
userGroupId : null
};
});
I created the nav bar as a directive, so i included it in the main html file
<nav-bar></nav-bar>
and the nav-bar directive code:
(function () {
angular.module('myalcoholist').directive('navBar', function () {
return {
restrict: 'E',
templateUrl: 'views/nav.html',
controller: ['$scope','$auth', 'UserService',function ($scope,$auth,UserService) {
$scope.user=UserService;
$scope.isAuthenticated = function()
{
return $auth.isAuthenticated();
};
}]
}
});
})();
as you can see I set $scope.user as the returned object from UserService.
in my login controller, after a successful login I set the userGroupId.
angular.module('myalcoholist').controller('LoginController',['$scope','$auth','$location', 'toastr','UserService',function ($scope,$auth,$location,toastr,UserService) {
$scope.authenticate = function (provider) {
$auth.authenticate(provider).then(function (data) {
var accessToken = data.data.token;
apiKey=accessToken;
UserService.userGroupId=data.data.user_group_id;
...
now.. my nav-bar template file is as the following code:
<li ng-show="user.userGroupId == 1">
Admin Drinks
</li>
even after the authentication, when I uset userGroupId to 1 the element is still not shown.
any ideas?
update
I debugged and noticed that UserService.userGroupId is still null. so
I changed the UserService to have the following code:
app.factory('UserService', function() {
var user = {userGroupId:null};
return {
setUserGroupId: function (userGroupId) {
user.userGroupId=setUserGroupId;
},
getUserGroupId: function () {
return user.userGroupId;
}
};
});
in my LoginController I now try to execute setUserGroupId:
angular.module('myalcoholist').controller('LoginController',['$scope','$auth','$location', 'toastr','UserService',function ($scope,$auth,$location,toastr,UserService) {
$scope.authenticate = function (provider) {
$auth.authenticate(provider).then(function (data) {
var accessToken = data.data.token;
apiKey=accessToken;
UserService.setUserGroupId(data.data.user_group_id);
...
when I debug i see that userService is an object with two functions as I defined, but when the javascript chrome debugger tries to execute this line:
UserService.setUserGroupId(data.data.user_group_id);
I get the following error:
ReferenceError: setUserGroupId is not defined
at Object.setUserGroupId (app.js:21)
at login-controller.js:12
at angular.js:15287
at m.$eval (angular.js:16554)
at m.$digest (angular.js:16372)
at m.$apply (angular.js:16662)
at g (angular.js:11033)
at t (angular.js:11231)
at XMLHttpRequest.v.onload (angular.js:11172)
I have created a fiddle showcasing your requirement (as close as possible), and it seems to work fine.
http://jsfiddle.net/HB7LU/21493/
My guess is that you aren't actually setting the value when you think you are, and will likely require some debugging. Here is the code for brevity.
HTML
<div ng-controller="MyCtrl">
<div ng-click="clicked()">
Click ME, {{user.value}}!
</div>
<test-dir></test-dir>
</div>
JS
angular.module('myApp',[])
.service('TestService', function(){
return {
value: 2
};
})
.directive('testDir', function(){
return {
restrict: 'E',
template: '<div ng-show="user.value === 1">Here is some text</div><div>Some more always showing</div>',
controller: function ($scope, TestService) {
$scope.user = TestService;
}
};
})
.controller('MyCtrl', function($scope, TestService){
$scope.user = TestService;
$scope.clicked = function(){
TestService.value = 1;
};
});
The non-GET instance action $save doesn't work in my example. I always get the Error, that $save is not a function. The problem is, I don't know where I have to define the $scope.example = new Resource();, because in my example I'm using 2 Controllers. One for the table list with objects and the other one for my modal window, where you can take CRUD operations. The CRUD operations are defined in an angular service.
The code is structured as follows:
Servie of Resource:
...
return {
name: $resource(baseUrl + '/api/name/:Id', {
Id: '#Id'
}, {
'update': {
method: 'PUT'
}
}),
...
Service of CRUD:
...
return {
create: function (newName) {
return newName.$save();
},
...
Ctrl of modal window:
$scope.selected = new resService.name();
$scope.createItem = function (newName) {
CrudService.create(newName).then(
function () {
$scope.dataSuccess = 'Person created.';
$scope.newName = null;
},
function (err) {
$scope.dataError = err.data.ModelState;
});
}
}
$scope.form = [{
label: 'Firstname',
fieldType: 'text',
name: 'Fname',
id: 'fname-id',
propertyName: 'fname',
disabled: false,
pattern: /^[a-zA-Z]{4}[a-zA-Z]*/,
required: true,
errRequired: 'Firstname is required.',
errPattern: 'Firstname has at least 4 letters.'
},
...];
The view with form:
<form class="form-horizontal" name="editForm" novalidate>
<div class="form-group-sm has-feedback" ng-repeat="elem in form" ng-class="{ 'has-error' : hasError(editForm, elem.name), 'has-success' : hasSuccess(editForm, elem.name) }">
<label class="control-label" for="{{elem.id}}">{{elem.label}}</label>
<input type="{{elem.fieldType}}"
class="form-control"
placeholder="{{elem.label}}"
name="{{elem.name}}"
id="{{elem.id}}"
ng-model="selected[elem.propertyName]"
ng-disabled="{{elem.disabled}}"
ng-pattern="elem.pattern"
ng-required="{{elem.required}}"
/>
<p class="help-block" ng-if="elem.errRequired" ng-show="editForm[elem.name].$error.required && editForm[elem.name].$touched">{{elem.errRequired}}</p>
<p class="help-block" ng-if="elem.errPattern" ng-show="editForm[elem.name].$error.pattern">{{elem.errPattern}}</p>
EDIT:
I'm getting a new Error. The console tells, that I have to use track by expression. But I was trying to use the form view without generating and then works. But I need the generated form view (the example view above).
Error Message:
Error: ngRepeat:dupes
Duplicate Key in Repeater
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.
If you wan't to create a new object you need the choose the service between the Services choice (factory, service, providers).
The difference between a factory and a service, is about syntax. Just syntax.
.factory(function(){
//Private variables and functions
var x = "ez";
function getX(){
return x;
}
//Public functions (or variables)
return {
a : "test",
getA : function(){
return a;
}
}
})
//Service example
.service(function(){
//Handled by Angular:
//new() is used to create a new object
//Private functions and variables
var x = "test";
function getX(){
return x;
}
//Public funcitons (and variables)
this.a = function(){
"test";
};
this.getA = function(){
return a;
};
//Handeled by AngularJS
//return this;
});
Everything that is returned in the factory is available.
The service automaticaly creates a new object when calling it, which makes available the object ("this")
Calling a service or a factory remains the same:
var a = service.getA();
var a = factory.getA();
EDIT
Notice also that you can decide if your promise is going to the next error or success call.
Just as an exmaple:
xhr()
.then(success1, error1)
.then(success2, error2)
.then(success3, error3)
...
success and error are all callback functions.
By using $q you can go to the next success or error, wathever the callback.
QUESTION CODE
. factory ( 'YourFacotry' , [ '$resource' ,
function ( $resource ) {
return $resource ( '/api/note/:id' , { id : '#id' },
{
markAsDone :
{
url : '/api/note/:id/done' ,
method : 'POST' ,
isArray : true
}
});
}]);
Ctrl of modal window:
$scope.createItem = function () { //Forgot $scope here!
CrudService.query().then(
function () {
$scope.dataSuccess = 'Person created';
$scope.newName = null;
},
function (err) {
$scope.dataError = err.data.ModelState;
});
}
}
I have a loader that I show while an async service call is completed, and simply want to hide the loader when complete. Here is my controller:
app.controller('DataController',
function($scope, DataService) {
// UI state
$scope.loading = true;
DataService.getData({ "count": 10 }).then(function(data) {
$scope.data = data;
// UI state
$scope.loading = false; // does not update ng-view
$scope.$apply(function() { // generates error
$scope.loading = false;
});
});
});
And the view:
<div ng-controller="DataController">
<div id="container">
<div>
{{ loading }}
</div>
<div class="spinner large" ng-show="loading"></div>
<div class="data-container" ng-show="!loading">
...
</div>
</div>
</div>
Note the the {{ loading }} value gets updated properly in the view. Using the wrapping $scope.$apply() call resulted in an error:
Error: [$rootScope:inprog]
UPDATE
As this might be promise-related, here's the promise generating getData() method from the DataService factory:
getData: function(params) {
var deferred = $q.defer();
APIService.data(params).then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
And the last piece, the APIService.data() method:
data: function(params) {
var deferred = $q.defer();
$resource(endpoint + '/data/feed', {}, {
'query': {
method: 'POST',
headers: headers
}
}).query(params).$promise.then(function(data) {
deferred.resolve(data);
});
return deferred.promise;
}
I would solve this by binding the show/hide directive to the data-property in the controller. It will be the same as false if the data is undefined.
<div class="spinner large" ng-hide="data"></div>
<div class="data-container" ng-show="data">
Try to use
$scope.$evalAsync(function() {
$scope.loading = false;
});
Found the issue - as this is in a Chrome Extension, I needed to include the Angular CSS CSP file, which includes the ng-hide class definition. Including that file resulted in the code working as expected. Thanks everyone for the help. More info:
https://docs.angularjs.org/api/ng/directive/ngCsp
Overview
I am building an app (running on MAMP) that holds contact information that will expand to hold more data such as project name & deadline, once this part is functional.
Questions
When the user visits /projects.php#/project/ I would like them to see a list of all the project names with a link to their detail page.
How should I write the following to access all of my data?
Do I need the .json at the end?
What does the #id do?
return $resource('data/project.json/:id', {id: '#id'});
When the user visits /projects.php#/project/a-gran-goodn I would like them to see the details about this project(for now, just the name & address).
How should I write the following to return my data by Id?
$scope.project = $routeParams.id ? Project.get({id: $routeParams.id}): new Project();
plunkr file
http://plnkr.co/edit/7YPBog
project.json
This file lives on http://localhost:8888/angularjs/ProjectsManager/data/project.json
[
{ "address" : [ " 3156 Dusty Highway",
" Teaneck New Jersey 07009-6370 US"
],
"id" : "a-gran-goodn",
"name" : "Grania Goodner",
"phone" : " (862) 531-9163"
},
{ "address" : [ " 62 Red Fawn Moor",
" Rodney Village West Virginia 25911-8091 US"
],
"id" : "b-aime-defranc",
"name" : "Aimery Defranco",
"phone" : " (681) 324-9946"
}
]
app.js
var projectsApp = angular.module('projects', ['ngResource']);
projectsApp.config(function($routeProvider) {
$routeProvider
.when('/', {
controller: 'ProjectListCtrl',
templateUrl: 'partials/projectlist.html'})
.when('project/:id', {
controller: 'ProjectDetailCtrl',
templateUrl: 'partials/projectdetail.html'
})
.otherwise('/');
});
projectsApp.factory('Project', function($resource) {
return $resource('data/project.json/:id', {id: '#id'});
});
projectsApp.controller('ProjectListCtrl', function(Project, $scope) {
$scope.projects = Project.query();
console.log($scope.projects);
});
projectsApp.controller('ProjectDetailCtrl', function(Project, $routeParams, $scope) {
$scope.project = $routeParams.id
? Project.get({id: $routeParams.id})
: new Project();
});
partials/projectlist.html
Add new item
<ul class="unstyled">
<li ng-repeat="project in projects">
<div class="well">
<h2><small>{{project.id}}</small> {{project.name}}</h2>
View Info for {{project.name}}
</div>
</li>
</ul>
partials/projectdetails.html
<h3>Information</h3>
<p>Name: {{project.name}}</p>
<p>Phone Number: {{project.phone}}</p>
<p ng-repeat="line in project.address">{{line}}</p>
index.php
<?php
header('Access-Control-Allow-Origin: *');
?>
<!doctype html>
<html ng-app="projects">
<head>
<meta charset="utf-8">
<title ng-bind="title" ng-cloak>Restaurant —</title>
<link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
</head>
<body ng-controller="ProjectListCtrl">
<a class="brand" href="#">Projects Manager</a>
<div id="app-container" class="container-fluid">
<div class="row-fluid">
<div class="span12" id="main" ng-view>
</div><!--/.span12-->
</div><!--/.row-fluid-->
<footer>Copyright Projects © 2013</footer>
</div><!--/.container-->
<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<!-- Don't forget to load angularjs AND angular-resource.js -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular-resource.js></script>
<!--Controllers-->
<script src="app.js"></script>
</body>
</html>
Since you can't query against a raw JSON file like you can with RESTful-style URLs (which is what $resource is built to do), you can instead get a copy of the JSON and then build your own query, get, etc. that looks at the data and returns the right thing. It's a bit tricky because you also want to support new Project, which doesn't really make sense when using a file-backed store, but this example supports it:
projectsApp.factory('Project', function($http) {
// Create an internal promise that resolves to the data inside project.json;
// we'll use this promise in our own API to get the data we need.
var json = $http.get('project.json').then(function(response) {
return response.data;
});
// A basic JavaScript constructor to create new projects;
// passed in data gets copied directly to the object.
// (This is not the best design, but works for this demo.)
var Project = function(data) {
if (data) angular.copy(data, this);
};
// The query function returns an promise that resolves to
// an array of Projects, one for each in the JSON.
Project.query = function() {
return json.then(function(data) {
return data.map(function(project) {
return new Project(project);
});
})
};
// The get function returns a promise that resolves to a
// specific project, found by ID. We find it by looping
// over all of them and checking to see if the IDs match.
Project.get = function(id) {
return json.then(function(data) {
var result = null;
angular.forEach(data, function(project) {
if (project.id == id) result = new Project(project);
});
return result;
})
};
// Finally, the factory itself returns the entire
// Project constructor (which has `query` and `get` attached).
return Project;
});
You can use the results of query and get like any other promise:
projectsApp.controller('ProjectListCtrl', function(Project, $scope) {
$scope.projects = Project.query();
});
projectsApp.controller('ProjectDetailCtrl', function(Project, $routeParams, $scope) {
$scope.project = $routeParams.id
? Project.get($routeParams.id)
: new Project();
});
Note the change to Project.get($routeParams.id); also, the updated Plunker also fixes a problem in your $routeProvider configuration.
This is all demonstrated here: http://plnkr.co/edit/mzQhGg?p=preview
i will paste here a generic code i use to fetch json from your local or a remoteserver maybe it will help you:
it uses a factory that you can call when you need it.
app.factory('jsonFactory', function($http) {
var jsonFactory= {
fromServer: function() {
var url = 'http://example.com/json.json';
var promise = $http.jsonp(url).then(function (response) {
return response.data;
});
return promise;
},
hospitals: function() {
var url = 'jsons/hospitals.js';
var promise = $http.get(url).then(function (response) {
return response.data;
});
return promise;
}
};
return jsonFactory;
});
Then when you need to call it:
function cardinalCtrl(jsonFactory, $scope, $filter, $routeParams) {
jsonFactory.hospitals().then(function(d){
$scope.hospitals=d.hospitals;
});
jsonFactory.fromServer().then(function(d){
$scope.fromServer=d.hospitals;
});
}