AngularJS load JSON data then parse/load HTML from it - javascript

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>

Related

UI-Router for item list Angularjs

I'm facing some problems with my simple code (study), i really need some help to fix this.
First of all i have a php file who provides a json.
app.php
<?php
$dbh = new PDO('mysql:host=localhost;dbname=caio', 'root', '');
$a = $dbh->query("SELECT * FROM usuario");
$b = json_encode($a->fetchAll(PDO::FETCH_ASSOC));
echo $b;
?>
It's a simple json with id, name and surname
Also i have a Js file to get this.
app.js
var meuApp = angular.module('meuApp', ['ui.router']);
meuApp.config(function($stateProvider, $urlRouterProvider){
$stateProvider
.state('usuarios.detail', {
url: "/usuarios/{id}",
templateUrl: 'uDetail.html'
});
});
meuApp.controller('userCtrl', ['$scope', '$http', function ($scope, $http) {
$http.get('app.php')
.success(function(data) {
$scope.usuarios = data;
console.log(data);
//just checking in the console...
var id = data[0].id
console.log(id);
var nome = data[0].nome
console.log(nome);
});
}]);
and finally my html file
<html ng-app="meuApp" lang="pt">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="userCtrl">
<ul>
<li ng-repeat="usuario in usuarios">
<a ui-sref="usuarios.detail">{{usuario.nome}}</a>
</li>
</ul>
</div>
</body>
If i want to show, ok the code is working, but i want to click in each name and then the template shows me the id, name and surname of this "person". That's my problem.
Thank you guys.
Here you need to pass person object from one state to another state.
For that you can use params attribute of ui-router. When you click any perticular person at that time you need to pass id also while routing from one state to another because you already configure in url "/usuarios/{id}".
ui-router will match that property from params and will set in url.
Now you can successfully pass clicked object from one state to another. Get that object with $stateParams service of ui-router in controller of usuarios.detail state so that you can display in uDetail.html
meuApp.config(function($stateProvider, $urlRouterProvider,$stateParams){
$stateProvider
.state('usuarios.detail', {
url: "/usuarios/{id}",
params: {
id: null,
person:null
},
templateUrl: 'uDetail.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.id;
$scope.person = $stateParams.person;
console.log("State parameters " + JSON.stringify($stateParams));
}
});
});
and in your template where you are showing the list.
<ul>
<li ng-repeat="usuario in usuarios">
<a ui-sref="usuarios.detail({ id:usuario.id,person:usuario})">{{usuario.nome}}</a>
</li>
</ul>
See above code which I gave for your reference.
You can see this Demo for in detail idea of ui-router.
You need some minor changes in order to get your code working, check the files bellow:
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-controller="userCtrl">
<ul>
<li ng-repeat="user in users">
<a ui-sref="users.detail({id: user.id, user: user})">{{user.name}}</a>
</li>
</ul>
</div>
<div ui-view></div>
</body>
app.js
var app = angular.module('app', ['ui.router']);
app.config(function($stateProvider) {
$stateProvider
.state('users', {
abstract: true,
url: 'users',
template: '<div ui-view></div>'
})
.state('users.detail', {
url: '/:id',
templateUrl: 'users.detail.html',
params: {
user: null
},
controller: function($scope, $stateParams) {
$scope.user = $stateParams.user;
}
});
});
app.controller('userCtrl', ['$scope', '$http',
function($scope, $http) {
// replace this with your service call
$scope.users = [{
id: 1,
name: 'john',
surname: 'doe'
}, {
id: 2,
name: 'mary',
surname: 'poppins'
}];
}
]);
user.detail.html
<fieldset>
<div>
<label>id: </label> {{user.id}}
</div>
<div>
<label>name: </label> {{user.name}}
</div>
<div>
<label>surname: </label> {{user.surname}}
</div>
</fieldset>

Angular directive taking no effect

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.

