AngularJS multiple uses of Controller and rootScope - javascript

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.

Related

Sharing data from API between controllers in AngularJS

I have a parent controller with some children controllers, and I want them all to share the same data that I retrieve from an Api service.
Controllers:
var app = angular.module('mymodule',[]);
app.controller('main', ['$scope', 'Api', function($scope, Api) {
var getList1 = Api.getList1()
.then(function(resp) {
$scope.list1 = resp.data;
});
var getList2 = Api.getList2()
.then(function(resp) {
$scope.list2 = resp.data;
});
}]);
app.controller('child1', ['$scope', function($scope) {
$scope.list1 = ?
$scope.list2 = ?
}]);
app.controller('child2', ['$scope', function($scope) {
$scope.list1 = ?
}]);
View:
<div ng-controller="main">
<ul>
<li ng-repeat="list in list1">
{{list.item}}
</li>
</ul>
<div ng-controller="child1">
<ul>
<li ng-repeat="list in list1">
{{list.item}}
</li>
</ul>
<ul>
<li ng-repeat="list in list2">
{{list.item}}
</li>
</ul>
</div>
<div ng-controller="child1">
<ul>
<li ng-repeat="list in list1">
{{list.item}}
</li>
</ul>
</div>
</div>
I tried to use this solution with Angular’s events mechanism ($on, $emit).
The problem was that I had to figure out which child controller is active and send the data when the promise has resolved. It ends with ugly spaghetti code...
Well, the best way is to use a service to have your API handling atomar placed inside your application. This fiddle shows you how you could achieve what you try to. By using AngularJS services you will be able to share the same data, objects and functions between controllers and let them interact with eachother. This is undepending on the amount of your controllers inside your application.
The following example is a full working API service with real HTTP-Requests and a real AngularJS service handling. It will help you by implement such logic inside your application. Please dont forget to check out the fiddle demo.
View
<div ng-controller="MyCtrl">
<h1>
MyCtrl
</h1>
<button ng-click="clearData()">
Clear data by using MyCtrl
</button>
<div ng-repeat="user in users">
<p>
Username: {{ user.name }}
</p>
</div>
</div>
<br /><br />
<div ng-controller="MyOtherCtrl">
<h1>
MyOtherController
</h1>
<button ng-click="clearData()">
Clear data by using MyOtherController
</button>
<div ng-repeat="user in users">
<p>
Username: {{ user.name }}
</p>
</div>
</div>
AngularJS Application
var myApp = angular.module('myApp',[]);;
myApp.controller('MyCtrl', function ($scope, apiService) {
$scope.users = apiService.getResponseData();
$scope.$watch(function () { return apiService.getResponseData()}, function (newValue, oldValue) {
$scope.users = newValue
});
$scope.clearData = function () {
apiService.reset();
}
});
myApp.controller('MyOtherCtrl', function ($scope, apiService) {
apiService.loadData();
$scope.$watch(function () { return apiService.getResponseData()}, function (newValue, oldValue) {
$scope.users = newValue
});
$scope.clearData = function () {
apiService.reset();
}
})
myApp.service('apiService', function ($http) {
var responseData = null;
return {
loadData: function () {
return $http({
url: 'https://jsonplaceholder.typicode.com/users',
method: 'GET'
}).then(function (response) {
responseData = response.data
});
},
getResponseData: function () {
return responseData
},
reset: function () {
responseData = null;
}
}
});
As your data is in the scope of the parent controller, you can access it in children controllers with $scope.$parent:
app.controller('child1', ['$scope', function($scope) {
$scope.list1 = $scope.$parent.list1;
$scope.list2 = $scope.$parent.list2;
}]);
Write your children as directives, and then you can inject data on the scope.
yourModule.directive('child1', function() {
return {
scope: {list1:'=',
controller: function (scope) {
//not sure you even need a controller, but it might look like this
scope.doSomething = function() {
//access scope.list1 here
}
},
template: '<ul><li ng-repeat="list in list1">{{list.item}}<li><ul>'
}
}
Usage:
<child1 list1="list1"></child1>

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>

how to create Custom filter angularjs javascript controller side?

how to create Custom filter angularjs javascript controller side?
i would like to search in array called segments by SegmentId, to create filter that do foreach on segments array search by SegmentId -
//Controller
$scope.GetSegmentDetails = function (id) {
$scope.SegmentName = $filter('SegmentById')($scope.segments,id);
}
//Filter
app.filter('SegmentById', function () {
return function (input, searchPerson) {
if (!searchPerson)
return input;
var results = [];
angular.forEach(input, function (person) {
}
});
return results;
}
});
You dont have to write your one filter to filter by SegmentId. Here you have an example
function MyCtrl($scope, $filter) {
$scope.data = [{"SegmentId":"1","Description":"hod Registrations"}, {"SegmentId":"2","Description":"hod Inactive"}, {"SegmentId":"3","Description":"hod testUpd"}, {"SegmentId":"8","Description":"hod test"}, {"SegmentId":"1111","Description":"hod Release"}, {"SegmentId":"12","Description":"hod Requests"}, {"SegmentId":"13","Description":"hod Welcome Back"}]
$scope.filterData = function(segmentId) {
return $filter('filter')($scope.data, { SegmentId: segmentId });
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="MyCtrl">
Full:
<ul>
<li ng-repeat="Segment in data">
{{Segment.SegmentId}}
</li>
</ul>
Filtered in View:
<ul>
<li ng-repeat="Segment in data | filter:{SegmentId:1111}">
{{Segment.SegmentId}}
</li>
</ul>
Filtered in Controller:
<ul>
<li ng-repeat="Segment in filterData(1111)">
{{Segment.SegmentId}}
</li>
</ul>
</div>
Only need to add in your controller the filter dependency and then call it.
app.controller("YourController", ['$scope', '$filter', function ($scope, $filter){
$filter('filterName')($args));
}]);

Reload DOM after AJAX queries AngularJS

In my project, I do an AJAX request using AngularJS who call another page that includes Angular directives (I want to make another AJAX call inside) but no interaction in loaded page.
I think new DOM isn't functionnal.. I'm trying the pseudo-code $apply unsuccessed.
Main.html :
<!DOCTYPE html>
<html data-ng-app="App">
<head></head>
<body >
<div data-ng-controller="editeurMenuMobile">
<ul>
<li data-ng-click="callMenu('FirstAjax.html')" > <!-- Work ! -->
<a href="">
<span>Modèles</span>
</a>
</li>
<li data-ng-click="callMenu('FirstAjax.html')"> <!-- Work ! -->
<a href="">
<span>Designs</span>
</a>
</li>
</ul>
<div data-ng-bind-html="data">
<!-- AJAX content -->
</div>
</div>
<!-- Javascript scripts -->
</body>
</html>
FirstAjax.html :
<div data-ng-controller="editeurActionAjax">
<div>
<button data-ng-click="callAction('SecondAjax.html')"> <!-- Doesn't work -->
Go
</button>
</div>
</div>
And my JS :
var App = angular.module('App', []);
App.controller('editeurMenuAjax', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.callMenu = function(element) {
$http({method: 'GET', url: element}).
success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}).
error(function() {
$scope.showAjaxError = true;
});
};
}
]);
App.controller('editeurActionAjax', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.callAction = function(element) {
$http({method: 'GET', url: element}).
success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}).
error(function() {
});
};
}
]);
Thank you for your help
From my point of view could the problem be because of the $scope?
your 2nd Controller don't have access to the same data variable.
Try to change the code to use $rootScope in both controllers instead of $scope, and see if it fix the problem.
Or
On your FirstAjax.html insert this:
<div data-ng-bind-html="data">
<!-- AJAX content -->
</div>
This should make a second data variable inside Scope of Controller 2, so that it can place the content.
I resolve my problem with this response.
My new JS :
App.directive('bindHtmlUnsafe', function( $compile ) {
return function( $scope, $element, $attrs ) {
var compile = function( newHTML ) { // Create re-useable compile function
newHTML = $compile(newHTML)($scope); // Compile html
$element.html('').append(newHTML); // Clear and append it
};
var htmlName = $attrs.bindHtmlUnsafe; // Get the name of the variable
// Where the HTML is stored
$scope.$watch(htmlName, function( newHTML ) { // Watch for changes to
// the HTML
if(!newHTML) return;
compile(newHTML); // Compile it
});
};
});
var App = angular.module('App', []);
App.controller('editeurMenuAjax', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.callMenu = function(element) {
$http({method: 'GET', url: element}).
success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}).
error(function() {
$scope.showAjaxError = true;
});
};
}
]);
App.controller('editeurActionAjax', ['$scope', '$http', '$sce', function($scope, $http, $sce) {
$scope.callAction = function(element) {
$http({method: 'GET', url: element}).
success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}).
error(function() {
});
};
}
]);
And new Main.html :
<!DOCTYPE html>
<html data-ng-app="App">
<head></head>
<body >
<div data-ng-controller="editeurMenuMobile">
<ul>
<li data-ng-click="callMenu('FirstAjax.html')" > <!-- Work ! -->
<a href="">
<span>Modèles</span>
</a>
</li>
<li data-ng-click="callMenu('FirstAjax.html')"> <!-- Work ! -->
<a href="">
<span>Designs</span>
</a>
</li>
</ul>
<div data-bind-html-unsafe="data">
<!-- AJAX content -->
</div>
</div>
<!-- Javascript scripts -->
</body>
</html>
And FirstAjax.html :
<div data-bind-html-unsafe='dataAction' >
<div class="addRubrique">
<button data-ng-click="callAction('SecondAjax.html')">
Ajouter
</button>
</div>
</div>
BindHtmlUnsafe the directive re-compile the new DOM to Angular knows the DOM loaded AJAX

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

Categories

Resources