Binding a single angular expression out of a json-file - javascript

I am pretty new to angularJS, and obviously there are some simple things that i do not yet understand. What i want to do is the following:
I've got a de-DE.json (that e.g. has several language-keys for a planned multi-language site) that looks somewhat like this
{
"index": {
"headline": "The title of that view",
"tabmenu": [
{
"id": "home",
"class": "active in",
"title":"Title No. 1",
"description":"Some description"
},
{
"id": "profile",
"class": "",
"title":"Title No. 2",
"banner":"WallBanner.jpg",
"description":"Some description"
},
{
"id": "messages",
"class": "",
"title":"Title No. 3",
"description":"Some description"
},
{
"id": "settings",
"class": "",
"title":"Title No. 4",
"description":"Some description"
}
]
},
"media": {
...
}
}
Next have a look at my index.html that looks like:
<html ng-app id="ng-app">
<head>
<title>Title of the Site</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="../assets/bootstrap/css/bootstrap.min.css">
</head>
<body ng-controller="languageKey">
<div class="container" ng-model="language.index">
<h1>{{ headline }}</h1>
<div class="row">
<div class="col-lg-12">
<ul class="nav nav-tabs" id="myTab">
<li class="active">Tab1</li>
<li>Tab2</li>
<li>Tab3</li>
<li>Tab4</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade {{ lg.class }}" id="{{ lg.id }}" ng-repeat="lg in language.index.tabmenu">
<h3>{{ lg.title }}</h3>
<p>{{ lg.description }}</p>
</div>
</div>
</div>
</div>
</div>
<script src="../assets/jquery/jquery.js"></script>
<script src="../assets/bootstrap/js/bootstrap.min.js"></script>
<script src="../assets/angularjs/angular.min.js"></script>
<script>
$(function () {
$('#myTab a:first').tab('show')
})
function languageKey($scope, $http)
{
$http({method: 'POST', url: 'de-DE.json'}).success(function(data)
{
$scope.language = data; //response Data
});
}
</script>
</body>
</html>
So thanks to some google-knownledge the part with the <div ng-repeat="lg in language.index.tabmenu"> works fine.
But much more common are language-keys that are just used once, without repeating html structure like in the above
<h1>{{ headline }}</h1>
(I've also tried <h1 ng-bind="{headline}"
So is there a leightweight way to just call those expressions?
Obviously it doesn't work if i try ng-model="language.index" in that case.

You just have to set your JSON object in your $scope element inside your controller, and you can use it in the view:
$scope.myJSON = {...};
In HTML
<h1>{{myJSON.index.headline }}</h1>
By the way, if you're implementing a multi-language application take a look at angular-translate.

If this works
<div ng-repeat="lg in language.index.tabmenu">
then as long as you load the parsed JSON object into the $scope the same way, this should output the headline:
<h1>{{ language.index.headline }}</h1>
The docs suggest that ng-model only works with inputs:
ngModel directive binds an input,select, textarea (or custom form control) to a property on the scope

Related

Why won't my AngularJS Controller take control on state change

I'm in the very beginning stages of learning AngularJS. I have a couple of years of Webdev experience (JS, HTML, etc.) so JS is not new to me.
I'm going through the tutorial at https://thinkster.io/tutorials/mean-stack. I'm still on the front-end part. I haven't even reached the Node/Express/Mongodb topics yet! It's pretty great but the ability to see the full work-in-progress requires paying for a membership which I don't feel is necessary at this point. If I had that I might be able to see what's different about my code but, alas.
As I usually do, I've changed a few semantic details from the tutorial (e.g. "persons" instead of "posts") but they shouldn't alter the functionality of the page fundamentally.
The trouble is, I can't get my controller to "take control" as it were, in its various states. The tutorial uses ui-router. I'm under the impression there are other alternatives for routing (e.g. "ngrouter?") and that is all well and good but I would like to get this work just on principle.
Here is the code:
app.js
var moduleApp = angular.module('flapperNews', ["ui.router"]);
moduleApp.config([
"$stateProvider",
"$urlRouterProvider",
function($stateProvider, $urlRouterProvider)
{
$stateProvider
.state("home",
{
"url": "/home",
"templateUrl": "/home.html",
"controller": "MainCtrl"
});
$stateProvider
.state("friends",
{
"url": "/friends/{id}",
"templateUrl": "/friends.html",
"controller": "FriendsCtrl"
});
$urlRouterProvider.otherwise("home");
}
]);
moduleApp.factory("persons", [
function(){
return {
"persons": [
{
"name": "John",
"age": 31,
"friends": [
{"name": "Brynn", "relationship": "Wife"},
{"name": "Becca", "relationship": "Sister"}
],
"homePage": "http://www.google.com"
},
{
"name": "Brynn",
"age": 30,
"friends": [
{"name": "John", "relationship": "Husband"},
{"name": "Becca", "relationship": "Sister-in-Law"}
]
},
{
"name": "Becca",
"age": 26,
"friends": [
{"name": "John", "relationship": "Brother"},
{"name": "Becca", "relationship": "Sister-in-Law"}
]
}
]
};
}
]);
controllers.js
moduleApp.controller("MainCtrl", [
"$scope",
"persons",
function($scope, persons) {
the_scope = $scope;
$scope.test = 'Hello world!';
$scope.persons = persons.persons;
$scope.addPerson = function() {
if (typeof $scope.name !== "undefined" && $scope.name !== "" &&
typeof $scope.age !== "undefined" && $scope.age !== "" &&
!isNaN($scope.age))
{
$scope.persons.push({"name": $scope.name, "age": $scope.age, "homePage": $scope.homePage});
$scope.name = "";
$scope.age = "";
$scope.homePage = "";
}
};
$scope.getOlder = function(post) {
post.age++;
};
$scope.getYounger = function(post) {
post.age--;
};
}
]);
moduleApp.controller("FriendsCtrl", [
"$scope",
"$stateParams",
"persons",
function($scope, $stateParams, persons)
{
$scope.friends = persons.persons[$stateParams.id];
}
]);
index.html
<html>
<head>
<title>My Angular App!</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.2/angular-ui-router.js"></script>
<script src="app.js"></script>
<script src="controllers.js"></script>
</head>
<body ng-app="flapperNews"><!-- ng-controller="MainCtrl"-->
<div class="row">
<div class="col-md-6 col-md-offset-3">
<ui-view></ui-view>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-1">
<form ng-submit="addPerson();">
<label for="name">Name</label>
<input id="name" type="text" ng-model="name">
<br />
<label for="age">Age</label>
<input id="age" type="text" ng-model="age">
<br />
<label for="homePage">Home Page</label>
<input id="homePage" type="text" ng-model="homePage">
<br />
<button class="btn btn-primary" type="submit">Add Person</button>
</form>
<div>
{{ test }}
</div>
<div ng-repeat="person in persons | orderBy: '-age'">
<span ng-click="getYounger(person);">-</span>
<span ng-click="getOlder(person);">+</span>
<a ng-show="person.homePage" href="{{person.homePage}}">{{ person.name }}</a>
<span ng-hide="person.homePage">{{ person.name }}</span>
-
Age: {{ person.age }}
<span>
Friends
</span>
</div>
</div>
</div>
<script type="text/ng-template" id="/home.html">
<div class="page-header">
<h1>Flapper News</h1>
</div>
</script>
<script type="text/ng-template" id="/friends.html">
<div class="page-header">
<h3>
<a ng-show="person.homePage" href="{{person.homePage}}">{{ person.name }}</a>
<span ng-hide="person.homePage">{{ person.name }}</span>
</h3>
</div>
<div ng-repeat="friend in person.friends | orderBy: '-age'">
<span class="glyphicon glyphicon-thumbs-up" ng-click="getOlder"></span>
{{ friend.name }} - {{ friend.relationship }}
</div>
</script>
</body>
</html>
Here is a functional jsbin to peruse. When you click one of the "Friends" button it initiates a state change. Note the jsbin combines the two js files.
Even with the initial state ("/home") the controller is not working though. The tutorial specifically says you shouldn't need the "ng-controller" directive in "body" tag so that shouldn't be the problem. Incidentally, when I do have the "ng-controller" directive in the "body" tag the controller does appear to work which suggests the controller definition is sound.
I'm sure I'm missing some small crucial semantic error but I've been working on this for days (in what little time I've had outside of taking care of our infant) and can't figure it out. I'd really like to move on in the tutorial but I feel like I'm probably missing something important.
To pre-empt the community members who are fond of drinking the haterade:
I don't know anything.
I'm a terrible programmer.
I'm barely qualified to use a toaster.
Please ignore this post's pathetic usage of SO server storage.
Now that that's been said... anyone who is still reading and actually feels like throwing an Angular noob a bone please help.
Thanks!

