Beginners issue with scope in Angularjs - javascript

I am working with angular at frontend and django as backend, writing smll stuff to learn angular.I am trying to display variable i defined in scope.I am loading the html as partial onclick of anchor tag.
Here is my app.js which have my router settings:
var blogApp = angular.module('blogApp',['ngRoute'])
blogApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{$');
$interpolateProvider.endSymbol('$}');
});
blogApp.config(['$routeProvider',function($routeProvider){
$routeProvider.
when('/login', {
templateUrl: 'static/partials/login.html',
}).
when('/register',{
templateUrl:'static/partials/register.html',
}).
when('/shoppingcart', {
templateUrl:'static/partials/shopping_cart.html',
controller:'CartController'
}).
otherwise({
redirectTo: '/'
});
}]);
Here is my CartController defined:
blogApp.controller('CartController', function($scope){
$scope.items = [
{title: 'Paint pots', quantity: 8, price: 3.95},
{title: 'Polka dots', quantity: 17, price: 12.95},
{title: 'Pebbles', quantity: 5, price: 6.95}
];
});
i have included the js which contains my CartController and the one that has defined my routing.My partial (called shopping_cart.html) is like this:
<div ng-controller='CartController'>
<h3>Your Cart</h3>
<div ng-repeat="item in items">
<span>{% item.title %}</span>
<input ng-model="item.quantity">
<span> {% item.price|currency %} </span>
<span> {% item.price * item.quantity | currency %} </span>
<button ng-click="remove($index)">Remove</button>
</div>
</div>
Intriguing problem is that item.quantity is displayed correctly but item.title, item.price etc is displayed as it is in my output.Point is if it displays item.quantity this implies that item variable is containing the variable defined, even if i put item.title in input in place of item.quantity it displays the variable, But {% item.title %} on its own never gets displayed.Plese point me to my mistake, and kindly explain why is this happening.

It looks like a typo.
In your config you override the start and end symbol for interpolation to '{$' and '$}', but in your template you use '{%' and '%}' instead. Replace the % with $ and it should work.

Related

Angular, connect objects and display right information

I'm creating an angular webapp, listing different cars in a sidebar and some information about the specific car in a informationbox.
The purpose is to show the right information in the box when clicking the different cars.
I have two different arrays(two API-endpoints), where the first array lists the car name, and the other one got the information about the car. But I have no idea how to connect the objects with the primary key and the foreign key, and how I'm supposed to output the right information after clicking the car.
app.js:
angular.module('myApp', [])
.controller('MyController', function($scope, $http) {
function fetch() {
$http({method : 'GET',url : 'http://*cars*'})
.success(function(data) {
$scope.cars = data;
});
$http({method : 'GET',url : 'http://*information*'})
.success(function(data) {
$scope.information = data;
});
}
fetch();
})
html:
<div id="sidebar">
<ul>
<li ng-repeat="name in cars">{{ name.displayName }}</li>
</ul>
</div>
For now all I have done is that I've fetched the data and outputed the cars in the sidebar. But now I've been googling and trying to connect the cars to the information with loops and functions for hours, but stil clueless.
Yes, I'm new to this. Any kind of help would be great! Thanks
You can deal with this with the ng-route. You can do something like :
In your route definition:
.when(/cars/:Id), {
name: 'cars',
templateUrl : 'ayourtemplate.html',
controller : 'yourCtrl'
})
In your html:
<div id="sidebar">
<ul>
<li ng-repeat="name in cars">{{ name.displayName }}</li>
</ul>
</div>
The Id will be your key tou will just have to match the right key in your $scope.information
It depends on what information those arrays contains.
If you're sure, that every element corresponds to other, you can just use $index in the html.
<li ng-repeat="name in cars">
{{ name.displayName }}
<p>{{ information[$index] }}</p>
</li>
However, if elements in array aren't ordered, you will have to check primary keys of objects in arrays. Let's assume, that data in arrays looks like this:
cars:
[
{ id: "1", name: "Carrera GT" },
{ id: "2", name: "DB 11" },
... and so on
]
information:
[
{ id: "2", info: "Lorem ipsum" },
{ id: "1", info: "Dolor sit amet" },
...
]
Then I'd suggest using loops and constructing new array using ids.
var carinfo = [];
cars.forEach(car => {
obj["id"] = car.id;
obj["name"] = car.name;
obj["info"] = ""; // Placeholder
info.forEach(c => {
if (c.id === car.id) {
obj["info"] = c.info;
}
});
carinfo.push(obj);
});
$scope.carInfo = carinfo;
Then you can use $scope.carInfo in the html file.
<li ng-repeat="car in carInfo">
{{ car.name }}
<p>{{ car.info }}</p>
</li>

