scope variable not updating on view in ng-repeat - angularjs

I have created a directive to handle multiple file uploads and defined a scope variable in the controller which holds the selected files. After selecting files the directive updates the parent scope with the file object.
I have used a loop to display multiple file uploads inputs.
Here is my controller
angular.module('myApp').controller('CompanyReviewsController', ['$state', '$scope', 'company', 'MediaUploadService', 'ReviewsService', 'rating_attributes', 'tags', function ($state, $scope, company, MediaUploadService, ReviewsService, rating_attributes, tags) {
$scope.company = company;
$scope.tags = tags;
$scope.rating_attributes = rating_attributes;
$scope.gallery = {
pictures: 0,
videos: 0,
};
$scope.reviewe_form_data = {
main_picture: null,
main_video: null,
review_subject: '',
review_description: '',
gallery: {
pictures: [],
videos: [],
}
};
}]);
Here is my Directive
angular.module('myApp').directive('fileUploadDirective', ['$parse', function ($parse) {
return {
scope:false,
link: function (scope, element, attrs) {
var model = $parse(attrs.fileUploadDirective);
var modelSetter = model.assign;
var valid_types = attrs.accept;
var valid_size = attrs.maxFileSize;//bytes
element.bind('change', function (event) {
var file = event.target.files[0];
scope.$apply(function () {
modelSetter(scope, file);
});
});}]);
Here is my view snippets
<div class="add-more-gallery-items add-more-gallery-images full-width clear-both pull-left">
<div class="gallery-item" ng-repeat="n in [].constructor($WU_APP_SETTINGS.pages_settings.write_review_page.gallery.pictures.maximum_pictures) track by $index">
{{reviewe_form_data.gallery}}
<div class="file-preview position-relative">
<span data-ng-if="reviewe_form_data.gallery.pictures[$index]" class="reset-image-preview wu-top-0-force wu-right-0-force" >
<img src="assets/images/close.png" alt="Close"/>
</span>
<img
alt="Review Gallery Image"
id="review-main-picture-{{$index}}"
data-file-preview-element="{{$WU_APP_SETTINGS.pages_settings.write_review_page.default_upload_image}}"
ng-src="{{$WU_APP_SETTINGS.pages_settings.write_review_page.default_upload_image}}"
/>
</div>
<input
file-upload-directive="reviewe_form_data.gallery.pictures[$index]"
data-max-file-size="{{$WU_APP_SETTINGS.pages_settings.write_review_page.gallery.pictures.max_picture_size}}"
accept="{{$WU_APP_SETTINGS.pages_settings.write_review_page.gallery.pictures.valid_picture_types}}"
class="upload"
type="file"
data-message-invalid-file="File not valid"
data-message-invalid-file-size="File size not valid"
data-file-preview-element="#review-main-picture-{{$index}}"
/>
</div>
</div>
In breif:
- I have a scope variable in controller
$scope.reviewe_form_data = {
main_picture: null,
main_video: null,
review_subject: '',
review_description: '',
gallery: {
pictures: [],
videos: [],
}
};
Above variable gets updated via directive(Working)
scope.$apply(function () {
modelSetter(scope, file);
});
But on view it shows empty object when i try to display the variable
{{reviewe_form_data.gallery}}

Related

Pass object back from ionic model service

I have created a factory for a modal that pulls in an array(list) and I have a ng-click where I get the index and then get the object I want to pass the object back to my controller so I can then use it.
I not sure how I will pass the object back to the controller.
This is the function in my service that fires the open() for the modal and I am passing it the model that i receive from a rest call.
function CopyModalService($ionicModal, $rootScope) {
var $scope = $rootScope.$new(),
myModalInstanceOptions = {
scope: $scope,
animation: 'slide-in-up'
};
return {
open: open
};
function open(model) {
$ionicModal.fromTemplateUrl('templates/copy-modal.html',
myModalInstanceOptions)
.then(function (modalInstance) {
$scope.model = model;
$scope.addCopyCertificate = function(index){
console.log('click', $scope.model[index]);
};
$scope.close = function () {
closeAndRemove(modalInstance);
};
return modalInstance.show(model);
});
}
This is the html in the modal so you can get the picture
<ul class="list">
<li class="item row" ng-repeat="item in model">
<span class="col col-67">{{item.installerReference}}</span>
<span class="col">
<button class="button button-calm button-calm-search ion-ios-arrow-down"
ng-click="addCopyCertificate($index)"></button>
</span>
</li>
</ul>
When I click the button in the html addCopyCertificate() it all appears fine but how do I pass that back to the controller.
In my controller I am using it like this: (which is working)
if (res.length) {
CopyModalService.open(res);
}else{
Alert.showAlert('No matching certificates');
....
}
what about $rootScope.$broadcast? something like:
function CopyModalService($ionicModal, $rootScope) {
var $scope = $rootScope.$new(),
myModalInstanceOptions = {
scope: $scope,
animation: 'slide-in-up'
};
return {
open: open
};
function open(model) {
$ionicModal.fromTemplateUrl('templates/copy-modal.html',
myModalInstanceOptions)
.then(function (modalInstance) {
$scope.model = model;
$scope.addCopyCertificate = function(index){
console.log('click', $scope.model[index]);
$rootScope.$broadcast('update-controller',$scope.model[index]);
};
$scope.close = function () {
closeAndRemove(modalInstance);
};
return modalInstance.show(model);
});
}
and then when you want to get the value ..attach the listener with $rootScope.$on('') (or better $scope.$on()) ..something like
if (res.length) {
CopyModalService.open(res);
$scope.$on('update-controller',function(event, data){
console.log(data);
});
}else{
Alert.showAlert('No matching certificates');
....
}

ng-click does not work in directive of inline magnific popup

I have created magnific popup directive in angularjs. It works fine ng-click event on description link when first time loads but after close popup and again click on description link it does not work.
Please refer below code:
PhotoCtrl.js
(function () {
'use strict';
function PhotoCtrl($rootScope, $scope, $state, HzServices, HzPhotoService) {
$scope.doAlbumList = function () {
var data = {q: $state.params.pid};
$scope.albums = {};
$scope.albumsCount = 0;
$scope.photos = {};
$scope.photosCount = 0;
$rootScope.arrData = [];
var deferred = HzServices.deferred("/api/album/list", 'GET', data);
deferred.then(
function (res) {
/*
* success in repsonse
* Share common photo & album data across all controllers, directives by services.
*/
var data = {album: {count: res.data.count.album, data: res.data.album}, photo: {count: res.data.count.photo, data: res.data.photo}};
/*
* Create an array of magnific inline popup content
*/
angular.forEach(data.photo.data, function (value, key) {
if (value.data.length > 0) {
angular.forEach(value.data, function (value_, key_) {
$rootScope.arrData.push({
imageDescription: value_.photo_description,
imageScale_img: "/resize/" + value_.module + "/" + value_.photo_name,
imageOriginal_href: "/" + value_.module + "/" + value_.photo_name
});
});
}
});
HzPhotoService.setSharedData(data);
$scope.albums = $rootScope.sharedData.album.data;
$scope.albumsCount = $rootScope.sharedData.album.count;
$scope.photos = $rootScope.sharedData.photo.data;
$scope.photosCount = $rootScope.sharedData.photo.count;
},
function (res) {
/*
* Error hading in repsonse
*/
var data = {album: {count: $scope.albumsCount, data: $scope.albums}, photo: {count: $scope.photosCount, data: $scope.photos}};
HzPhotoService.setSharedData(data);
}
);
}
/**
* Get Photos data & count
* #returns {Array}
*/
$scope.doGetPhoto = function () {
return [{photos: $scope.photos, photoCount: $scope.photoCount}];
}
$scope.doEditDescription = function () {
console.log("description links from controller called");
}
angular
.module("AppWhizbite")
.controller('PhotoCtrl', ['$rootScope', '$scope', '$state', 'HzServices', 'HzPhotoService', PhotoCtrl]);
}());
photoList.html
<div>
<div class="total_album_photo gallery" ng-repeat-start="photo in photos track by $index">
<div class="no_of_photo imgWrapper">
<a href="javascript:void(0);" class="popup-link" data-index="{{$index}}">
<img ng-src="/resize/photo/{{photo.photo_name}}" height="120" width="120"/>
</a>
</div>
</div>
<div ng-repeat-end=""><hz-photo-popup></hz-photo-popup></div>
</div>
hzPhotoDirective.js
(function () {
'use strict';
angular
.module("AppWhizbite")
.directive("hzPhotoPopup", ["$rootScope", "$compile", "HzPhotoService", function ($rootScope, $compile, HzPhotoService) {
var magnificMarkup = "\n\
<form ng-controller=\"PhotoCtrl as Photo\" class=\"white-popup-block popMarkup ng-pristine ng-valid ng-scope\" id=\"dataPopup\" >\n\
<div class=\"popup_heading\">Photo</div>\n\
<div id=\"img_center\">\n\
<img style=\"width:100%\" src=\"\" id=\"img_center_content\" class=\"mfp-imageScale\">\n\
</div>\n\
<div class=\"popup_main\">\n\
<div class=\"popup_left photo_popup_left\">\n\
<div class=\"popup_raw1\">\n\
Edit description\n\
<div class=\"mfp-imageDescription\" style=\"cursor:pointer;\" ng-click=\"doEditDescription()\"></div>\n\
<textarea class=\"submitByEnter commentarea mfp-imageDescription\" placeholder=\"Edit description\" style=\"height: 76px;display:none;\"></textarea>\n\
</div>\n\
</div>\n\
</div>\n\
<div class=\"video_main\">\n\
</div>\n\
<button class=\"mfp-close\" type=\"button\" title=\"Close (Esc)\">×</button>\n\
</form>";
return {
restrict: "AE",
replace: false,
scope: true,
compile: function (scope, element) {
return{
pre: function (scope, element, attrs) {
if (scope.$last) {
// Iterate through all thumbnails class to bind magnific popup plugins
angular.forEach(angular.element(".gallery > .imgWrapper > a"), function (val, key) {
angular.element(".popup-link").eq(key).magnificPopup({
key: 'my-popup',
//items: arrData, // Array of media details
items: $rootScope.arrData, // Array of media details
index: key, // Index of media ref: data-index
type: 'inline',
verticalFit: true, // Fits image in area vertically
inline: {
// Magnific popup custom markup to show media (photo) gallery
markup: $compile(magnificMarkup)(scope)
},
gallery: {
enabled: true
},
callbacks: {
open: function () {
console.log("open called");
},
change: function () {
console.log("cahnge callaed");
},
markupParse: function (template, values, item) {
// optionally apply your own logic - modify "template" element based on data in "values"
// console.log('Parsing:', template, values, item);
console.log("markup parse called");
},
elementParse: function (item) {
console.log("element parse called");
}
}
});
});
}
}
}
},
link: function (scope, element, attrs) {
console.log("link method called");
}
}
}]);
}());
After R&D I crack the issue.
Magnific Popup callbacks objects have markupParse() method. It calls in every action of popup, so I put my angular js template $compile in markupParse method and it works fine.
It may different as per conditions or situations, but almost in all conditions it works finally fine.
Code:
inline: {
// Magnific popup custom markup to show media (photo) gallery
markup: magnificMarkup
},
callbacks:{
markupParse: function (template, values, item) {
$compile(template)(scope);
}
}
In the markupParse method having 3 parameters:
template : template holds actual HTML template which use in popup.
values: value holds current indexed value from arrData
item: item hold current item object.

How do I use ng-grid with Angular HotTowel

I am using John Papa's Angular HotTowel and I don't know how to incorporate Angulars ng-grid into the html. Here is what I've added thanks to wonderful help from stondo. Breeze seems to be adding extra information that is no allowing ng-grid to render the data in the grid. Is there a way to strip the extra info that breeze sends or a work around for ng-grid to behave correctly with breeze data?
angular.module('app').controller(controllerId,
['common', 'datacontext','$scope', '$http', grid2]);
function grid2(common, datacontext, $scope, $http) {
.....
.....
} else {
$http.get('/breeze/Breeze/NoBadgePersonnels').success(function (largeLoad) {
$scope.setPagingData(largeLoad, page, pageSize);
});
activate();
function activate() {
common.activateController([mockData()], controllerId)
.then(function() { log('Activated Grid View'); });
function mockData() {
return datacontext.getEmployeePartialsNoBadges().then(function (data) {
return vm.grid2 = data.results;
});
}
}
Additional information
Datacontext.js looks as follows:
(function () {
'use strict';
var serviceId = 'datacontext';
angular.module('app').factory(serviceId,
['common', 'config', 'entityManagerFactory', datacontext]);
function datacontext(common, config, emFactory ) {
var EntityQuery = breeze.EntityQuery;
var getLogFn = common.logger.getLogFn;
var log = getLogFn(serviceId);
var logError = getLogFn(serviceId, 'error');
var logSuccess = getLogFn(serviceId, 'success');
var manager = emFactory.newManager();
var $q = common.$q;
var service = {
getPeople: getPeople,
getMessageCount: getMessageCount,
getEmployeePartials: getEmployeePartials,
getEmployeePartialsNoBadges: getEmployeePartialsNoBadges
};
var entityNames = {
personnel: 'Personnel'
};
return service;
function getEmployeePartialsNoBadges() {
var orderBy = 'lname';
var employees; //variable to hold employees once we get them back
//use query using Employees resource
return EntityQuery.from('NoBadgePersonnels')
.select('id, fname, lname, class, zip, cntySnrDte')
.orderBy(orderBy)
.toType('Personnel')
.using(manager).execute()
.then(querySucceeded, _queryFailed)
function querySucceeded(data) {
employees = data.results;
log('Retrieved [Employee Partials] from remote data source', employees.length, true);
//log('Retrieved [Employee Partials] from remote data source');
return employees;
}
}
function _queryFailed(error) {
var msg = config.appErrorPrefix + 'Error retrieving data from entityquery' + error.message;
logError(msg, error);
throw error;
}
=================================
It seems like the grid sees 5 items that I queried for, however the items don't want to display on the cells. Red arrow indicates that it allocated 5 rows, and green arrow indicates that I have selected one of the rows. Still doesn't display the records.
thanks
nick
I had to modify John Papa's Hottowel.Angular template, because it wasn't working as expected with latest angular/breeze versions. I'll later share a github link and a blog post about that.
I was able to get ng-grid working just adding $scope and $http to the controller. Read the comment inside the code block to see how it could be entirely done without inject $http.
(function () {
'use strict';
var controllerId = 'corrieri';
angular.module('app').controller(controllerId, ['common', 'datacontext', '$scope', '$http', corrieri]); //'$http', '$scope',
function corrieri(common, datacontext, $scope, $http) { //,$http, $scope
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var vm = this;
$scope.corrieriList = [];
vm.corrieri = [];
vm.news = {
title: 'Corrieri',
description: 'Lista Corrieri'
};
vm.title = 'Corrieri';
//ng-grid test
$scope.filterOptions = {
filterText: "",
useExternalFilter: false
};
$scope.totalServerItems = 0;
$scope.pagingOptions = {
pageSizes: [10, 20, 30],
pageSize: 10,
currentPage: 1
};
$scope.setPagingData = function (data, page, pageSize) {
data = data.map(function (item) {
return {
PK_ID: item.PK_ID,
Ragione_Sociale: item.Ragione_Sociale,
Telefono: item.Telefono,
Nazionalita: item.Nazionalita,
Indirizzo: item.Indirizzo,
Cap: item.Cap,
Provincia: item.Provincia,
Descrizione: item.Descrizione
};
});
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.corrieriList = pagedData; //.results;
$scope.totalServerItems = data.length;
if (!$scope.$$phase) {
$scope.$apply();
}
};
$scope.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$http.get('breeze/Corrieri/GetCorrieri').success(function (largeLoad) {
var myModArray = largeLoad.map(function (item) {
return {
Pk_ID: item.Pk_ID,
Ragione_Sociale: item.Ragione_Sociale,
Telefono: item.Telefono,
Nazionalita: item.Nazionalita,
Indirizzo: item.Indirizzo,
Cap: item.Cap,
Provincia: item.Provincia,
Descrizione: item.Descrizione
};
});
data = myModArray.filter(function (item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
$scope.setPagingData(data, page, pageSize);
});
} else {
$http.get('breeze/Corrieri/GetCorrieri').success(function (largeLoad) {
$scope.setPagingData(largeLoad, page, pageSize);
});
}
}, 100);
};
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.$watch('pagingOptions', function (newVal, oldVal) {
if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText);
}
}, true);
$scope.$watch('filterOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText);
}
}, true);
$scope.gridOptions = {
data: 'corrieriList',
enablePaging: true,
showFooter: true,
showFilter: true,
enableCellEdit: true,
enableColumnResize: true,
enableColumnReordering: true,
pinSelectionCheckbox: true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions
};
//ng-grid test end
activate();
function activate() {
var promises = [getCorrieri()];
common.activateController(promises, controllerId)
.then(function () {
log('Activated Corrieri View');
});
}
//This function was used to get data using Breeze Controller
//and I was even able to use it to bind data to ng-grid
//calling the function getCorrieri inside my controller and binding
//gridOptions data to vm.corrieri or just the name of the function (in my case getCorrieri)
// $scope.gridOptions = { data: getCorrieri}
//Be aware that since we r using a Breeze Controller data retrieved have additional
//informations, so we have to remove those, if we bind using vm.corrieri.
//I found it easier to implement paging using $http and $scope, even though I think
//I could do it using only $scope and breeze.
//getCorrieri().then(function() {
// angular.forEach(vm.corrieri, function (cor) {
// delete cor._backingStore['$id'];
// delete cor._backingStore['$type'];
// $scope.corrieriList.push(cor._backingStore);
// });
//});
function getCorrieri() {
return datacontext.getCorrieri().then(function (data) {
return vm.corrieri = data.results;
});
}
}
})();
Below you can find my html for reference. Make sure to surround your's ng-grid div with data-ng-controller or just ng-controller='corrieri'
<section id="corrieri-view" class="mainbar" data-ng-controller="corrieri as vm">
<section class="matter">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="widget wgreen">
<div data-cc-widget-header title="Corrieri" allow-collapse="true"></div>
<div class="widget-content text-center text-info">
<div data-ng-controller='corrieri'>
<div class="gridStyle col-md-12" ng-grid="gridOptions">
</div>
</div>
<div class="widget-foot">
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</section>
Btw, don't forget to add 'ngGrid' to your modules list in app.js
var app = angular.module('app', ['ngGrid', other modules])
and also include ng-grid css and js in index.html (that is obvious, but better safe than sorry)
I struggled a few days to get this working properly, so I hope to help anyone out there having the same problem.
Try this out:
angular.module('app').controller(controllerId, ['common', 'datacontext', '$scope', grid]);
function grid(common, datacontext, $scope) {
$scope.gridOptions = {
data: 'vm.employees'
};
activate();
function activate() {
common.activateController([getEmployees()], controllerId)
.then(function () { log('Activated Grid View'); });
}
//get data for employees
function getEmployees() {
return datacontext.getEmployeePartialsNoBadges().then(function (mydata) {
return vm.employees = data;
});
}
}
here is an image of what I see
and here is the code I changed:
function getEmployees() {
return datacontext.getEmployeePartialsNoBadges().then(function (mydata) {
log(JSON.stringify(mydata));
return vm.employees = mydata.data;
});
Here is some additional info showing the data is coming through. Remote data source shows 1496 records. The preview for /breeze/breeze show data. I've blanked out sensitive info.
Here is the getEmployeePartialsNoBadges() method in my datacontext that was using entity framework:
function getEmployeePartialsNoBadges() {
var orderBy = 'lname';
var employees; //variable to hold employees once we get them back
//use query using Employees resource
return EntityQuery.from('NoBadgePersonnels')
.select('id, fname, lname, class, zip, cntySnrDte')
.orderBy(orderBy)
.toType('Personnel')
.using(manager).execute()
.then(querySucceeded, _queryFailed)
function querySucceeded(data) {
employees = data.results; //fillup the variable for employee with results
log('Retrieved [Employee Partials] from remote data source', employees.length, true);
//log('Retrieved [Employee Partials] from remote data source');
return employees;
}
}
============================== Nick ==============================
This is what my new mockup looks like now and I put this in datacontext calling it getPeople:
function getPeople() {
var people = [
{ firstName: 'John', lastName: 'Papa', age: 25, location: 'Florida' },
{ firstName: 'Ward', lastName: 'Bell', age: 31, location: 'California' },
{ firstName: 'Colleen', lastName: 'Jones', age: 21, location: 'New York' },
{ firstName: 'Madelyn', lastName: 'Green', age: 18, location: 'North Dakota' },
{ firstName: 'Ella', lastName: 'Jobs', age: 18, location: 'South Dakota' },
{ firstName: 'Landon', lastName: 'Gates', age: 11, location: 'South Carolina' },
{ firstName: 'Haley', lastName: 'Guthrie', age: 35, location: 'Wyoming' }
];
return $q.when(people);
}
I have reworked html and controller code to clean things up. The html is now call grid2.html and the controller is called grid2.js
(function () {
'use strict';
var controllerId = 'grid2';
angular.module('app').controller(controllerId,
['common', 'datacontext','$scope', grid2]);
function grid2(common, datacontext, $scope) {
var vm = this;
vm.grid2 = [];
$scope.gridOptions = {
data: 'vm.grid2'
};
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
vm.activate = activate;
vm.title = 'Grid2';
activate();
function activate() {
common.activateController([mockData()], controllerId)
.then(function() { log('Activated Grid View'); });
function mockData() {
return datacontext.getPeople().then(function (mydata) {
log(JSON.stringify(mydata));
return vm.grid2 = mydata.data;
});
}
}
}
})();
controller grid2.js
<section class="mainbar" data-ng-controller="grid2 as vm">
<section class="matter">
<div class="container">
<div class="row">
<div class="widget wgreen">
<div data-cc-widget-header title="Grid 2"></div>
<div class="widget-content user">
</div>
this is grid2 test
<div class="gridStyle" ng-grid="gridOptions"></div>
<div class="widget-foot">
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
</section>
Here what the screen looks like now. still no data in the grid:
In the debug, the data property shows undefined still
The mydata does contain array of data
The vm is an empty array on the return statement
The vm.grid becomes empty after the return and I'm unsure what the vm is also
The console show data being present

Accessing a service or controller in my link function - Angular.js

I have a directive, but I am having a problem access the controller and my service that is injected into it. Here is my directive:
angular.module('clinicalApp').directive('chatContainer', ['encounterService', function(encounterService) {
return {
scope: {
encounter: '=',
count: '='
},
templateUrl: 'views/chat.container.html',
controller: 'EncounterCtrl',
link: function(scope, elem, attrs, controller) {
scope.addMessage = function(message) {
//RIGHT HERE
scope.resetChat();
};
scope.resetChat = function() {
scope.chatText = '';
scope.updateCount(scope.chatText);
};
}
};
}]);
You can see that I am attaching a couple of functions to my scope inside the link function. Inside those methods, like addMessage, I don't have access to my controller or the service that is injected into the directive. How do I acceess the controller or service?
UPDATE
Here is the service:
angular.module('clinicalApp').factory('encounterService', function ($resource, $rootScope) {
var EncounterService = $resource('http://localhost:port/v2/encounters/:encounterId', {encounterId:'#id', port: ':8280'}, {
search: {
method: 'GET'
}
});
var newEncounters = [];
var filterTerms = {};
EncounterService.pushNewEncounter = function(encounter) {
newEncounters.push(encounter);
$rootScope.$broadcast('newEncountersUpdated');
};
EncounterService.getNewEncounters = function() {
return newEncounters;
}
EncounterService.clearNewEncounters = function() {
newEncounters = [];
}
EncounterService.setFilterTerms = function(filterTermsObj) {
filterTerms = filterTermsObj;
$rootScope.$broadcast('filterTermsUpdated');
EncounterService.getFilterTerms(); //filter terms coming in here, must redo the search with them
}
EncounterService.getFilterTerms = function() {
return filterTerms;
}
return EncounterService;
});
and the chat.container.html
<div class="span4 chat-container">
<h5 class="chat-header">
<span class="patient-name-container">{{encounter.patient.firstName }} {{encounter.patient.lastName}}</span>
</h5>
<div class="chat-body">
<div class="message-post-container">
<form accept-charset="UTF-8" action="#" method="POST">
<div class="text-area-container">
<textarea id="chatBox" ng-model="chatText" ng-keyup="updateCount(chatText)" class="chat-box" rows="2"></textarea>
</div>
<div class="counter-container pull-right">
<span class="muted" id="counter">{{count}}</span>
</div>
<div class="button-container btn-group btn-group-chat">
<input id="comment" class="btn btn-primary btn-small btn-comment disabled" value="Comment" ng-click="addMessage(chatText)"/>
</div>
</form>
<div messages-container messages="encounter.comments">
</div>
</div>
</div>
</div>
Here is Demo Plunker I played with.
I removed scope{....} from directive and added 2 values in controller and directive to see how they change regards to action.
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
// listen on any change of chatText in directive
$scope.$watch(function () {return $scope.chatText;},
function (newValue, oldValue) {
if (newValue == oldValue) {return;}
$scope.chatTextFromController = newValue;
}, true);
});
app.directive('chatContainer', ['encounterService', function(encounterService) {
return {
templateUrl: 'chat.container.html',
link: function(scope, elem, attrs) {
scope.countStart = scope.count;
scope.updateCount = function(chatText) {
alert('updateCount');
scope.count = scope.countStart - chatText.length;
};
scope.addMessage = function(message) {
alert('addMessage');
encounterService.sayhello(message);
scope.resetChat();
};
scope.resetChat = function() {
alert('resetChat');
scope.chatText = 'someone reset me';
scope.name = "Hello " + scope.name;
scope.updateCount(scope.chatText);
};
}
};
}]);
app.service('encounterService', function() {
var EncounterService = {};
EncounterService.sayhello = function(message) {
alert("from Service " + message);
};
return EncounterService;
});
HTML
<body ng-controller="MainCtrl">
<div chat-container></div>
<pre>chatText from directive: {{chatText|json}}</pre>
<pre>chatText from controller: {{chatTextFromController|json}}</pre>
<pre>name: {{name|json}}</pre>
</body>

The "with" binding of KnockoutJS in AngularJS?

I have just switched from KnockoutJS to AngularJS and I am not able to find the KnockoutJS's "with" data-bind in AngularJS.
Here is the piece of code in KnockoutJS. The "with" binding creates a new binding context, so that descendant elements are bound in the context of a specified object.
<h1 data-bind="text: city"> </h1>
<p data-bind="with: coords">
Latitude: <span data-bind="text: latitude"> </span>,
Longitude: <span data-bind="text: longitude"> </span>
</p>
<script type="text/javascript">
ko.applyBindings({
city: "London",
coords: {
latitude: 51.5001524,
longitude: -0.1262362
}
});
</script>
Does AngularJS have anything like context?
Nothing like with that I know of.. this is the best I could do:
<h1>{{city}}</h1>
<p ng-repeat="c in [coords.or.possibly.deeper.in.tree]">
Latitude: {{c.latitude}},
Longitude: {{c.longitude}}
</p>
Create a custom directive that loops through the source object and creates corresponding properties on the directive's scope that are getter/setter references to the source object.
Check out this plunker.
directive module:
angular.module('koWith', [])
.directive('koWith', function () {
return {
controller: function ($scope, $attrs) {
var withObj = $scope.$parent[$attrs.ngWith];
function getter(prop) {
return this[prop];
}
function setter(val, prop) {
this[prop] = val;
}
for (var prop in withObj) {
if (withObj.hasOwnProperty(prop)) {
Object.defineProperty($scope, prop, {
enumerable: true,
configurable: true,
get: getter.bind(withObj, prop),
set: setter.bind(withObj, prop)
});
}
}
},
restrict: 'A',
scope: true
};
});
app module:
angular.module('myApp', [])
.controller('myController', function ($scope) {
$scope.customer = {
name: "Timmeh",
address: {
address1: "12 S Street",
address2: "",
city: "South Park",
state: "CO",
zipCode: "80440"
}
};
});
html:
<div ko-with="customer">
<h2>{{name}}</h2>
<div ko-with="address">
{{address1}}<br>
{{address2}}<br>
{{city}}, {{state}} {{zipCode}}
</div>
</div>
Explanation
In KnockoutJS, bindings keep the bindingContext and data separated so creating the with binding is trivial since it only needs to create a new child bindingContext from the current one and use the with object as its data value.
In AngularJS, a directive's scope is basically the bindingContext and data object rolled into one. When a new scope is created, in order to get the with-like behavior, the properties of the with object have to be referenced onto the newly created scope object.
Here is solution based on #nwayve, but it supports expressions in koWith and also it watches for updating property/expression specified in koWith:
.directive('koWith', function () {
return {
restrict: 'A',
scope: true,
controller: function ($scope, $attrs, $parse) {
var ScopePropertyDesc = function (prop) {
var self = this;
self.propName = prop;
self.parsed = $parse(prop);
self.enumerable = true;
self.configurable = true;
//self.writable = true;
self.get = function () {
var withObj = $scope.$parent[$attrs.koWith];
var res = self.parsed($scope.$parent, withObj);
return res;
};
self.set = function (newValue) {
var withObj = $scope.$parent[$attrs.koWith];
self.parsed.assign(withObj, newValue);
};
};
$scope.$parent.$watch($attrs.koWith, function (oldVal, newVal) {
var withObj = $scope.$parent[$attrs.koWith];
(function copyPropertiesToScope(withObj) {
for (var prop in withObj) {
if (withObj.hasOwnProperty(prop)) {
Object.defineProperty($scope, prop, new ScopePropertyDesc(prop));
}
};
})(withObj);
});
}
};
});

Resources