Unable to add or remove elements from an array in AngularJS

Hello: im gettin my feet wet in AngularJS.
I am trying to add and remove elements in an array; however, im unable to accomplish the desired effect.
the foll is the JSON structure:
$scope.masters = [
{
"name": "Wittgenstein",
"thoughts": ["thought 1", "thought 2", "thought 3"]
},
{
"name": "King",
"thoughts": ["thought 1", "thought 2", "thought 3"]
}
];
the foll is the plunker.
Plunker
any input is greatly appreciated. thank you.
As #Mathew suggested, you should introduce the usage of $index as follows:
JS code:
$scope.input = [];
$scope.add = function(i) { // receiving index as param
$scope.masters[i].thoughts.push($scope.input[i]);
$scope.input[i] = '';
};
HTML code:
<div ng-repeat="master in masters">
<h1>{{ master.name }}</h1>
<ul>
<li ng-repeat="thought in master.thoughts">
{{ thought }} <button ng-click="remove($index)">Remove</button>
</li>
</ul>
<input type="text" ng-model="input[$index]">
<button type="submit" ng-click="add($index)">Add</button>
</div>
See this Plunker working example
You need to specify the index of masters. Something like this should work in the remove function:
$scope.masters[masterIndex].thoughts.splice(index, 1);
where masterIndex is the index of the master that you want to remove a thought from
the problem is that you need pass to the add and remove functions the index of the masters. Because you do this:
$scope.masters.thoughts.splice(index, 1);
But masters is an Array, therefore you need to select one object inside that Array, for example
$scope.remove = function(masterIndex, thoughtsIndex) {
$scope.masters[masterIndex].thoughts.splice(thoughtsIndex, 1);
};
For this to work in your HTML you have to use the parent index inside the inner ng-repeat
<div ng-repeat="master in masters">
<h1>{{ master.name }}</h1>
<ul>
<li ng-repeat="thought in master.thoughts">
{{ thought }}
<button ng-click="remove($patent.index, $index)">
Remove
</button>
</li>
</ul>
<input type="text" ng-model="input">
<button type="submit" ng-click="add($index)">Add</button>
</div>
In the Add function you must do the same to recibe the $index of the masters array.
This is an updated version of your plnkr.
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.4.0" data-semver="1.4.0" src="https://code.angularjs.org/1.4.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="demoController">
<div ng-repeat="master in masters track by $index">
<h1>{{ master.name }}</h1>
<ul>
<li ng-repeat="thought in master.thoughts track by $index">
{{ thought }} <button ng-click="remove(master, $index)">Remove</button>
</li>
</ul>
<input type="text" ng-model="input[$index]">
<button type="submit" ng-click="add($index)">Add</button>
</div>
</body>
</html>
Notice how I included the current master as an argument to the remove function. This ensures that the correct master is spliced since JavaScript arrays are passed by reference. Below is the updated angular controller.
var app = angular.module('app', []);
app.controller('demoController', ['$scope', function ($scope) {
$scope.input = [];
$scope.masters = [ {
"name": "Wittgenstein",
"thoughts": ["thought 1", "thought 2", "thought 3"]
}, {
"name": "King",
"thoughts": ["thought 1", "thought 2", "thought 3"]
}];
$scope.add = function(index) {
$scope.masters[index].thoughts.push($scope.input[index]);
$scope.input[index] = '';
};
$scope.remove = function(master, index) {
master.thoughts.splice(index, 1);
};
}]);
The add function seems not to be picking the value from the input model, but then that should be a binding issue, not the function not working as it should.
Working Plunkr
I hope this helps.