Angular: How do I pass data between 2 views of a single page app?

This is my first attempt at Angular.js. I am attempting to create a single page app which load some JSON and displays a list. When I click a link in the list it should go to a new page and populate the page in greater detail via the id from the JSON data.
Any help is massively appreciated.
Here is the JavaScript (easyjet.js):
var app = angular.module('easyjet', ['ui.router']);
app.config(function($stateProvider, $urlRouterProvider){
$stateProvider.state('flights', {
url: '/',
templateUrl: 'templates/list.html',
controller: 'ResultsController'
})
.state('details', {
url: '/detail/:id',
templateUrl: 'templates/fulldetails.html',
controller: 'ResultsController'
});
$urlRouterProvider.otherwise('/');
});
app.controller('ResultsController', function($scope, $http, $stateParams) {
console.log($stateParams);
// Get JSON data
$http({
method : "GET",
url : "http://ejtestbed.herokuapp.com/flights"
}).then(function(response) {
$scope.resultsData = response.data;
}, function(response) {
$scope.resultsData = response.statusText;
console.log("Oops! Couldn't load JSON!");
});
// Select and display result details
$scope.selectedResult = null;
$scope.selectResult = function (result) {
$scope.selectedResult = result;
};
//Sorting default setting
$scope.order = "flightNumber.number";
});
Here is the default HTML page:
<!DOCTYPE html>
<html ng-app="easyjet">
<head>
<meta charset="utf-8" />
<title>Easyjet, Flights</title>
</head>
<body>
<div ui-view></div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="angular-ui-router.min.js"></script>
<script src="easyjet.js"></script>
</body>
</html>
The template files:
1. list.html
<div class="container">
<!-- Filtering & Sorting -->
<div>
<input type="text" id="search" ng-model="search.$" placeholder="Search anything..." />
<select ng-model="order">
<option value="flightNumber.number">Flight Number (ASC)</option>
<option value="-flightNumber.number">Flight Number (DEC)</option>
<option value="localDepartureTime">Date (ASC)</option>
<option value="-localDepartureTime">Date (DEC)</option>
</select>
</div>
<!-- Result List -->
<div class="result"
ng-repeat="result in filteredResults = (resultsData | filter: search | orderBy: order)"
ng-style="{ 'background-color': result.id == selectedResult.id ? 'lightgray' : '' }"
ng-click="selectResult(result)">
<span style="display:none;">{{ $index }}</span>
<a ng-href="#/detail/{{ result.id }}"><span>EZY {{ result.flightNumber.number }}</span></a>
<span>From: {{ result.departureAirport }}</span>
<span>To: {{ result.arrivalAirport }}</span>
<span>Date: {{ result.localDepartureTime | date:"longDate" }}</span>
</div>
<div ng-show="filteredResults.length == 0">No Result Found</div>
</div>
fulldetails.html
Flight Number: {{ selectedResult.flightNumber.carrierCode }} {{ selectedResult.flightNumber.number }}
From: {{ selectedResult.departureAirport }}
To: {{ selectedResult.arrivalAirport }}
Departure Terminal: {{ selectedResult.depTerminalName }}
Departure Time: {{ selectedResult.localDepartureTime | date:"longDate" }}
Arrival Time: {{ selectedResult.localArrivalTime | date:"longDate" }}
Seats Available: {{ selectedResult.seatsAvailable }}
Adult Fare: {{ selectedResult.prices.adult.value }}
Debit Card Booking Fee: {{ selectedResult.prices.adult.valueWithDebitCard }}
Credit Card Booking Fee: {{ selectedResult.prices.adult.valueWithCreditCard }}
Child Fare: {{ selectedResult.prices.child.value }}
Debit Card Booking Fee: {{ selectedResult.prices.child.valueWithDebitCard }}
Credit Card Booking Fee: {{ selectedResult.prices.child.valueWithCreditCard }}
You should use a service which will do the call and store the data between the pages.
Your controllers will call this service to get the data or ask to refresh it.
You can use angular service to share the variable with two ore more controllers
But here your problem is to share the values between views that having same controller.
Here when ever you are changing the view, the controller reloads. So you have to store that value in local storage or session storage (even though we can setup a service, better option is localStorage or sessionStorage).
For this you can use $localStorage or $sessionStorage dependencies.
If you are Using same controller then you can store your data in cache or local-storage other wise if you use service to store data and same controller it will break because service will also be reloaded and loose the data if you are not using same controller then you should definitely use service as best practice
How to work with cache? use $cacheFactory in controller
check on controller loaded
var yourCache = $cacheFactory.get('yourCache') || $cacheFactory('searchCache');
in your function after success just save to cache
yourCache.put(key, value);
and for removing it
yourCache.remove('key');

