我花了大约5-6个小时试图解决这个问题,而且我不知道为什么简单地在ng-repeat中显示一个object
被重复10次。这些值也没有输出。
$scope looks like this
↪ projects (this loads up on demand)
↪ activeProjects (this is one object) and this used in the view
↪ setActiveProject(id,scope) passed along to the <projectpick> directive
请有人快速查看http://bit.ly/1TQxQW2 单击右侧列中的任何项目并观察输出。
编辑:添加了js - 省略了没有必要的东西。另一种看待这个问题的方法是如何调试ng-repeat循环?
(function() {
'use strict';
var DATA_KEY_PREFIX = 'project_',
AJAX_EVENT_PREFIX = 'projectsService';
angular.module('projectsApp', []).controller('ProjectsController', ['$scope', 'appUtils', '$timeout', function($scope, utils, $timeout) {
$scope.projects = [];
$scope.ajaxPojectsIsLoading = false;
$scope.activeProjects = [];
$scope.$watch('activeProjects', function(newProject, oldProject) {
$scope.ajaxPojectsIsLoading = false;
// user has changed focus on the current project so update
if (newProject !== oldProject) {
$scope.setActiveProject(newProject, $scope);
console.log('setting project to active' , newProject , $scope);
}
});
$scope.$on(AJAX_EVENT_PREFIX + '.get', function(e, data) {
$scope.ajaxPojectsIsLoading = true;
});
$scope.$on(AJAX_EVENT_PREFIX + '.done', function(e, data) {
console.log('activeProjects.done', $scope.activeProjects);
});
$scope.$on('mapMarker.click', function(e, marker) {
$scope.$apply(function() {
$scope.activeProjects = $scope.projects[marker.id];
});
});
}])
/*
* directive projectpick
* @dep {Object} SquizApiFactory - factory to provide the calls to/from squiz cms
* @return {Object} after ajax request passes back data object for rest of application.
* */
.directive('projectpick', ['projectsService', '$timeout', 'appUtils', function(projectsService, $timeout, utils) {
/*
* loadProject
* @scope
*
* */
function loadProject(scope) {
try {
var elementText = this.text().trim(),
ngModel = this.attr('ng-model'),
id = +this.attr('projectpick');
if (!angular.isNumber(id)) {
throw new Error('id must be a Number, ' + typeof id + ' passed in');
}
// bundle up the raw server data with directive's attributes and update the scope.
var handleProjectData = function(data) {
scope[ngModel][id] = angular.extend({
name: elementText,
id: id
}, data);
scope['active' + utils.capitaliseFirstLetter(ngModel)] = scope[ngModel][id];
}
// check to see if the data is already cached
if (angular.isDefined(scope[ngModel][id])) {
handleProjectData(scope[ngModel][id]);
} else {
projectsService.get(id).then(handleProjectData);
}
} catch (e) {
console.log(e.stack);
}
}
/*
* getProject
* @todo refactor this shit implementation
* @param id
* */
function setActiveProject(id, scope) {
var element = angular.element(document.querySelector('[projectPick="' + id + '"]'));
if (angular.isElement(element) && element.length) {
loadProject.call(element, scope);
}
}
return {
restrict: 'AE',
link: function(scope, element, attr) {
scope.setActiveProject = setActiveProject;
element.bind('click', function(e) {
e.preventDefault();
loadProject.call(element, scope);
});
}
}
}]).directive('loader', function() {
return {
restrict: 'E',
templateUrl: 'templates/loader.html',
replace: true,
scope: {
src: '@src',
content: '@content'
}
}
})
/*
* asDate
* @param String input
* @return Number as timestamp
* */
.filter("asDate", function() {
...
})
/*
* proejctsService
* API wrapper around the SquizApi.
* @Events - before and after ajax has finished
* @Methods: get
* ↪ calls getMetadata method
*
* @return Promise
* */
.service('projectsService', ['$rootScope', '$q', 'fakeSquizApiFactory', 'appUtils', function($rootScope, $q, squizApiFactory, utils) {
var squizApi = squizApiFactory();
return {
get: function(id) {
var defer = $q.defer();
$rootScope.$apply(function() {
$rootScope.$broadcast(AJAX_EVENT_PREFIX + '.get', {
data: id
});
});
squizApi.getMetadata({
asset_id: id,
dataCallback: function(data) {
if (data.hasOwnProperty('error')) {
defer.reject(data.error);
}
data = utils.tidyJSONKeys(data, DATA_KEY_PREFIX);
defer.resolve(data);
$rootScope.$apply(function() {
$rootScope.$broadcast(AJAX_EVENT_PREFIX + '.done', {
data: data
});
});
}
});
return defer.promise;
}
}
}])
/*
* appUtils
* contains useful/necessary functions in order to make this app work
* @return
* */
.factory('appUtils', [function() {
return {
tidyJSONKeys: function(arry, stringReplace) {
...
},
capitaliseFirstLetter: function(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
};
}])
.factory('fakeSquizApiFactory', function squizApiFactory() {
var Squiz_Matrix_API = new Function;
Squiz_Matrix_API.prototype.getMetadata = function(args) {
if (args.hasOwnProperty('dataCallback')) {
setTimeout(function() {
args.dataCallback.call({}, {
project_area: "Metro",
project_body: "...",
project_cost: "100",
project_image: "29031",
project_link: "http://nbhsredev.health.nsw.gov.au/",
project_end_date: "2017-05-03 00:00:00",
project_start_date: "2016-01-01 00:00:00",
project_status: "progress"
});
}, 150);
}
};
return function(apiOptions) {
apiOptions = angular.extend({
key: SQUIZ_API_KEY
}, apiOptions || {});
return new Squiz_Matrix_API(apiOptions);
};
}).directive('gmap', ['GMapsService', '$http', '$rootScope', function(gmaps, $http, $rootScope) {
var map;
var link = function(scope, element, attr) {
try {
var id = element.children(':first').prop('id'),
gmapParams = {},
props = JSON.parse(attr.params);
if (id && props) {
init(id, props, scope);
} else {
throw new Error('gmap expects a json string parsed into attribute params');
}
} catch (e) {
console.log(e.stack);
}
}
var controller = function($scope) {
this.addMarkers = addMarkers, this.panTo = panTo
}
function init(id, props, scope) {
map = new gmaps(angular.extend({
div: '#' + id
}, props));
if (scope.modelUrl) {
$http.get(scope.modelUrl).then(function(response) {
addModel.call(scope, response);
addMarkers(formatModelForGMap(scope.model, $rootScope));
}, handleError);
}
}
function addModel(ajaxResp) {
if (ajaxResp.hasOwnProperty(('data'))) {
this.model = ajaxResp.data;
}
}
function handleError(err) {
}
/*
* formatModelForGMap
* transforms the model to be compatible for gmaps
* @link https://hpneo.github.io/gmaps/examples/markers.html
* @events marker.click as we don't want to put a ng-click on the marker itself.
* {
* lat: -12.043333,
* lng: -77.028333,
* title: 'Lima',
* click: function(e) {},
* infoWindow: {}
*
}
* */
function formatModelForGMap(model, $rootScope) {
return model.map(function(location) {
...
}
})
}
/*
* addMarkers
* wrapper around the addMarkers method on GMap
* return map;
* */
function addMarkers(arrGMapParams) {
...
}
function panTo(marker, zoomLevel) {
...
}
return {
restrict: 'E',
scope: {
params: '@params',
modelUrl: '@modelUrl',
projectChange: '&'
},
transclude: true,
replace: true,
controlller: controller,
link: link
}
}]).service('GMapsService', function() {
return function(params) {
return new GMaps(params);
}
});
}());
HTML:
<div id="projects-side-links">
<ul class="projects-search__list scrollable">
<li>
<button projectpick="29112" ng-model="projects" class="projects-search__quick-button regional">Armidale Hospital Redevelopment</button>
</li>
<li>
<button projectpick="29096" ng-model="projects" class="projects-search__quick-button regional">Kempsey District Hospital</button>
</li>
<li>
<button projectpick="29100" ng-model="projects" class="projects-search__quick-button metro">Northern Beaches Health Service Redevelopment (Community Health Services)</button>
</li>
<li>
<button projectpick="29116" ng-model="projects" class="projects-search__quick-button regional">Shellharbour Ambulatory Care</button>
</li>
<li>
<button projectpick="29104" ng-model="projects" class="projects-search__quick-button metro">Prince of Wales - Reconfiguration and Expansion</button>
</li>
<li>
<button projectpick="29108" ng-model="projects" class="projects-search__quick-button metro">The Bright Alliance</button>
</li>
</ul>
</div>
<loader ng-show="ajaxPojectsIsLoading" src="mysource_files/ajax-loader.gif" content="Retrieving content..."></loader>
<div class="projects-search-results__container" ng-cloak ng-repeat="project in activeProjects">
<h1>{{$index + 1}}</h1>
<div class="projects-search-results__wrapper">
<div ng-show="project.image" class="js-result-thumbnail projects-search-results__image">
<img ng-src="./?a={{project.image}}" alt="{{project.name}}">
</div>
<div class="projects-search-results__details">
<h2 class="js-result-name project-search-results__name">{{project.name}}</h2>
<p class="projects-search-results__area">
<span class="js-result-area">{{project.area}}</span> Area
</p>
<p class="projects-search-results__status">
Project Status - <span class="js-result-status">{{project.status}}</span>
</p>
<div class="js-result-description projects-search-results__description">
{{project.body}}
<p>
<a href="#">Click here</a> for more information.
</p>
</div>
<ul class="projects-search-results__list">
<li class="projects-search-results__link icon-globe">
<a href="{{project.link}}" target="_blank">{{project.link}}</a>
</li>
<li ng-show="project.start_date" class="projects-search-results__list-item icon-clock">
<time>{{ project.start_date | asDate | date:'MMMM yyyy'}}</time>
<time ng-show="project.end_date"> — {{ project.end_date | asDate | date:'MMMM yyyy'}}</time>
</li>
<li ng-show="project.cost" class="projects-search-results__list-item icon-cost">
{{project.cost}} Million
</li>
</ul>
</div>
</div>
答案 0 :(得分:0)
使用$ scope。$ watchcollection而不是$ scope。$ watch
$scope.$watchcollection('activeProjects', function(newProject, oldProject) {
$scope.ajaxPojectsIsLoading = false;
// user has changed focus on the current project so update
if (newProject !== oldProject) {
$scope.setActiveProject(newProject, $scope);
console.log('setting project to active' , newProject , $scope);
}
});