AngularJS Filter by ID

I have the following Json_encode FRom PHP Response...
[
{"ID":"149","IDusr":"4","aut_more_info":"good","doc_name":"img1838142879.jpeg","doc_type":"jpg"},{"ID":"149","IDusr":"4","aut_more_info":"good","img5733250433.jpeg","doc_type":"jpg"},{"ID":"149","IDusr":"4","aut_more_info":"good","doc_name":"img1230306801.jpg_doc","doc_type":"jpg"}
]
And I have tried the https://github.com/a8m/angular-filter Plugin. Here is my code.
<div ng-repeat="(key, value) in Detail | groupBy: 'ID'">
<div ng-repeat="aut in value">
<div class="item item-avatar bar bar-calm">
<h2>{{aut.name}} {{aut.model}}</h2>
</div>
<div class="item item-avatar">
<img src="../www/img/icon/{{aut.doc_name}}">
</div>
</div>
</div>
AS you see from the Array, I have the same ID but the doc_name is different, Means I want to show the ID once but the three Images should be shown as well. How can I achieve that?
Thanks in advance...
You have an error in your JSON whereby you are missing a property name.
Plunker.
angular.module('foo', ['angular.filter'])
.controller('bar', ['$scope', function($scope){
$scope.Detail = [
{
"ID" : "149",
"IDusr" : "4",
"aut_more_info" : "good",
"doc_name" : "img1838142879.jpeg",
"doc_type" : "jpg"
},
{
"ID" :"149",
"IDusr" :"4",
"aut_more_info" :"good",
// you were missing the property name 'doc_name' here...
"doc_name" :"img5733250433.jpeg",
"doc_type" :"jpg"
},
{
"ID" :"149",
"IDusr" :"4",
"aut_more_info" :"good",
"doc_name" :"img1230306801.jpg_doc",
"doc_type" :"jpg"
}
];
}]);
Also you seem to be trying to render values for properties that don't exist in your JSON data
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body ng-app="foo">
<div ng-controller="bar">
<div ng-repeat="(key, value) in Detail | groupBy: 'ID'">
<div ng-repeat="aut in value">
<div class="item item-avatar bar bar-calm">
<!-- you don't have properties name or model in your json -->
<h2>{{aut.name}} {{aut.model}}</h2>
</div>
<div class="item item-avatar">
<img src="../www/img/icon/{{aut.doc_name}}">
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script>
<script src="script.js"></script>
</body>
</html>
I think there's a conceptual error in your JSON (apart the one noticed by #A.Alger).
I like the uniqueness of an ID. For instance, in a simple database table your ID (primary key) is showed exactly one time for each select. If you make some join operations that ID can be present more than one time. This is fine, since database table (and select) lack the expressivity that JSON indeed have, so I will return a json like this:
[{
"ID": "149",
"IDusr": "4",
"aut_more_info": "good",
"docs": [{
"doc_name": "img1838142879.jpeg",
"doc_type": "jpg"
}, {
"doc_name": "img5733250433.jpeg",
"doc_type": "jpg"
}, {
"doc_name": "img1230306801.jpg",
"doc_type": "jpg"
}]
}]
In this way the documents are naturally grouped together, and you can use your angular without any plugin:
<div ng-repeat="user in users">
ID: {{user.ID}} - IDusr: {{user.IDusr}}
<h5>Images</h5>
<div ng-repeat="doc in user.docs">
{{doc.doc_name}} <br>
{{doc.doc_type}}
<hr>
</div>
</div>
This is a plunker that show how it works.

