Factory:
(function(){
angular
.module('projectApp')
.factory('weatherfactory', weatherfactory);
weatherfactory.$inject=['$http'];
function weatherfactory($http){
var cities=[
{name: "Łódź", link: "http://api.openweathermap.org/data/2.5/forecast/daily? q=Lodz&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Warszawa", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Warszawa&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Wrocław", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Wroclaw&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Kraków", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Krakow&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Gdańsk", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Gdansk&mode=json&units=metric&cnt=7&lang=pl"},
{name: "Londyn", link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=London&mode=json&units=metric&cnt=7&lang=pl"}
];
var weat={};
var service={
getCities: getCities,
GetWeather: _GetWeather
};
return service;
function _GetWeather(link){
return $http.get(link);
}
function getCities(){
return cities;
}
}
})();
In controller i get data from factory:
sa.cities=weatherfactory.getCities();
And in view i am trying to show data in but it does'n work:
<select class="form-control" ng-options="item.name as item.link for item in sa.cities"></select>
I am using controllerAs approach.
ng-options requires ng-model to be specified:
<select class="form-control" ng-model="something" ng-options="item.name as item.link for item in sa.cities"></select>
var app = angular.module('app', []);
app.controller('myController', function($scope) {
var sa = this;
sa.cities = [{
name: "Łódź",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Lodz&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Warszawa",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Warszawa&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Wrocław",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Wroclaw&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Kraków",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Krakow&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Gdańsk",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=Gdansk&mode=json&units=metric&cnt=7&lang=pl"
}, {
name: "Londyn",
link: "http://api.openweathermap.org/data/2.5/forecast/daily?q=London&mode=json&units=metric&cnt=7&lang=pl"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app' ng-controller='myController as sa'>
<select ng-model="selected" ng-options="item.name as item.link for item in sa.cities"></select>
</div>
Related
I found a code that outputs a multi-level list into Angular here
http://jsfiddle.net/c4Kp8/
var items = [{
title: 'Something',
children: [
{ title: 'Hello World' },
{ title: 'Hello Overflow' },
{ title: 'John Doe', children: [
{ title: 'Amazing title' },
{ title: 'Google it' },
{ title: 'Im a child', children: [
{ title: 'Another ' },
{ title: 'He\'s my brother' },
{ title: 'She\'s my mother.', children: [
{title: 'You never know if im going to have children'}
]}
]}
]}
]
}];
var app = angular.module('app', []);
app.controller('test', function( $scope ) {
$scope.items = items;
});
app.directive('nestedItem', ['$compile', function($compile){
return {
restrict: 'A',
link: function(scope, element){
console.log(element);
if (scope.item.children){
var html = $compile('<ul><li nested-item ng-repeat="item in item.children">{{item.title}}</li></ul>')(scope);
element.append(html);
}
}
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="test">
<ul>
<li nested-item ng-repeat="item in items">{{item.title}}</li>
</ul>
</div>
How to implement here hiding and displaying items in the list? Only Angular not jquery....
Hope you know that, you can make nested ng-repeat without special directive for it :-) ?
You can use for example angular-ui.bootstrap.collapse, or some custom solutions.
Solutions:
Angular UI Bootrap
Googled custom solution
Use ng-show / ng-hide / ng-if
https://docs.angularjs.org/api/ng/directive/ngShow
https://docs.angularjs.org/api/ng/directive/ngHide
https://docs.angularjs.org/api/ng/directive/ngIf
Maybe a treeview is more adequate than multiples imbricated ng-repeat
http://ngmodules.org/modules/angular.treeview
Hi I have a directive like,
mainApp.directive('myMenu',function(){
return {
restrict : 'E',
scope :{menuItems : "=menuItems"},
compile: function(element, attributes) {
var linkFunction = function($scope, element, attributes){
for (i = 0;i<$scope.menuItems.length;i++){
element.append('<li>'+$scope.menuItems[i].name+'</li>');
}
}
return linkFunction;
}
}
});
I am using it like below in my HTML
<my-menu menuItems="menuItems"></my-menu>
But in the console I am getting an error like
TypeError: Cannot read property 'length' of undefined
The problem could be that, then the linking phase is executed the menuitems may not be loaded so $scope.menuItems may be undefined.
A better solution could be
var mainApp = angular.module('my-app', [], function() {})
mainApp.controller('AppController', function($scope) {
$scope.menuItems = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}, {
name: 'four'
}];
})
mainApp.directive('myMenu', function() {
return {
restrict: 'E',
scope: {
menuItems: "="
},
template: '<ul><li ng-repeat="item in menuItems">{{item.name}}</li></ul>'
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<my-menu menu-items="menuItems"></my-menu>
</div>
If you can't use template then
var mainApp = angular.module('my-app', [], function() {})
mainApp.controller('AppController', function($scope) {
$scope.menuItems = [{
name: 'one'
}, {
name: 'two'
}, {
name: 'three'
}, {
name: 'four'
}];
})
mainApp.directive('myMenu', function() {
return {
restrict: 'E',
scope: {
menuItems: "=menuItems"
},
link: function($scope, element, attributes) {
$scope.$watch('menuItems', function(value) {
element.empty();
angular.forEach(value, function(item) {
element.append('<li>' + item.name + '</li>');
});
});
}
}
});
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<my-menu menu-items="menuItems"></my-menu>
</div>
The Issue was with the name I used, like menuItems in directive should be equal to menu-items, solved the issue by repalcing the menuItems to menu.
use $scope.$eval(attributes.menuItems) inside compile function to get menuItems
I am building up a simple task management system with AngularJS and am currently playing with it and mocking the data up. I have a problem with the injection of a service into my ProjectCtrl controller and I cannot wrap my head around it.
At the bottom of this code: Why is the projects variable in the ProjectsCtrl controller just an empty array? It should contain the mockup data, no? What am I doing wrong?
Please excuse this maybe very stupid question. I just don't find my mistake.
angular.module("TaskManager", ['ui.router'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl'
})
.state('projects', {
url: '/projects/{id}',
templateUrl: '/projects.html',
controller: 'ProjectsCtrl'
});
$urlRouterProvider.otherwise('home');
}
])
.factory('projects', [function(){
var o = {
projects: []
};
return o;
}])
.controller('MainCtrl', [
'$scope',
'projects',
function($scope, projects){
$scope.projects = projects.projects;
$scope.projects = [
{title: "project 1", done: 0},
{title: "Another project 2", done: 0},
{title: "project 3", done: 1},
{title: "project 4", done: 0},
{title: "project 5", done: 0},
];
$scope.addproject = function() {
if(!$scope.title || $scope.title === '') { return };
$scope.projects.push({
title: $scope.title,
comment: $scope.comment,
done: 0,
tasks: [
{title: 'Task 1', comment: 'Kommentar 1', done: 0},
{title: 'Task 2', comment: 'Kommentar 2', done: 0}
]
});
$scope.title = "";
$scope.comment = "";
}
$scope.markAsDone = function(project) {
project.done = 1;
}
}
])
.controller('ProjectsCtrl', [
'$scope',
'$stateParams',
'projects',
function($scope, $stateParams, projects){
$scope.project = projects.projects[$stateParams.id];
// Why is this an empty array?
console.log(projects);
}
])
For completion: This is the HTML part:
<html>
<head>
<title>TaskManager</title>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="TaskManager">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<ui-view></ui-view>
</div>
</div>
<script type="text/ng-template" id="/home.html">
<div class="page-header">
<h1>TaskManager</h1>
TEST
</div>
<div ng-repeat="project in projects | orderBy: ['done','title']">
<span ng-click="markAsDone(project)">Done</span>
{{project.title}} - done: {{project.done}}
<span>
Tasks
</span>
<p ng-show="project.comment">Comment: {{project.comment}}</p>
</div>
</hr>
<form ng-submit="addproject()" style="margin-top: 30px;">
<h3>Add new project</h3>
<input type="text" ng-model="title" placeholder="project"></input>
<br><br>
<textarea name="comment" ng-model="comment"></textarea>
<br>
<button type="submit" class="btn">Post</button>
</form>
</script>
<script type="text/ng-template" id="/projects.html">
<div class="page-header">
<h3>Project: {{project.title}}</h3>
</div>
<div ng-repeat="task in project.tasks | orderBy: ['done','title']">
{{task.title}} - done: {{task.done}}
<p ng-show="task.comment">Comment: {{task.comment}}</p>
</div>
</script>
</body>
</html>
You never assign the values to projects.projects. In MainCtrl, you assign $scope.projects to the value of projects.projects (which is an empty array at that point). Then after that you overwrite $scope.projects with a totally new and different array, so you never end up modifying projects.projects.
I would move functions that let you add, remove, update items to projects service, but in the interim you can assign projects.projects first and then assign that to $scope.projects.
Better projects service:
.factory('projects', function() {
var projects = [];
return {
add: function(item) {
// your additional code
projects.push(item);
},
remove: function(item) {
// your additional code
var i = projects.indexOf(item);
if (i >=0) projects.splice(i,1);
},
get: function() {
return projects;
},
initialize: function(items) {
projects = items;
}
};
});
Then you can use this in your controller:
.controller('MainCtrl', function($scope, projects) {
projects.initialize([ ... ]);
$scope.projects = projects.get();
$scope.addproject = function() {
// NOTE: move whatever code you feel is or could be the responsibility of the service to the add method. I left this function as-is though, so you have a frame of reference.
if(!$scope.title || $scope.title === '') { return };
projects.add({
title: $scope.title,
comment: $scope.comment,
done: 0,
tasks: [
{title: 'Task 1', comment: 'Kommentar 1', done: 0},
{title: 'Task 2', comment: 'Kommentar 2', done: 0}
]
});
$scope.title = "";
$scope.comment = "";
};
// etc.
});
I recommend making the service the single point of authority so you can test logic pertaining to interacting with the service and its data, and avoid repeating yourself when different controllers and directives need to interact with the service or its data. It'll also help you avoid these sorts of issues where the data becomes out of sync between different controllers, directives, etc.
When you're setting up the mock data, you're only setting them on $scope.projects...the factory's projects is never updated. You could flip it around and it should work:
projects.projects = [<mock data>];
$scope.projects = projects.projects;
Put your mocked data in factory and remove the initialization in your controller.
angular.module("TaskManager", ['ui.router'])
.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl'
})
.state('projects', {
url: '/projects/{id}',
templateUrl: '/projects.html',
controller: 'ProjectsCtrl'
});
$urlRouterProvider.otherwise('home');
}
])
.factory('projects', [function(){
var o = {
projects: [
{title: "project 1", done: 0},
{title: "Another project 2", done: 0},
{title: "project 3", done: 1},
{title: "project 4", done: 0},
{title: "project 5", done: 0},
]
};
return o;
}])
.controller('MainCtrl', [
'$scope',
'projects',
function($scope, projects){
$scope.projects = projects.projects;
$scope.addproject = function() {
if(!$scope.title || $scope.title === '') { return };
$scope.projects.push({
title: $scope.title,
comment: $scope.comment,
done: 0,
tasks: [
{title: 'Task 1', comment: 'Kommentar 1', done: 0},
{title: 'Task 2', comment: 'Kommentar 2', done: 0}
]
});
$scope.title = "";
$scope.comment = "";
}
$scope.markAsDone = function(project) {
project.done = 1;
}
}
])
.controller('ProjectsCtrl', [
'$scope',
'$stateParams',
'projects',
function($scope, $stateParams, projects){
$scope.project = projects.projects[$stateParams.id];
// Why is this an empty array?
console.log(projects);
}
])
In the example from the Angular documentation, a directive can be used by putting its name as an attribute in a <div>. The example given is
<div ng-controller="Controller">
<div my-customer></div>
</div>
with the js looking like
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
However, in a similar example, over here, if I change the html code from <tree> to <div tree>, the code no longer works.
Why not?
The code from JS Fiddle:
<div ng-app="myapp">
<div ng-controller="TreeCtrl">
<tree family="treeFamily">
<p>{{ family.name }}</p>
</tree>
</div>
</div>
var module = angular.module('myapp', []);
module.controller("TreeCtrl", function($scope) {
$scope.treeFamily = {
name : "Parent",
children: [{
name : "Child1",
children: [{
name : "Grandchild1",
children: []
},{
name : "Grandchild2",
children: []
},{
name : "Grandchild3",
children: []
}]
}, {
name: "Child2",
children: []
}]
};
});
module.directive("tree", function($compile) {
return {
restrict: "E",
transclude: true,
scope: {family: '='},
template:
'<ul>' +
'<li ng-transclude></li>' +
'<li ng-repeat="child in family.children">' +
'<tree family="child">{{family.name}}</tree>' +
'</li>' +
'</ul>',
compile: function(tElement, tAttr, transclude) {
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents, transclude);
}
compiledContents(scope, function(clone, scope) {
iElement.append(clone);
});
};
}
};
});
tree {
margin-left: 20px;
display: block;
}
The restrict option is used to specify how a directive can be invoked on the page. There are four different ways to invoke a directive, so there are four valid options for restrict:
'A' - attribute - <span ng-sparkline></span>
'E' - element - <ng-sparkline></ng-sparkline>
'C' - class - <span class="ng-sparkline"></span>
'M' - comment - <!-- directive: ng-sparkline -->
In your case its not working because it is defined as restrict 'E' - element.
It's because of the restrict option in the directive.
Here it is set to e which means to match only the element name.
More https://docs.angularjs.org/guide/directive
In a controller I have defined two different arrays and I need to pass different arrays to directives, but the same item array is getting passed only.
Actually this is just snippets. In my actual code this directive I am using inside another.
Is that causing problem or any other way?
<div my-sticky tags="items" template_name="test2.html"></div>
<div my-sticky tags="kititems" template_name="test2.html"></div>
JS:
app.controller('MyCtrl', function($scope, $http) {
$scope.items = [
{ id: 18, text: '1' },
{ id: 28, text: '2' },
{ id: 38, text: '3' }
];
$scope.kititems = [
{ id: 0, text: '001' },
{ id: 028, text: '002' },
{ id: 038, text: '003' }
];
});
app.directive("mySticky", function($http, $compile) {
return {
restrict : "A",
scope : {
templateName: "#",
tags: "=",
},
templateUrl : function(el, attrs) {
return attrs.templateName;
},
controller : function($http, $scope, $element, $sce, $templateCache, $compile, $attrs) {
$scope.drawerStyle = {left: '140px'};
//$scope.CommonArray=$attrs.tags;
},
replace : false,
transclude : false,
link : function(scope, element, attrs) {
// change element just to show we can
}
};
});
Try this one
Working Demo
Inside your templateURL(test2.html) should like this i.e,
<div ng-repeat="tag in tags"> {{tag.id}} {{tag.text}} </div>
You might try to access items or kititems both are not in current scope because you have used isolated scope here.For clarity template used here instead of templateURL.
HTML Markup:
<div ng-controller="MyCtrl">
MyCtrl
<div my-sticky tags="items"></div>
<hr/>
<div my-sticky tags="kititems"></div>
</div>
angular modules
var app = angular.module("myApp",[]);
app.controller('MyCtrl', function($scope) {
$scope.items = [
{ id: 18, text: '1' },
{ id: 28, text: '2' },
{ id: 38, text: '3' }
];
$scope.kititems = [
{ id: 0, text: '001' },
{ id: 028, text: '002' },
{ id: 038, text: '003' }
];
});
app.directive("mySticky", function($http, $compile) {
return {
restrict : "A",
scope : {
templateName: "#",
tags: "=",
},
template :'<div ng-repeat="tag in tags"> {{tag.id}} {{tag.text}} </div>',
controller : function($http, $scope, $element, $sce, $templateCache, $compile, $attrs) {
$scope.drawerStyle = {left: '140px'};
// console.log($scope);
//$scope.CommonArray=$attrs.tags;
},
replace : false,
transclude : false,
link : function(scope, element, attrs) {
// change element just to show we can
}
};
});