angular.js template variables to bootbox dialog?

I've been trying to figure this out for like 10 hours now. Time to ask for help!
I'm trying to pass a variable from an angular.js template variable to bootbox for a nice looking confirmation prompt.
Assume that I have the following (abbreviated for clarity):
<script>
$(document).on("click", ".confirm", (function(e) {
e.preventDefault();
bootbox.confirm("This needs to be the value of {{item.name}}", function(confirmed) {
console.log("Confirmed: "+confirmed);
});
}));
</script>
which is executed as such:
<ul class="list-group">
<li ng-repeat="item in items">
<span class="glyphicon glyphicon-fire red"></span>
</li>
</ul>
When the user clicks the link, I would like a the confirmation box to appear, and I need to include attributes like {{item.name}} and {{item.row}} that are specific to this element in the list.
I have read up on the $compile functionality of angular.js and I got it working in so far as having a <div compile="name"> but that doesn't help me for retrieving a single entry out of my list as I am iterating. Any help would be appreciated!
Applied as a directive...
HTML:
<body ng-app="myApp" ng-controller="MainController">
<ul class="list-group">
<li ng-repeat="item in items">
<confirm-button name="{{item.name}}"></confirm-button>
</li>
</ul>
</body>
JS:
angular.module('myApp', [])
.controller('MainController', function($scope) {
$scope.items = [
{ name: 'one' },
{ name: 'two' },
{ name: 'three' }
];
})
.directive('confirmButton', function(){
return {
restrict: 'E',
scope: { name: '#' },
template: '<span class="glyphicon glyphicon-fire red" ng-click="confirm(name)">Button</span>',
controller: function($scope) {
$scope.confirm = function(name) {
bootbox.confirm("The name from $scope.items for this item is: " + name, function(result){
if (result) {
console.log('Confirmed!');
} else {
console.log('Cancelled');
}
});
};
}
}
});
Working plunk
This is a simple approach, but depending on what you're trying to do it may not suit you:
var app = angular.module('app', []);
app.controller('AppCtrl', function ($scope) {
$scope.items = [
{ name: "Bla bla bla bla?", url: "http://stackoverflow.com" },
{ name: "Ble ble ble ble?", url: "http://github.com" }
];
$scope.confirm = function (item) {
bootbox.confirm("Confirm?", function (confirmed) {
alert('Confirmed: '+ confirmed +'. Url: '+ item.url);
});
};
});
In your html:
<div ng-app='app' ng-controller="AppCtrl">
<ul class="list-group">
<li ng-repeat="item in items">
<a ng-click="confirm(item)">
<span class="glyphicon glyphicon-fire red"></span>
{{ item.name }}
</a>
</li>
</ul>
</div>
Depending on what you want, maybe you should check out the directives: https://docs.angularjs.org/guide/directive

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
};
});

AngularJS multiple uses of Controller and rootScope

