Angular directive taking no effect - javascript

I am new to angular and I am trying to build my first directive, but it's not rendering inside my HTML.
Here is my HTML page : the directive is called at the bottom of the HTML
<!DOCTYPE html>
<html ng-app="store">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
</head>
<body ng-controller="storeController as store">
<ul class="list-group">
<li class="list-group-item" ng-repeat="product in store.product">
<h3 >
<product-name></product-name>
</h3>
</li>
</ul>
</body>
</html>
And here is my app.js :
(function(){
var app = angular.module("store",[]);
var gem = [
{
name: "Dodec",
price: 2.95,
canPurchase: true,
soldOut: false,
reviews: []
},
{
name: "Panta",
price: 20.4,
canPurchase: true,
soldOut: false,
reviews: []
}
];
app.controller("storeController",function(){
this.product = gem;
});
app.controller("tabController",function($scope){
$scope.tab = 1;
$scope.SetTab = function(value){
$scope.tab = value;
};
$scope.IsSet = function(value){
return value === $scope.tab;
}
});
app.controller("ReviewController", function($scope){
$scope.review = "";
$scope.addReview = function(product){
product.reviews.push($scope.review);
$scope.review = "";
};
});
app.directive("productName",function(){
return {
restrict : "E",
templateUrl : "product-name.html"
};
});
})();
notice the directive at the end.
And finally here is my product-name.html file :
{{product.name}}
<em class="pull-right"> {{product.price | currency}}</em>
What did I do wrong? why is the directive not behaving like it should?
Thanks

In product-name.html, AngularJS directive templates must have one root element.
I wrapped the html in divs in this plnkr.
<div>
{{product.name}}
<em class="pull-right"> {{product.price | currency}}</em>
</div>

The problem was with my server, I downloaded the http server from here https://github.com/nodeapps/http-server then started it and the directive shows up now.

Related

404 Not Found in AngularJS

