AngularJS: how to split functionality into many controllers - javascript

I have a single controller in my application http://jsfiddle.net/HKF9h/:
<div ng-controller="MyCtrl">
All Items:
<ul>
<li ng-repeat="item in items">{{item }} -
like it
</li>
</ul>
Items you liked:
<ul>
<li ng-repeat="item in likedItems">{{item }}
</li>
</ul>
</div>
<script type="text/javascript">
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.items= ['foo', 'bar', 'z'];
$scope.likedItems = [];
$scope.like = function (item) {
if($scope.likedItems.indexOf(item) === -1) {
$scope.likedItems.push(item);
}
}
</script>
}
I would like move all 'favoriting' functionality into sep controller that can be reused later. Here's what i did, but repeater is not showing likedItems anymore. What did I do wrong? http://jsfiddle.net/MR8wz/
<div ng-controller="MyCtrl">
All Items:
<ul>
<li ng-repeat="item in items">{{item }} -
<a href=""
ng-controller="FavoriteCtrl"
ng-click="like(item)">like it</a>
</li>
</ul>
Items you liked:
<ul ng-controller="FavoriteCtrl">
<li ng-repeat="item in likedItems">{{item }} //this one does not trigger
</li>
</ul>
</div>
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.items= ['foo', 'bar', 'z'];
}
function FavoriteCtrl($scope) {
$scope.likedItems = [];
$scope.like = function (item) {
console.log('you liked', item); //this one is displayed
if($scope.likedItems.indexOf(item) === -1) {
$scope.likedItems.push(item);
}
}
}

You shouldn't really be relying on controller inheritance for sharing data. It's a bad case of close coupling.
In Angular, if you want to share data, you're advised to use services/factories because:
They're easily injectable anywhere you need them
They're easily mockable
They're easily testable
FIDDLE
var myApp = angular.module('myApp',[]);
myApp.factory('Favorite', function(){
var likedItems = [];
function like(item) {
console.log('you liked', item); //this one is displayed
if(likedItems.indexOf(item) === -1) {
likedItems.push(item);
}
}
function getLikedItems(){
return likedItems;
};
return {
like: like,
getLikedItems: getLikedItems
};
});
function MyCtrl($scope, Favorite) {
$scope.favorite = Favorite;
$scope.items = ['foo', 'bar', 'z'];
}
function FavoriteCtrl($scope, Favorite) {
$scope.likedItems = Favorite.getLikedItems();
}
<div ng-controller="MyCtrl">
All Items:
<ul>
<li ng-repeat="item in items">{{item }} -
<a href=""
ng-controller="FavoriteCtrl"
ng-click="favorite.like(item)">like it</a>
</li>
</ul>
Items you liked:
<ul ng-controller="FavoriteCtrl">
<li ng-repeat="item in likedItems">{{item }}
</li>
</ul>
</div>

You use ng-controller directive two times
like it
and
<ul ng-controller="FavoriteCtrl">
It creates two different scope this is the reason it doesn't work. So if you move
$scope.likedItems = []; in parent controller MyCtrl then it works. Updated
fiddle

Related

Syntax Error: Token '{' Invalid Key - Errors even when displaying correctly

I'm struggling here (I am new to programming still), I have an ng-repeat that is repeating a list (page on Pages) which displays the correct number on the front end (i.e. 1) but, when I try and use the same for the ng-click, it will throw this error:
Error: [$parse:syntax] Syntax Error: Token '{' invalid key at column 31 of the expression [getCtrlScope().currentPage = {{ page }}] starting at [{ page }}].
What is strange is that it will display it correctly within the HTML code correctly but will not function.
Code:
<li class="page-item" ng-repeat="page in Pages">
<a class="page-link" ng-click="getCtrlScope().currentPage = {{ page }}">{{ page }}</a>
</li>
What shows when rendered in HTML:
<li class="page-item ng-scope" ng-repeat="page in Pages">
<a class="page-link waves-effect ng-binding" ng-click="getCtrlScope().currentPage = 2">2</a>
</li>
JS
var MyApp = angular.module('MyApp', []);
MyApp.controller('MyCtrl', function ($scope, $filter) {
$scope.Users = tJSONData;
$scope.currentPage = 0;
$scope.pageSize = 3;
$scope.search = '';
$scope.Pages = [1, 2, 3];
$scope.getData = function () {
return $filter('filter')($scope.Users, $scope.search)
}
$scope.updatePage = function() {
$scope.$apply(function () {
$scope.Users = tJSONData
})
}
$scope.numberOfPages = function () {
return Math.ceil($scope.getData().length / $scope.pageSize);
}
$scope.getCtrlScope = function () {
return $scope;
}
});
MyApp.filter('startFrom', function () {
return function (input, start) {
start = +start; //parse to int
return input.slice(start);
}
});
I've looked around but pretty much all the fixes have been by removing {{ }}, some it has worked for because it was rendering incorrectly but it seems mine is rendering correctly.
If anyone has any ideas, it'd be a great help :)
You can access scope variables directly from view so you don't need getCtrlScope() function. Also all variables inside angular directives are scopes so you don't need brackets {{}}
Try this
<li class="page-item" ng-repeat="page in Pages">
<a class="page-link" ng-click="currentPage = page">{{ page }}</a>
</li>