Cannot Get Upvoting to Work from Thinkster MEAN Stack Tutorial

I have been going through the this MEAN Stack tutorial at following along however I have changed my code to use Controller as rather than having $scope like they do in their code.
I am stuck on the enabling upvotes portion. When I click it does not increase the number of upvotes and I am not sure why this happening.
Can anyone help resolve this? Here is my code:
index.html
<html>
<head>
<title>My Angular App!</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="flapperNews" ng-controller="MainCtrl as main">
<div ng-repeat="post in main.posts | orderBy: '-upvotes'">
<span ng-click="incrementUpvotes(post)">^</span>
{{ post.title }} - upvotes: {{ post.upvotes }}
</div>
<form ng-submit="main.addPost()">
<input type="text" ng-model="main.title"></input>
<button type="submit">Add Post</button>
</form>
</body>
</html>
app.js
/*global angular*/
/*jslint white:true*/
angular
.module('flapperNews', [])
.controller('MainCtrl', function(){
'use strict';
var main = this;
main.posts = [
{title: 'post 1', upvotes: 5},
{title: 'post 2', upvotes: 2},
{title: 'post 3', upvotes: 15},
{title: 'post 4', upvotes: 9},
{title: 'post 5', upvotes: 4}
];
main.addPost = function(){
if(!main.title || main.title === '') {return;}
main.posts.push({title: main.title, upvotes: 0});
main.title = '';
};
main.incrementUpvotes = function(post) {
post.upvotes += 1;
};
});
The problem you're having is with ng-repeat. You need to change your code to $parent.incrementUpvotes(post) to make this work.
Here's why: ng-repeat creates a new child scope for each iteration, but you don't have full access to everything you might need. This is due to how angular copies properties into the child scope. In order to access the scope that actually contains a definition for incrementUpvotes (the controller scope), you need to move up into the parent scope first. Alternatively you could probably do main.incrementUpvotes(post) to accomplish the same thing since you're aliasing the controller.
You can see a more detailed explanation of what happens when angular creates a child scope, and why certain properties are not inherited here https://github.com/angular/angular.js/wiki/Understanding-Scopes
What happens is that the child scope gets its own property that
hides/shadows the parent property of the same name. This is not
something AngularJS is doing – this is how JavaScript prototypal
inheritance works. New AngularJS developers often do not realize that
ng-repeat, ng-switch, ng-view and ng-include all create new child
scopes, so the problem often shows up when these directives are
involved.
Just add main.incrementUpvotes(post) instead of incrementUpvotes(post).
angular
.module('flapperNews', [])
.controller('MainCtrl', function(){
'use strict';
var main = this;
main.posts = [
{title: 'post 1', upvotes: 5},
{title: 'post 2', upvotes: 2},
{title: 'post 3', upvotes: 15},
{title: 'post 4', upvotes: 9},
{title: 'post 5', upvotes: 4}
];
main.addPost = function(){
if(!main.title || main.title === '') {return;}
main.posts.push({title: main.title, upvotes: 0});
main.title = '';
};
main.incrementUpvotes = function(post) {
post.upvotes += 1;
};
});
<html>
<head>
<title>My Angular App!</title>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="flapperNews" ng-controller="MainCtrl as main">
<div ng-repeat="post in main.posts | orderBy: '-upvotes'">
<span ng-click="main.incrementUpvotes(post)">^</span>
{{ post.title }} - upvotes: {{ post.upvotes }}
</div>
<form ng-submit="main.addPost()">
<input type="text" ng-model="main.title"></input>
<button type="submit">Add Post</button>
</form>
</body>
</html>

AngularJS: I clearly do not understand the use of ng-init