I want to use a controller on 2 seperated HTML elements, and use the $rootScope to keep the 2 lists in sync when one is edited:
HTML
<ul class="nav" ng-controller="Menu">
<li ng-repeat="item in menu">
{{item.title}}
</li>
</ul>
<div ng-controller="Menu">
<input type="text" id="newItem" value="" />
<input type="submit" ng-click="addItem()" />
<ul class="nav" ng-controller="Menu">
<li ng-repeat="item in menu">
{{item.title}}
</li>
</ul>
</div>
JS
angular.module('menuApp', ['menuServices']).
run(function($rootScope){
$rootScope.menu = [];
});
angular.module('menuServices', ['ngResource']).
factory('MenuData', function ($resource) {
return $resource(
'/tool/menu.cfc',
{
returnFormat: 'json'
},
{
getMenu: {
method: 'GET',
params: {method: 'getMenu'}
},
addItem: {
method: 'GET',
params: {method: 'addItem'}
}
}
);
});
function Menu($scope, MenuData) {
// attempt to add new item
$scope.addNewItem = function(){
var thisItem = $('#newItem').val();
MenuData.addItem({item: thisItem},function(data){
$scope.updateMenu();
});
}
$scope.updateMenu = function() {
MenuData.getMenu({},function(data){
$scope.menu = data.MENU;
});
}
// get menu data
$scope.updateMenu();
}
When the page loads, both the UL and the DIV display the correct contents from the database, but when i use the addNewItem() method only the DIV gets updated.
Is there a better way to structure my logic, or can I do something to make sure the $scope.menu in the UL gets updated at the same time?
Here's an example of something similar: http://plnkr.co/edit/2a55gq
I would suggest to use a service that holds the menu and its methods. The service will update the menu which is referenced by the controller(s).
See a working plunker here: http://plnkr.co/edit/Bzjruq
This is the sample JavaScript code:
angular
.module( 'sampleApp', [] )
.service( 'MenuService', [ '$rootScope', function( $rootScope ) {
return {
menu: [ 'item 1' ],
add: function( item ) {
this.menu.push( item );
}
};
}])
.controller( 'ControllerA', [ 'MenuService', '$scope', function( MenuService, $scope ) {
$scope.menu = MenuService.menu;
$scope.addItem = function() {
MenuService.add( $scope.newItem );
};
}]);
And the sample Html page:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="sampleApp">
<div ng-controller="ControllerA">
<ul>
<li ng-repeat="item in menu">{{item}}</li>
</ul>
<input type="text" ng-model="newItem" /><input type="submit" ng-click="addItem()" />
</div>
<div ng-controller="ControllerA">
<ul>
<li ng-repeat="item in menu">{{item}}</li>
</ul>
</div>
</body>
</html>
Edit:
Here is the updated version plunker. it works in two controller.
Main idea is using service and broadcast to sync the data with the directive.
app.service('syncSRV', function ($rootScope) {
"use strict";
this.sync = function (data) {
this.syncData = data;
$rootScope.$broadcast('updated');
};
});
app.controller('MainCtrl1', ['$scope', function ($scope) {
}])
.controller('MainCtrl2', ['$scope', function ($scope) {
}]);
app.directive('sync',function (syncSRV) {
"use strict";
return {
template: '<div><input ng-model="syncdata" type="text" /></div> ',
controller: function ($scope, $element, $attrs) {
$scope.$watch('syncdata', function (newVal, oldVal, $scope) {
syncSRV.sync(newVal);
}, true);
}
};
}).directive('dataview', function (syncSRV) {
"use strict";
return {
template: '<div>Sync data : {{data}}</div> ',
controller: function ($scope, $element, $attrs) {
$scope.$on('updated', function () {
$scope.data = syncSRV.syncData;
});
}
};
});
<div ng-controller="MainCtrl1">
<fieldset>
<legend> Controller 1</legend>
<div dataview></div>
<div sync></div>
</fieldset>
</div>
<div ng-controller="MainCtrl2">
<fieldset>
<legend> Controller 2</legend>
<div dataview></div>
<div sync></div>
</fieldset>
</div>
Here is what I would do for this case.
I will create a directive for
<ul class="nav" ng-controller="Menu">
<li ng-repeat="item in menu">
{{item.title}}
</li>
</ul>
so once item is updated, it will be updated in both directive.
small example
I just want to update and simplify the selected answer. It seems you can reduce this by deleting this line:
$rootScope.$broadcast( 'MenuService.update', this.menu );
and this chunk:
$scope.$on( 'MenuService.update', function( event, menu ) {
$scope.menu = menu;
});
The reason being, we are already using a Service, and that basically binds the two identical controllers, so no need to use $rootScope.$broadcast and add an observable.
Working plunk here:
http://plnkr.co/edit/1efEwU?p=preview
You only need to link the service, when I refactor the code I was able to reduce it to 13 lines instead of 22.

Categories

Resources