Angular ui-tree and accept callback for restricting nodes?

I'm using : https://github.com/angular-ui-tree/angular-ui-tree
I want to accept:
Categories to root scope of ui-tree
apps to the apps of same categories.
My controller is (partial):
//Accept Categories at root scope and accept apps only inside same category
$scope.options = {
accept: function(sourceNodeScope, destNodesScope, destIndex) {
//todo check nodes and return
alert('called');
$log.debug("sourceNodeScope");
$log.debug(sourceNodeScope);
$log.debug("destNodesScope");
$log.debug(destNodesScope);
return false;
},
dropped: function(event) {
},
beforeDrop: function(event) {
}
};
My HTML is:
<div ng-controller="CpoTreeViewCtrl">
<div>
<script type="text/ng-template" id="apps_renderer.html">
<div ui-tree-handle>
{{app.name}}
</div>
</script>
<script type="text/ng-template" id="category_renderer.html">
<div ui-tree-handle >
{{category.name}}
</div>
<ol ui-tree-nodes ng-model="category.apps">
<li ng-repeat="app in category.apps" ui-tree-node ng-include="'apps_renderer.html'">
</li>
</ol>
</script>
<div ui-tree="options">
<ol ui-tree-nodes ng-model="treeData" id="tree-root">
<li ng-repeat="category in treeData" ui-tree-node ng-include="'category_renderer.html'"></li>
</ol>
</div>
</div>
</div>
I want to accept:
Categories to root scope of ui-tree
apps to the apps of same categories.
The accept callback is not getting fired. What's not right here?
Thanks!
If anyone is wondering how to restrict by groups, here's how I got it working. The docs leave a bit to be desired on how to do this.
here is the html markup
<div ng-repeat="list in lists" >
<div ui-tree="treeOptions" class="col-xs-6" >
<ol ui-tree-nodes ng-model="list.categories" data-type="{{list.type}}">
<li ng-repeat="item in list.categories" ui-tree-node data-type="{{item.type}}">
<div ui-tree-handle class="tree-node">
<div class="tree-node-content">
{{item.name}}
</div>
</div>
</li>
</ol>
</div>
<div class="clearfix" ng-if="::$index===1"></div>
</div>
for a sample item array such as
$scope.lists = [{
"name": "Selected Charts",
"type": "charts",
"categories": [{
"name": "222 docs",
"type": "charts"
}]
}, {
"name": "sdf",
"type": "tables",
"categories": [{
"name": "2222 docs",
"type": "tables"
}]
}];
then in tree options, define
$scope.treeOptions = {
accept: function(sourceNodeScope, destNodesScope, destIndex) {
var srctype = sourceNodeScope.$element.attr('data-type');
var dsttype = destNodesScope.$element.attr('data-type');
if(srctype===dsttype){
return true;
}else{
return false;
}
}
};
That will prevent non-matching types from dropping.
The API of this awesome package is updated and same was not available in doc/demo.
Details : https://github.com/angular-ui-tree/angular-ui-tree/pull/281
Quick Fix :
<div ui-tree="options">
should be replaced with
<div ui-tree callbacks="options">
Update (with thanks to #zach-l)
After v2.8.0 you need to switch back to
<div ui-tree="options">
I would do this.
accept: function(sourceNodeScope, destNodesScope) {
return sourceNodeScope.$parent.$id === destNodesScope.$id;
}

AngularJS: Error: Unknown provider

Simple thing - works in the jsfiddle but not in my file. I'm trying to add some data to "factory" of the module and then use it within my controllers.
This is the relevant code:
var challengesApp = angular.module('challengesApp', []);
challengesApp.factory('Challenges', function() {
var challenges = [
{
'program': {
"num": "1",
"name": "aaa"
},
'challengeDesc': "desss",
'items': [
{title:"aaa",
desc:"bbb",
link: "www.google.com",
fiction: true},
]
}
];
return challenges;
});
function ChallengeCtrl($scope, Challenges) {
$scope.challenges = Challenges;
etc...
}
function ChallengeListCtrl($scope, Challenges) {
$scope.challenges = Challenges;
etc..
}
and the HTML:
<body ng-app="challengesApp">
<div ng-controller="ChallengeCtrl">
<div id="question">
<h2>{{ challenge.challengeDesc }}</h2>
<ul>
<li ng-repeat="item in challenge.items">
<strong>{{ item.title }}</strong> - {{ item.desc }}
</li>
</ul>
</div>
</div>
<div ng-controller="ChallengeListCtrl">
<div id="programs_list">
<ul>
<li ng-repeat="program in challenges | orderBy:orderProp:reverse">
<a href="" ng-click="changeChallenge(program)">
{{ program.program.num }} - {{ program.program.name }}
</a>
</li>
</ul>
</div>
</div>
<script src="js/main.js"></script>
</body>
anything here that I'm missing?
So, as I suspected, it was a dumb mistake. the <html> tag was:
<html ng-app>
blocking the correct ng-app attribute at the <body> tag.

Categories