I just started AngularJS. When I try to implemt following example, I am getting
404 Not Found
This is my main.html
<!DOCTYPE html>
<html ng-app="demoApp">
<script src="angular.min.js"></script>
<head>
<title>main page</title>
</head>
<body>
<div ng-view></div>
<script>
var demo = angular.module('demoApp',[]);
demo.config(function ($routeProvider){
$routeProvider
.when ('foo',{
controller : 'SimpleController',
templateUrl: 'first.html'
})
});
demo.controller('SimpleController',function($scope){
$scope.customers = [
{name: 'sac',city:'abcd'},
{name: 'mac',city:'efgh'},
{name: 'nalaka',city:'ijkl'}
];
});
</script>
</body>
</html>
My first.html
<ul>
<li ng-repeat="cus in customers ">{{ cus.name }}</li>
</ul>
When I visit localhost/foo I am getting 404 exception. I just started AngularJS. So any help/guide to solve this problem will be a great help for me.
Thanks in advance
You should refer angular-route.min.js file on page & then include ngRoute module in your application.
var demo = angular.module('demoApp',['ngRoute']);
You should correct your .when condition to below
.when('/foo',
As you did't enabled html5mode in your routing, you could access the page via localhost/#/foo URL.
var demo = angular.module('demoApp', ['ngRoute']);
demo.config(function($routeProvider) {
$routeProvider
.when('/foo', {
controller: 'SimpleController',
templateUrl: 'first.html'
})
// By default it will open foo
.otherwise({redirectTo: '/foo'})
});
demo.controller('SimpleController', function($scope) {
$scope.customers = [{
name: 'sac',
city: 'abcd'
},
{
name: 'mac',
city: 'efgh'
},
{
name: 'nalaka',
city: 'ijkl'
}
];
});
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-route.min.js"></script>
<div ng-app="demoApp">
<div ng-view></div>
<script type="text/ng-template" id="first.html">
Content of the template.
</script>
</div>

AngularJS load JSON data then parse/load HTML from it

first and foremost, this is my first encouter with Angular.
What I am aiming to achieve is, I have a list of notifications, that I somehow must limit with limitTo, so the elements are limited to three and after I click the button, the rest should load up.
What I dont understand how to do:
set up the "view" and how to apply ng-repeat
load the JSON data that I have set up and somehow parse it as pure HTML from the *json into the view
after everything goes well, use limitTo so I Can limit the items to 3 from the start and after I click the button, I want the rest to load under.
I request assistance, this is as far as I've come.
Sample code because SO requires it:
var app = angular.module('notifyApp', []);
app.controller('mainController', function($scope, $http) {
$http({
method: 'GET',
url: 'notifications.json'
}).success(function(){
console.log('success');
}).error(function(){
console.log('error');
});
$scope.loadmore = true;
});
Here is a Plunker
Thank You in advance!
Your plunker has a couple of errors.
First of all, your ng-app in index.html should be notifyApp, since that's what you've set in your script.js - var app = angular.module('notifyApp', []);
Secondly: you have to assign your notifications to $scope.notifications in your success function.
$http({
method: 'GET',
url: 'notifications.json'
}).then(function(res){
$scope.notifications = res;
});
After that you should already see the first 3 elements.
Last thing you need is to remove notifications = !notifications from the load more button's ng-click attr.
Modified Plunker here.
Setting up the view and using ng-repeat.
You'll want to store the notifications somewhere (probably an array) and then use ng-repeat="data in yourArray" in your html file within a tag (div for example). You can then apply filters to it if you want to show specific notifications. In your case, you want to initially show 3 and then move onto the rest if a button is clicked. You could create a separate array that stores the first 3 notifications and and another that contains the whole array and use ng-if in your tag to display the results.
var myApp = angular.module('myApp', []);
myApp.controller('notificationController', ['$scope',
function($scope) {
$scope.buttonNotClicked = true;
$scope.fullArray = [item1, item2, item3, item4, item5];
$scope.partialArray = [fullArray[0], fullArray[1], fullArray[2]];
function onButtonClick() {
$scope.buttonNotClicked = false;
}
}
]);
<div ng-if="buttonNotClicked">
<div ng-repeat="data in partialArray"></div>
</div>
<div ng-if="!buttonNotClicked">
<div ng-repeat="data in fullArray"></div>
</div>
How to use parameters within the filter in AngularJS?
So the first step is that on the success callback you should save then returning data somewhere in your scope, like:
.success(function (data) {
$scope.myList = data;
$scope.myListLimit = 3; // lets also set a limit for our list
}
Then, you can write your view as descendant of the element that points to the ng-controller directive:
<div ng-controller="mainController">
<ul ng-repeat="item in myList | limitTo: myListLimit">
<li>{{item}}</li>
</ul>
<button type="button" ng-show="anyResultsPending()" ng-click="showMoreResults()">Show more results</button>
</div>
After that your list should be shown with 3 results, and to be able to show more as a result of the user interaction, we create a new method on the controller:
$scope.showMoreResults = function () {
$scope.myListLimit += 3;
}
// this is to hide the "show more results" button after the all items was shown
$scope.anyResultsPending = function () {
return $scope.myList && $scope.myListLimit < $scope.myList.length;
}
Here's a solution that does what some other answers don't provide:
Animation
Loads objects directly from your JSON using an angular factory and promises
Limits to 3 items and loads the rest on a button click
JavaScript:
(function () {
"use strict";
var module = angular.module('app', ['ngAnimate']);
module.factory('NotificationsService', ['$http', function ($http) {
return {
fetch: function () {
return $http.get('notifications.json').then(function (response) {
return response.data
});
}
};
}]);
module.controller('Controller', ['$scope', '$filter', 'NotificationsService', function ($scope, $filter, NotificationsService) {
$scope.notifications = [];
$scope.limit = 3;
$scope.allLoaded = false;
NotificationsService.fetch().then(function (data) {
$scope.notifications = data;
});
$scope.loadAll = function () {
$scope.limit = $scope.notifications.length;
$scope.allLoaded = true;
};
}]);
})();
Html/CSS
<!doctype html>
<html ng-app="app">
<head>
<title>ng-limit</title>
<style>
.my-repeat-animation.ng-enter,
.my-repeat-animation.ng-leave,
.my-repeat-animation.ng-move {
-webkit-transition: 0.5s linear all;
transition: 0.5s linear all;
position: relative;
}
.my-repeat-animation.ng-enter {
top: -20px;
opacity: 0;
}
.my-repeat-animation.ng-enter.ng-enter-active {
top: 0;
opacity: 1;
}
.my-repeat-animation.ng-leave {
top: 0;
opacity: 1;
}
.my-repeat-animation.ng-leave.ng-leave-active {
top: -20px;
opacity: 0;
}
.my-repeat-animation.ng-move {
opacity: 0.5;
}
.my-repeat-animation.ng-move.ng-move-active {
opacity: 1;
}
</style>
<script src="node_modules/angular/angular.js"></script>
<script src="node_modules/angular-animate/angular-animate.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="Controller">
<button ng-show="!allLoaded" ng-click="loadAll()">Load all</button>
<div ng-repeat="notification in notifications | limitTo:limit" class="my-repeat-animation">
<div>
<h4>Notification: {{$index+1}}</h4>
<div>
Avatar: {{notification.avatar}}
</div>
<div>
Type: {{notification.type}}
</div>
<div>
Name: {{notification.userName}}
</div>
<div>
Action: {{notification.userAction}}
</div>
<div>
Target: {{notification.targetObject}}
</div>
</div>
</div>
</body>
</html>
In addition, as explained in AngularJS : Insert HTML into view ,
when using HTML contained in JSON, you need to Sanitize. Use this module:
<script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
In your JS, I was surprised, it seems to be as simple as adding a dependency:
var ricksiteControllers = angular.module('ricksiteControllers', ["ngSanitize"]);
ricksiteControllers.controller('PagesCtrl', ['$scope', 'Page',
function($scope, Page) {
$scope.pages = Page.query();
}]);
And my services.js has the angular-resource code:
var ricksiteServices = angular.module('ricksiteServices', ['ngResource']);
ricksiteServices.factory('Page', ['$resource',
function($resource){
return $resource('pages/:pageId.json', {}, {
query: {method:'GET', params:{pageId:'pages'}, isArray:true}
});
}]);
// actually, the pageID parameter has only one value, only the default 'pages' is used.
I've got a solution. Try it in your plnkr.
Note how I've hardcoded $scope.notifications. You'll want to retrieve the actual data - can't figure out how to do it in plnkr. When you do retrieve the JSON, you will have to trust the data like so:
var app = angular.module('notifications', []);
app.controller('mainController', function($scope, $http, $sce) {
$http({
method: 'GET',
url: 'notifications.json'
}).success(function(data){
console.log('success');
$scope.notifications = data;
for (var i=0; i<$scope.notifications.length; i++){
$scope.notifications[i].userAction = $sce.trustAsHtml($scope.notifications[i].userAction)
}
}).error(function(data){
console.log('error');
});
$scope.myLimit = 3;
$scope.loadmore = true;
});
EDIT because perhaps explanations are due. Here are the changes I made:
Angular Module had an error (wrong name) so I changed the first line of JS.
$scope.notifications must be declared in the JS.
Added $scope.myLimit so we can modify this variable for limitTo
In ng-click, removed notifications = !notifications, added myLimit = notifications.length so it can show all results.
Finally, added ng-bind-html instead of {{notification.userAction}} so it can be displayed as HTML.
JS:
var app = angular.module('notifications', []);
app.controller('mainController', function($scope, $http) {
$http({
method: 'GET',
url: 'notifications.json'
}).success(function(){
console.log('success');
}).error(function(){
console.log('error');
});
$scope.notifications = [
{
"avatar" : "images/otherUser.png",
"type" : "",
"userName" : "Ivana Stankova",
"userAction" : "<span class=\"heart\">❤</span> your photo",
"targetObject" : "images/targetPhoto.jpg"
},
{
"avatar" : "images/otherUser2.png",
"type" : "",
"userName" : "Ivana Stankova",
"userAction" : "<span class=\"heart\">❤</span> your photo",
"targetObject" : "images/targetPhoto.jpg"
},
{
"avatar" : "images/otherUser4.png",
"type" : "checkedIn",
"userName" : "Dave Peters",
"userAction" : "Checked in<br/>962 Grant Street Victoria",
"targetObject" : "images/place.jpg"
},
{
"avatar" : "images/otherUser4.png",
"type" : "commented",
"userName" : "Dave Peters",
"userAction" : "Commented on your post<p>Hey guys,&nbsp8 o’clock? Let’s get some food first? How<br/>about that fancy restaurant we wanted to try for...</p>",
"targetObject" : "images/targetPhoto.jpg"
},
{
"avatar" : "images/otherUser.png",
"type" : "",
"userName" : "Ivana Stankova",
"userAction" : "<span class=\"heart\">❤</span> your photo",
"targetObject" : "images/targetPhoto.jpg"
},
{
"avatar" : "images/otherUser.png",
"type" : "",
"userName" : "Ivana Stankova",
"userAction" : "<span class=\"heart\">❤</span> your photo",
"targetObject" : "images/targetPhoto.jpg"
},
{
"avatar" : "images/otherUser4.png",
"type" : "",
"userName" : "Dave Peters",
"userAction" : "Made a new post.",
"targetObject" : "images/targetPhoto.jpg"
},
{
"avatar" : "images/otherUser.png",
"type" : "",
"userName" : "Ivana Stankova",
"userAction" : "Started following you.",
"targetObject" : ""
},
{
"avatar" : "images/fivePeople.png",
"type" : "",
"userName" : "",
"userAction" : "Five people Started following You.",
"targetObject" : ""
}
]
$scope.myLimit = 3;
$scope.loadmore = true;
});
Index.html
<!DOCTYPE html>
<html lang="en" ng-app="notifications">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>App</title>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body class="container" ng-controller="mainController">
<header>
<a id="logo" href="www.google.com"><img src="images/logo.png" alt="logo"></a>
<nav id="menu">
<ul>
<li><img src="images/bell.png" alt="bell icon"></li>
<li>
<a href="#">
<img src="images/message.png" alt="message icon">
<div id="nCount">22</div>
</a>
</li>
<li><img src="images/profilePic.png" alt="girl profile pic"></li>
</ul>
</nav>
</header>
<main>
<div class="wrapper">
<div id="list">
<h1>My notifications</h1>
<ul id="mainList" ng-show="notifications" class="slideInDown" ng-init="limit = myLimit">
<li ng-repeat="notification in notifications | limitTo: limit">
<figure>
<img src="{{notification.avatar}}" alt="other user photo">
</figure>
<div class="infoLine {{notification.type}}">
<a class="userName" href="#">{{notification.userName}}</a>
<span ng-bind-html="notification.userAction"></span>
</div>
<div class="whenWhat">
<span>
<img src="images/clock.png" alt="clock illustration">
2m
</span>
<img src="{{notification.targetObject}}" alt="photo">
</div>
</li>
</ul>
</div>
<a id="loadMore" href="#" ng-show="loadmore" ng-click=" loadmore = false ; limit = notifications.length; myLimit = notifications.length" ng-class="{ active: notifications }" >Load More</a>
</div>
</main>
</body>
</html>
First of all, you should use then() promisse instead success() and error(). This is the current way that promisses should be used.
You can limit your nrRepeat looping using ´limitTo=val´ in your ng-repeat attribute.
I made a snippet to show you a solution for your problem:
var myApp = angular.module('myApp', []);
myApp.controller('myAppController', ['$scope', function($scope){
$scope.defaultLimit = 3; // the default limit
$scope.list = ['item 1', 'item 2', 'item 3', 'item 4', 'item 5', 'item 6', 'item 7'];
$scope.showAll = function(){
$scope.defaultLimit = $scope.list.length; // show all reccords
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myAppController">
<ul>
<li ng-repeat="item in list | limitTo:defaultLimit">{{item}}</li>
</ul>
<button ng-click="showAll()" type="button">Show all</button>
</div>
</div>

Making a regular angular app, a modular angular app

Okay so I am trying to learn how to create a modular angular app, but I don't really know how it would look. Based on my code what would I need to do to make it modular? My app is pretty small but I still want to try and get the idea down as for how to create a modular app so that I can just do that from the beginning the next time I create a web app. I didn't include the css as it seems irrelevant for this question. Help would be greatly apprciated.
index.html
<!DOCTYPE html>
<html>
<head>
<header>To do App</header>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>To do App</title>
<script type='text/javascript' src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"> </script>
<script type='text/javascript' src="//use.edgefonts.net/vast-shadow:n4:all.js"></script>
<script type='text/javascript' src="//use.edgefonts.net/vast-shadow:n4:all;megrim.js"></script>
<script type='text/javascript' src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script type='text/javascript' src="js/index.js"></script>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div ng-app="demoApp">
<script type="text/ng-template" id="partials/edit-form.html">
<div ng-show="todo.editMode">
<input ng-model="todo.text" />
<button ng-click="save(todo)">save</button>
</div>
</script>
<div class="todo-wrapper" ng-controller="todoCtrl">
<h2>You have <span class="emphasis">{{getTotalTodos()}}</span> tasks</h2>
<input class="search-input" type="text" ng-model="searchText" placeholder="enter search term" />
<ul>
<li ng-repeat="todo in todos | filter: searchText">
<span>{{todo.text}}: {{todo.date_created}}</span>
<div ng-include="'partials/edit-form.html'"></div>
<button class="clear-btn" ng-click="removeTask(todo)">Remove</button>
<button class="clear-btn" ng-click="editTask(todo)">Edit</button>
</li>
</ul>
<form>
<input class="add-input" placeholder="task name" type="text" ng-model="text" ng-model-instant />
<button class="add-btn" ng-click="addTask()"><h2>Add</h2></button>
</form>
</div>
</body>
</html>
index.js
angular.module('demoApp', [])
.controller('todoCtrl', TodoCtrl);
function TodoCtrl($scope) {
$scope.todos = [{
id: 1,
text: 'Mow the lawn',
selected: false
}, {
id: 2,
text: 'Wash the car',
selected: false
}];
$scope.id = $scope.todos.length + 1; //later create an uuid
$scope.getTotalTodos = function () {
return $scope.todos.length;
};
$scope.addTask = function () {
$scope.todos.push({
editMode: false,
text: $scope.text,
id: $scope.id,
date_created: Date.now,
selected: false
});
$scope.text = '';
$scope.id = '';
};
$scope.removeTask = function (todo) {
/*$scope.todos = _.filter($scope.todos, function (todo) {
return !todo.selected;
});*/
$scope.todos.pop(todo);
//update server now with ngResource...
};
$scope.showDetails = function (task_id) {
var found = $filter('filter')($scope.todos, {
id: task_id
}, true);
if (found.length) {
$scope.selected = JSON.stringify(found[0]);
} else {
$scope.selected = 'Not found';
}
}
$scope.editTask = function(todo) {
todo.editMode = true;
console.log(todo);
};
$scope.save = function(todo) {
todo.editMode = false;
// update data at server now too. $scope.todos is up-to-date
}
$scope.updateTask = function (task_id) {
// search $scope.todos for the item to update
var indexOfTask;
for (var i = 0; i < $scope.todos.length; i++) {
if ($scope.todos[i].id === $scope.id) indexOfTask = i;
$scope.todos[i] = todo;
$scope.todos.push();
$scope.text = '';
$scope.id = '';
}
// update the todo
};
}
Essentially just make a new file for every angular whatever (factory, controller, directive, etc.)
I use this syntax
angular.module('myapp.functionName.type', [])
.type('functionName',);
Then in your app.js, in your case index.js
angular.module('myapp', ['myapp.functionName.type', ... ]) ;

angularJS why $scope.$watch cant't work?

I want to add a $watch to watch $scope.data is changed or not, but it cant't work
[http://jsbin.com/biqesojaqu/1/edit?html,js,console,output][1]
app.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-app="App" ng-controller="TestController">
<div>
<ul>
<li ng-repeat="item in data"> {{ item.name }}--{{ item.status }}</li>
<button ng-click="addOnePerson()">ADD</button>
</ul>
</div>
</body>
</html>
app.js
var App = angular.module("App", []);
App.controller('TestController', function ($scope) {
$scope.data = [
{name: 'cc', status: true},
{name: 'bob', status: false}
];
$scope.name = 'nataila';
$scope.addOnePerson = function () {
var personCount = $scope.data.length;
personCount += 1;
$scope.data.unshift({name: 'cc'+personCount, status: true});
};
$scope.$watch('data', function (){
console.log('has changed');
});
});
when I click the button, I think console will output has chenged, but it's nothing
please tell me Why, and it's Good to help me in JSbin
Use $watchCollection for an array or object
$scope.$watchCollection('data', function (){
console.log('has changed');
});
JSBin

Using a directive's compile function with an isolate scope seems to access the wrong scope

I'm not sure if this is an angularjs bug or if I am doing something wrong, but when i create a directive using an isolate scope as well as a compile function to programmatically modify the template, the wrong scope is used. What's interesting is that if I change the ng-repeat to item in users, which should not work as I am using an isolate scope...it actually works. Even more confusing...this code actually works correctly in angular 1.1.3. Anyone know whats going on?
Fiddles:
Not working in angular 1.2.3 - http://jsbin.com/iPIbubA/12/edit
Working in angular 1.1.3 as is - http://jsbin.com/iPIbubA/7/edit
Working in angular 1.2.3 with "item in users" http://jsbin.com/iPIbubA/15/edit
For example:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Demo Blog</title>
</head>
<body>
<div ng-app="app" class="container">
<h1>Blog Demo</h1>
<div class="row" ng-controller="main">
<collection items="users">
{{ $index + 1 }} - {{ item.name}} / {{ item.email }}
</collection>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
</body>
</html>
var app = angular.module('app', [ ]);
app.controller("main", function($scope) {
$scope.users = [
{ name : "bob", email : "bob#aol.com" },
{ name : "joe", email : "joe#aol.com" }
];
});
app.directive("collection", function() {
return {
restrict : "E",
scope : {
"items" : "="
},
compile : function(el, attrs) {
var itemTemplate = el.text();
// if I change ng-repeat to "item in users" it works!
el.html('<ul><li ng-repeat="item in items">' + itemTemplate + '</li></ul>');
}
};
});
Use transclude:
app.directive("collection", function() {
return {
restrict : "E",
scope : {
"items" : "="
},
template : '<ul><li ng-repeat="item in items">
<span ng-transclude></span></li></ul>',
transclude : true
};
});

Categories

Resources