How do I display this json file correctly in ng-repeat?

I'm trying to create a Store page for my website. I want to display the following json file in ng-repeat:
{
"cupcakes": {
"Vanilla": [
{"price":"9.99", "stock":"20", "amount":"8",
"ingredients":"flour, sugar, vanilla extract",
"popular":true}
],
"Maple": [
{"price":"9.99", "stock":"15", "amount":"8",
"ingredients":"flour, sugar, maple syrup",
"popular":true}
]
},
"cookies": {
"Vanilla": [
{"price":"7.99", "stock":"50", "amount":"12", "ingredients":"flour, sugar, vanilla extract"}
],
"Maple": [
{"price":"7.99", "stock":"50", "amount":"12", "ingredients":"flour, sugar, maple syrup"}
]
}
}
I want to display the type item (cupcake or cookie) then the type of each item (vanilla or maple) then the unique attributes and be able to filter and search through them. I'm thinking I may have to restructure my json file, but I'm not sure. Should I sort out the items ahead of time in my controller with a forEach? Here is my controller:
angular.module('root', [])
.controller("index", ['$scope', '$http', function ($scope, $http) {
$scope.name = null;
$scope.email = null;
$scope.comments = null;
$scope.reason = null;
$scope.employees = [];
$scope.items = [];
$http.get('http://localhost:8000/employees.json').then(function(result) {
$scope.employees = result.data.records;
});
$http.get('http://localhost:8000/inventory.json').then(function(result) {
$scope.items = result.data;
});
$scope.isEnabled = function() {
if ($scope.name && $scope.email && $scope.comments) {
return false;
}
else {
return true;
}
}
}])
In Javascript, almost everything is an array. You can iterate over an array of objects or, in an object you are iterating over his properties.
The following script should do the work.
<div ng-repeat="(itemLabel, itemValue)in items">
{{itemLabel}}
<div ng-repeat="(flavorLabel, flavorValue)in itemValue">
{{flavorLabel}}
<div ng-repeat="object in flavorValue">
<div ng-repeat="property in object">
{{property}}
</div>
</div>
</div>
</div>
jsfiddle
Here is a way for showing your data using ng-repeat:
<div ng-repeat="(foodKey, foodVal) in foods">
<b>{{foodKey}}</b>
<div ng-repeat="(typesKey, typesVal) in foodVal">
<span>{{typesKey}}</span>
<ul ng-repeat="types in typesVal">
<li ng-repeat="(key, val) in types">
{{key}} : {{val}}
</li>
</ul>
</div>
</div>
Of course it will only work if foods is the json you posted in your answer.
Here is a working jsFiddle.
For more information on ng-repeat see here.
Just going through your JSON.
<div ng-repeat="(key, value) in inventory">{{key}}
<ul>
<li ng-repeat="(material_key, material_value) in value">{{material_key}}
<ul>
<li ng-repeat="(options_key, options_value) in material_value[0]">{{options_key}} - {{options_value}}</li>
</ul>
</li>
</ul>
<br>
Have a look at this plunker. I hope this will solve you problem.
you need multiple ng-repeaters hope this plunker is your requirement
you can do anything with the keys of your JSON in javascript , by converting the JSON to object by
JSON.parse(json);
later on you can access any key and that will be an object/hashmap to loop or sort
So in your case, your API result can be parsed like
var resultData = JSON.parse(result.data);
Later you can do ng-repeat in your view some thing like
[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'

Angular js + ng-repeat + alphanumeric index not working

I have created one Nav Controller in Angular JS as mentioned below.
weatherApp.controller('navCtrl', ["$scope", "$localStorage", function($scope, $localStorage){
if($localStorage.user_email){
var navItems = new Array();
navItems["/"] = 'Home';
navItems["/logout"] = 'Logout';
$scope.navItems = navItems;
}
else{
var navItems = new Array();
navItems["/"] = 'Home';
navItems["/login"] = 'Login';
$scope.navItems = navItems;
}
$scope.test = "test";
}]);
I am calling this controller in index.html as shown below.
<ul class="nav navbar-nav" ng-controller="navCtrl">
<li ng-repeat="(url, navItem) in navItems">
{{ navItem }}
</li>
</ul>
if I keep navItems indexes alphanumeric then it does not load values but if I keep its indexes numeric, it show menu items.
Is there any way by which I can use alphanumeric indexes in ng-repeat?
I think you need a diferent data structure. Something like this:
https://jsfiddle.net/relferreira/3aexpfk5/
JS:
angular.module('app', []);
angular.module('app')
.controller('MainController', mainController);
mainController.$inject = ['$scope'];
function mainController($scope){
var vm = this;
vm.navItems= [
{ path: '/', name: 'Home'},
{ path: '/login', name: 'Login'},
];
}
HTML:
<div data-ng-app="app">
<div data-ng-controller="MainController as mainVm">
<ul class="nav navbar-nav">
<li ng-repeat="navItem in mainVm.navItems">
{{ navItem.name }}
</li>
</ul>
</div>
</div>
Your syntax for ng-repeat is correct.
However, do not use Array. Initialize your objects as:
var navItems = {};
As a side note, keeping user email in LocalStorage sounds like a very unusual thing (if this is a client-server app), but of course this ultimately depends on your use case.

Hide or Display an Item with Angular or Jquery?

i am using mvc. I have model and i take data from model to view with this code:
<ul>
<li id="geri"><<</li>
#foreach (var item in Model.Skills)
{
<li id="#String.Format("{0}{1}", "skill", item.SkillId)">
#item.SkillName
</li>
}
<li id="ileri" style="margin-right: 0;">>></li>
</ul>
After first 4 items, they should be hidden (display:none). I searched angular and find ng-show attribute but cannot find how to use. Now my website looks like:
It should be one line and when i pressed next button, first item will hide and 5th item will show.
I hope i can explain myself, thanks
In Angular, try to use limitTo and offset filters.
Here's the Jsfiddle link.
AngularJS sample codes:
HTML:
<div ng-app="myApp">
<ul ng-controller="YourCtrl">
<li ng-click="previousSkills()"><<</li>
<li ng-repeat="skill in skills | offset: currentPage * 4 | limitTo: 4">
{{skill.SkillName}}
</li>
<li ng-click="nextSkills()">>></li>
</ul>
</div>
AngularJS Controller:
'use strict';
var app = angular.module('myApp', []);
app.controller('YourCtrl', ['$scope', function ($scope) {
$scope.currentPage = 0;
$scope.skills = [
{SkillName:'C#'},
{SkillName:'MVC'},
{SkillName:'Web Forms'},
{SkillName:'Web API'},
{SkillName:'SignalR'},
{SkillName:'EF'},
{SkillName:'Linq'},
{SkillName:'Github'},
{SkillName:'Html'},
{SkillName:'CSS'},
{SkillName:'SQL'},
{SkillName:'Angular'},
{SkillName:'Azure'}
];
$scope.previousSkills = function() {
$scope.currentPage = $scope.currentPage - 1;
};
$scope.nextSkills = function() {
$scope.currentPage = $scope.currentPage + 1;
};
}]);
app.filter('offset', function() {
return function(input, start) {
start = parseInt(start, 10);
return input.slice(start);
};
});
Hope it helps.
In Angular your HTML should be something like this to display only the first 4 items <li>, where items is your $scope.items:
<ul>
<li id="geri"><<</li>
<li ng-repeat="(key, item) in items" ng-show="key <= 3">{{item.SkillName}}</li>
<li id="ileri" style="margin-right: 0;">>></li>
</ul>
JSFiddle here

onclick="AuthorityCheck({{meeting.people}})", expected identifier, string or number

I am having trouble with sending {{meeting.people}} value to JS function. Code is following
Error message says: Expected identifier, string or number. {{meeting.people}} is plain text.
Link:
<li class=" seriesblock ms-ContentAccent1-bgColor" ng-repeat="meeting in meetings | SeriesTitleFilter:filterString" >
<a id="meetingItem_{{meeting.id}}" onclick="AuthorityCheck({{meeting.people}})" href="Meeting.aspx?seriesId={{meeting.id}}"></a>
</li>
JS function:
function AuthorityCheck(people){
var ppl = people;
}
Note, that meetingItem_{{meeting.id}} works correctly.
Any ideas, how could I fix this problem?
Angular doesn't like mixing html event handlers with templates. You will need to use ng-click which calls a $scope method aliasing AuthorityCheck,. similar to this answer:
function AuthorityCheck(p) {
alert(p);
event.returnValue = false;
}
var meetingApp = angular.module('meetingApp', []);
meetingApp.controller('meetingCtrl', function ($scope) {
$scope.meetings = [
{ 'id': 1, people: 'fred' },
{ 'id': 2, people: 'bob' }
];
// set the alias as a scope method
$scope.meetingClick = AuthorityCheck;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<div ng-app="meetingApp">
<div ng-controller="meetingCtrl">
<ul>
<li class="seriesblock ms-ContentAccent1-bgColor" ng-repeat="meeting in meetings">
<a id="meetingItem_{{meeting.id}}" ng-click="meetingClick(meeting.people)" href="Meeting.aspx?seriesId={{meeting.id}}">{{meeting.people}}</a>
</li>
</ul>
</div>
</div>

Categories

Resources