I have an extremely hierarchical JSON structure as a scope variable in my AngularJS controller. I want to loop around different sections of that variable. I thought about using ng-init to specify where in the structure I am. Here is some code:
my_app.js:
(function() {
var app = angular.module("my_app");
app.controller("MyController", [ "$scope", function($scope) {
$scope.things = {
name: "a",
children: [
{
name: "a.a",
children: [
{ name: "a.a.a" },
{ name: "a.a.b" }
]
},
{
name: "a.b",
children: [
{ name: "a.b.a" },
{ name: "a.b.b" }
]
}
]
}
}]);
});
my_template.html:
<div ng-app="my_app" ng-controller="MyController">
<ul>
<li ng-init="current_thing=things.children[0]" ng-repeat="thing in current_thing.children>
{{ thing.name }}
</li>
</ul>
</div>
I would expect this to display a list:
a.a.a
a.a.b
But it displays nothing.
Of course, if I specify the loop explicitly (ng-repeat="thing in things.children[0].children") it works just fine. But that little snippet of template code will have to be run at various points in my application at various levels of "things."
(Just to make life complicated, I can get the current thing level using standard JavaScript or else via Django cleverness.)
Any ideas?
ng-init runs at a lower priority (450) than ng-repeat (1000). As a result, when placed on the same element, ng-repeat is compiled first meaning that the scope property created by ng-init won't be defined until after ng-repeat is executed.
As a result, if you want to use it in this manner, you'd need to place it on the parent element instead.
<div ng-app="my_app" ng-controller="MyController">
<ul ng-init="current_thing=things.children[0]">
<li ng-repeat="thing in current_thing.children>
{{ thing.name }}
</li>
</ul>
</div>

How to Return Distinct Values with ng-repeat (no duplicates)

I'm making a multipage form with AngularJS & UI Router. The first page gives the user option buttons to select which way they would like to filter through a list of movies (title, genre, or rating).
I'm using a switch statement to filter through what is shown on the second page. If the "title" button is clicked, then only titles are shown. If the user clicks "genre" then they will see the available genres, etc.
For the most part, my switch statement is working, however I run into errors when I try to show the available genres or ratings because I do not want duplicates. I tried using Angular's unique: 'genre', but when I add that, it returns nothing.
Any suggestions? Thanks!
My HTML:
<label>Available Movies</label>
<div class="form-group" ng-controller="formController">
<h3>What Button was clicked: </h3>
<div ng-switch on="Data.sortType">
<div ng-switch-when="title">Title</div>
<div ng-switch-when="genre">Genre</div>
<div ng-switch-when="rating">Rating</div>
</div>
<h4>Show results: </h4>
<div ng-switch on="Data.sortType">
<div ng-switch-when="title">
<div ng-repeat="movie in movies">{{movie.title}}</div>
</div>
<!-- Here's where I run into trouble: -->
<div ng-switch-when="genre">
<div ng-repeat="movie in movies | unique: 'genre'">{{movie.genre}}</div>
</div>
<div ng-switch-when="rating">
<div ng-repeat="movie in movies">{{movie.rating}}</div>
</div>
</div>
</div>
My JS:
angular.module('movieApp', ['ngAnimate', 'ui.router'])
.factory('Data', function() {
return { sortType: ''};
})
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
// route to show our basic form (/form)
.state('orderForm', {
url: '/orderForm',
templateUrl: 'orderForm.html',
controller: 'formController'
})
.state('orderForm.step1', {
url: '/step1',
templateUrl: 'step1.html'
})
.state('orderForm.step2', {
url: '/step2',
templateUrl: 'step2.html'
});
$urlRouterProvider.otherwise('/orderForm/step1');
})
.controller('formController', ['$scope', 'Data', function($scope, Data) {
$scope.formData = {};
$scope.Data = Data;
//Create lists of Drinks:
$scope.movies = [
{title:'Get Hard ', genre: 'Comedy', rating: 'R'},
{title:'Cinderella', genre: 'Romance', rating: 'PG'},
{title:'Avengers ', genre: 'Action', rating: 'PG13'},
{title:'Hot Pursuit', genre: 'Comedy', rating: 'PG13'},
{title:'Age of Adaline', genre: 'Romance', rating: 'PG13'},
{title:'Pitch Perfect 2', genre: 'Musical', rating: 'PG13'},
{title:'Longest Ride', genre: 'Romance', rating: 'PG13'},
{title:'Furious 7', genre: 'Action', rating: 'PG13'},
{title:'Home', genre: 'Adventure', rating: 'PG'},
{title:'Insurgent', genre: 'Action', rating: 'PG13'}
];
}]);
Try this:
<div ng-repeat="movie.genre for movie in movies | unique: 'genre'">{{movie.genre}}</div>
You will need to install some dependencies:
1. In your terminal, go to your project and run to install angular-ui-utils unique:
bower install angular-ui-utils#bower-unique
2. Require the unique.js file by adding this to your project below your angular script:
<!-- angular script -->
<script type="text/javascript" src="bower_components/angular/angular.js"></script>
<!-- unique script -->
<script type="text/javascript" src="bower_components/angular-ui-utils/unique.js"></script>
3. Add unique as a module to your app:
angular.module('myApp', ['ui.unique'])

Categories