I've been trying to learn AngularJS recently, and hit a bump in the road with Localstorage i spend so many hours trying to make it save locally, I think that it's working as it should now, but now i would like to print out the data saved local from the JSON array, how can i go about that?
EDIT:
A bit of clarification, What im trying to achieve is getting the information i save in the localstorage out onto the website as a string, so it's readable. hope i'ts more understandable. Thanks in advance
My view.
<ion-list>
<div >
<ion-item ng-controller='ModalEditCtrl' ng-click="openModal()">
<div class="thumbnail" style="border:1px black solid">
</div>
<div ng-controller="createPerson" class="contactinfo" >
<li ng-repeat="contact in contactdetail.contactinfo"> {{contact.name}} </li>
</div>
</ion-item>
</div>
<div ng-controller="ModalAddCtrl">
<button type="button" ng-click="openModal()">+++</button>
</div>
</ion-list>
My controller
app.controller('createPerson', function ($scope) {
var id = id_counter = 1;
$scope.editorEnabled = false;
$scope.disableEditor = function() {
$scope.editorEnabled = false;
};
$scope.enableEditor = function() {
$scope.editorEnabled = true;
};
$scope.contactinfo = [
{name: 'test', phone: 1231, email: 'asd#asd.com'}
];
$scope.saveData = function () {
id_counter += 1;
$scope.editorEnabled = false;
$scope.contactinfo.push({
name: $scope.contactName,
phone: $scope.contactPhone,
email: $scope.contactEmail,
sort_id: id_counter
});
//$scope.todoText = ''; //clear the input after adding
localStorage.setItem('contactinfo', JSON.stringify($scope.contactinfo));
// localStorage.setItem("contacts", JSON.stringify(contacts));
}
$scope.loadData = function () {
var contacts = localStorage.getItem("contactinfo");
var contactdetail = JSON.parse(contacts); //
console.log(contactdetail);
}
$scope.clearData = function () {
window.localStorage.clear();
}
});
Your question is not very clear, I dont think you will be able to get much help unless you clean it up a little.
To print out the data (for debugging, usually) you could just add {{contactinfo|json}} somewhere in your html.
To actually display the data for use on the webpage the following should work for you.
<div ng-repeat="contact in contactinfo track by $index">
<div>Name: {{contact.name}}</div>
<div>Phone: {{contact.phone}}</div>
<div>Email: {{contact.email}}</div>
</div>
I think that some of that logic might be better split into a factory, too. Something like this maybe...?
var contactFactory = angular.module('contactFactory', []);
contactFactory.factory('contactInfo', ['$window', function ($window) {
var id = id_counter = 1;
var contacts = [];
function addContact(name, phone, email) {
id_counter += 1;
contacts.push({
name: name,
phone: phone,
email: email,
sort_id: id_counter
});
saveData();
}
function saveData(contactInfo) {
$window.localStorage.setItem('contactinfo', angular.fromJson(contacts));
}
function loadData() {
contacts = angular.toJson($window.localStorage.getItem('contactinfo'));
return contacts;
}
function clearData() {
$window.localStorage.removeItem('contactinfo');
}
return {
addContact: addContact,
saveData: saveData,
loadData: loadData,
clearData: clearData
};
}]);
var app = angular.module('yourAppName', ['contactFactory']);
app.controller('createPerson', ['$scope', 'contactInfo', function ($scope, contactInfo) {
$scope.editorEnabled = false;
$scope.disableEditor = function() {
$scope.editorEnabled = false;
};
$scope.enableEditor = function() {
$scope.editorEnabled = true;
};
$scope.contactinfo = [
{name: 'test', phone: 1231, email: 'asd#asd.com'}
];
$scope.saveData = function () {
contactInfo.addContact($scope.contactName, $scope.contactPhone, $scope.contactEmail);
$scope.editorEnabled = false;
}
$scope.loadData = contactInfo.loadData;
$scope.clearData = contactInfo.clearData;
}]);
Angular has wrapper for window, which should be used inside your code. There is also ngStorage module or many available solutions which are dealing with browser storage in Angular way. Moreover Angular has functions like angular.toJson() and angular.fromJson(). If e.g. jsonObj is JSON array then var obj = angular.fromJson(jsonObj) gives you JavaScript array. If jsonObj has array property inside then you should go with: var jsArray = angular.fromJson(jsonObj).array.
Related
I have a nested ng-repeat with a filter to group by.
I have created this
fiddle.
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl',['$scope', function($scope) {
$scope.data =[
{subject: 'English',module: 'Literature', score: 95},
{subject: 'Maths',module: 'Calculus', score: 90}
];
$scope.dropScore = function() {
if ($scope.data[0].score > 0) {
$scope.data[0].score -= 8;
}
}
$scope.origData = angular.copy($scope.data)
$scope.reset = function () {
$scope.data = angular.copy($scope.origData);
};
}])
.filter('groupBy', function() {
return _.memoize(function(items, field) {
return _.groupBy(items, field);
}
);
});
When you press the button hit score the score of English drops but clicking reset will reset the $scope.data value but not show the updated data on the screen.
Can someone help with this
Working Demo
Just use this reset method:
$scope.reset = function () {
$scope.data[0].score = angular.copy($scope.origData[0].score);
};
You need to call $scope.$apply() to refresh your scope :
$scope.dropScore = function() {
if ($scope.data[0].score > 0) {
$scope.data[0].score -= 8;
$scope.$apply();
}
}
http://jsfiddle.net/zewaeqpx/
When you do ng-repeat=(subject, value) in data | groupBy: 'subject', you are creating another array for ng-repeat to use. So either you would need to assign each parameter within or not use the filter in the ng-repeat.
Option 1:
If you want to keep how you are using the ng-repeat, you could to this:
$scope.reset = function () {
_.forEach($scope.data, function(subject, idx){
subject.score = $scope.origData[idx].score;
});
};
Option 2:
or you could simplify your ng-repeat so that it works like this where the template becomes:
<li ng-repeat="subject in data">
<span ng-bind="subject.subject"></span>
<ul>
<li ng-bind="subject.module + ' - ' + subject.score">
</li>
</ul>
</li>
You should use ng-bind because in some browsers, when the data attribute is not loaded, it will flash {{}} for a split second before it loads.
Maybe this question was asked before, but i tried the solutions i saw in other posts such as disabling the fast watch, but nothing seems to work...
I have a grid using Angular ui-grid in my web page and the behavior i'm seeking for is that after click on a button the data must be updated. The issue is i can see that gridOptions.data is updated, the columnDefs too, even the length but view doesn't update, also the displaying becomes a bit messy and i have to scroll to get it right.
Here's my code
Controller :
(function(app) {
'use strict';
app.controller('Ctrl', Ctrl);
function Ctrl($scope, $routeSegment, $log, $filter) {
$log.debug('Controller - init()');
// Set ViewModel Object - will be exported in View
var viewModel = this;
viewModel.gridOptionsData = [];
viewModel.gridOptions = {};
viewModel.gridOptions.paginationPageSizes = [25, 50, 75];
viewModel.gridOptions.paginationPageSize = 25;
viewModel.gridOptions.data = [];
viewModel.gridOptions.rowIdentity = function(row) {
return row.id;
};
viewModel.gridOptions.getRowIdentity = function(row) {
return row.id;
};
$scope.showgrid = false;
$scope.filterText;
viewModel.refreshData = function() {
viewModel.gridOptions.data = $filter('filter')(viewModel.gridOptionsData, $scope.filterText, undefined);
};
$scope.$watch('dataStructure', function(data) {
if (angular.isDefined(data) && !angular.equals(data, [])) {
var struct = [];
for (var key in data[0]) {
if (angular.equals(key, 'id')) {
struct.push({
field: key,
visible : false
});
} else {
struct.push({
field: key,
});
}
}
viewModel.gridOptionsData = data;
viewModel.gridOptions.data = data;
viewModel.gridOptions.columnDefs = struct;
$scope.showgrid = true;
}
});
}
}(angular.module('app')));
View :
<div class="gridStyle" ui-grid="pageViewModel.gridOptions" ui-grid-pagination ></div>
<input type="text" ng-model="$parent.filterText" ng-change="pageViewModel.refreshData()" placeholder="Search / Filter" />
Appreciate your help
OK, yeah, I know this is basic stuff, but it's got me by you know what.
Here's the code:
myApp.controller('keyExpController', function ($scope, KeyExpDataService) {
var ctrlExp = this;
ctrlExp.keyExp = [];
$scope.company = {};
ctrlExp.achKeys = {
company: [{
achieves: [],
details: {
super: "",
whyleft: ""
}
}]
};
$scope.keyachievements = [];
$scope.compName = null;
ctrlExp.fetchKeyExp = function () {
//Resume Data
KeyExpDataService.getKeyExpData().then(function (result) {
ctrlExp.keyExp = result.data.resume.proexperience;
console.log("Result: " + ctrlExp.keyExp);
$scope.groupBy(ctrlExp.keyExp.length);
});
};
ctrlExp.fetchKeyExp();
// I group the friends list on the given property.
$scope.groupBy = function (nbrComps) {
//Set the global value for number of companies
nbrCompanies = nbrComps;
var compValue = "_INVALID_GROUP_VALUE_";
for (var i = 0; i < nbrComps; i++) {
$scope.keyachievements = ctrlExp.keyExp[i].keyachievements;
if (ctrlExp.keyExp[i].companyat !== compValue) {
$scope.company = [{
achievements: [],
details: {
companyName: ctrlExp.keyExp[i].companyat,
super: ctrlExp.keyExp[i].supervisor,
whyleft: ctrlExp.keyExp[i].reasonforleaving
}
}];
compValue = $scope.company.companyName;
$scope.compName = compValue;
//It's HERE, with the first line that I continually get the following error:
//TypeError: Cannot read property 'achieves' of undefined
//at Scope.$scope.groupBy (controllers.js:151)
//This is line 151 just below:
achievements.company[i].achieves[i] = $scope.keyachievements;
achievements.company[i].details.super = ctrlExp.keyExp[i].supervisor;
achievements.company[i].details.whyleft = ctrlExp.keyExp[i].reasonforleaving;
ctrlExp.achKeys.company[i].achieves[i] = $scope.keyachievements;
ctrlExp.achKeys.company[i].details.super = ctrlExp.keyExp[i].supervisor;
ctrlExp.achKeys.company[i].details.whyLeft = ctrlExp.keyExp[i].reasonforleaving;
}
}
};
});
Now what I'm doing is fine until I hit this in the controller. My OBJECT looks like this broken out for brevity:
ctrlExp.achKeys = {
company: [{
achieves: [],
details: {
super: "",
whyleft: ""
}
}]
};
I cannot figure out, for the life of me, yeah, it's got to be because I'm over 50, why I cannot assign anything to the first part of the object:
THIS:
ctrlExp.achKeys.company[0].achieves[0] = $scope.keyAchievements
The $scope.keyAchievements hold all the bullets for a particular company. There could be "n" number of companies on a person's resume. Hence, I'm looping through the companies to get the name of the company and the "key achievements" that reside "under" that company for a person.
That's pretty much it.
I'll post the "service" but this site is a cut for my own site, not posted yet, that I'll be using as a template for future considerations. Once I get this, I'm golden.
Thanks everyone for your contributions to our craft.
It looks like it's trying to set a value to a variable which is as yet undefined, namely achievements.company[i]
So, if you added something like:
achievements.company[i]={};
before your line 151, it could work. Well, you wouldn't get the
TypeError: Cannot read property 'achieves' of undefined
message anymore.
In JavaScript, you can have undefined variables/objects, but you can't assign properties to them.
From the error message above, it looks like achievements is defined, whereas achievements.company[i] is not, hence the issue when you want to assign the achieves[i] property to it.
Had achievements been undefined, you would have got the message:
Cannot read property 'company' of undefined
What browser are you using to test this? I ask because I've run into some incompatibilities in the past with similar commands and so I just avoid them now.
Instead I would do a more compatible Array.push() changing:
ctrlExp.achKeys.company[0].achieves[0] = $scope.keyAchievements
to
ctrlExp.achKeys.company[0].achieves.push($scope.keyAchievements);
So, yeah, I figured it out RIGHT after I posted the question.
Here's how I did it:
myApp.controller('keyExpController', function ($scope, KeyExpDataService) {
var ctrlExp = this;
ctrlExp.keyExp = [];
$scope.company = {};
ctrlExp.achKeys = {
company: {
achieves: [],
details: []
}
};
$scope.keyachievements = [];
$scope.compName = null;
ctrlExp.fetchKeyExp = function () {
//Resume Data
KeyExpDataService.getKeyExpData().then(function (result) {
ctrlExp.keyExp = result.data.resume.proexperience;
console.log("Result: " + ctrlExp.keyExp);
$scope.groupBy(ctrlExp.keyExp.length);
});
};
ctrlExp.fetchKeyExp();
// I group the friends list on the given property.
$scope.groupBy = function (nbrComps) {
//Set the global value for number of companies
nbrCompanies = nbrComps;
var compValue = "_INVALID_GROUP_VALUE_";
for (var i = 0; i < nbrComps; i++) {
$scope.keyachievements = ctrlExp.keyExp[i].keyachievements;
if (ctrlExp.keyExp[i].companyat !== compValue) {
$scope.company = {
achievements: [$scope.keyachievements],
details: [{
companyName: ctrlExp.keyExp[i].companyat,
super: ctrlExp.keyExp[i].supervisor,
whyleft: ctrlExp.keyExp[i].reasonforleaving
}]
};
compValue = $scope.company.companyName;
$scope.compName = compValue;
achievements.company.achieves[i] = $scope.keyachievements;
details.company.hr[i] = [
{
super: ctrlExp.keyExp[i].supervisor,
whyleft: ctrlExp.keyExp[i].reasonforleaving
}
];
ctrlExp.achKeys.company.achieves[i] = $scope.keyachievements;
ctrlExp.achKeys.company.details[i] = [
{
super: ctrlExp.keyExp[i].supervisor,
whyleft: ctrlExp.keyExp[i].reasonforleaving
}
];
}
}
};
});
So up above all the controller calls I put these ARRAYS in a global scope:
var achievements = {
company: {
achieves: []
}
};
var details = {
company: {
hr: []
}
};
var edu1 = {
hsandcol: {
school: []
}
};
var edu2 = {
other: {
school: []
}
};
And here's an example service I simply copy and paste and rename as I need
myApp.factory('EduDataService', function ($http, URL) {
var getEduExpData = function () {
return $http.get(URL + 'resume.json')
.success(function (data) {
console.log("SUCCESS!");
//console.log("The Key Experiences array length: " + data.resume.proexperience.length);
return data.resume.education;
})
.error(function (e) {
console.log("He\'s dead Jim!", e);
return e;
});
};
return {
getEduExpData: getEduExpData
};
});
In the HTML file, I figured out how to isolate and embed controllers within controllers calling the parent controller to do everything while the child controller get all the data.
It's pretty cool.
Note: ctrlRes is the call from the PARENT CONTROLLER: resumeController way above. I'll post the final site completion in a wee bit. It's pretty awesome and the JSON object holds everything about a person's resume and it's a great template for anyone needing a core resume website with ANGULAR and Bootstrap exclusively. Actually, it's for me and I don't mind sharing but it'll expose my last name.
and the HTML snippet:
<div class="my-education">
<h3>My Education</h3>
<!-- Education Start -->
<div class="education" ng-controller="eduController">
<h4>High School and College</h4>
<div class="panel-group" id="accordionedu{{$index}}">
<div class="panel panel-default" ng-repeat="edu in ctrlRes.eduHsCol[0]">
<div class="panel-heading">
<h5 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseEdu{{$index}}">
{{edu.name}} {{edu.type}}
<span class="pull-right">[{{edu.start}} - {{edu.end}}]</span>
</a>
</h5>
</div>
<div id="collapseEdu{{$index}}" class="panel-collapse collapse">
<div class="panel-body">
<div>
<h5>
<span class="pull-left">
<strong>Location:</strong>
{{edu.locale}}
</span>
<span class="pull-right">
<strong>Major:</strong>
{{edu.maj}}
</span>
</h5>
<br><br>
<span class="pull-left">
<strong>Graduated:</strong>
{{edu.grad}}
<strong>GPA:</strong>
{{edu.gpa}}
</span>
<span class="pull-right">
<strong>Minor:</strong>
{{edu.min}}
</span>
<br>
<hr/>
<p>
<strong><em>Comments: </em></strong>
</p>
<div>
{{edu.comments}}
</div>
<br>
<div ng-show="edu.type === 'College'">
<a href="documents/Academic Transcript.pdf" target="_blank">
Download Academic Transcript - PASSWORD PROTECTED
</a>
</div>
</div>
</div>
</div>
</div>
<pre hidden="hidden">{{ctrlRes.eduHsCol|json}}</pre>
</div>
<!-- Education 1 End -->
</div>
<pre hidden="hidden">{{ctrlRes.eduHsCol|json}}</pre>
<pre hidden="hidden">{{ctrlRes.eduOther|json}}</pre>
</div>
I want to use a select x-editable in my Meteor application. My goal is to assign users to groups. This should be reactive, so when you assign a user, other clients should see the changes. The current problem is that the assignment works (data-value changes), but only the user who made the change is able to see the new value.
Here is my code:
Template.userGroup.rendered = function() {
var groupId = this.data._id;
var sourceUsers = [];
Users.find().forEach(function(user) {
sourceUsers.push({value: user._id, text: user.username});
});
Tracker.autorun(function() {
$('.assign-user').editable("destroy").editable({
emptytext: "Empty",
source: sourceUsers,
success: function(response, result) {
if (result) {
Groups.update({_id: groupId}, {$set: {adminId: result}});
}
}
});
});
};
<template name="userGroup">
</template>
I already tried to "destroy" the stale x-editable and put it inside the Tracker.autorun function, but unfortunately, this does not work.
Any help would be greatly appreciated.
I don't use Tracker.autorun but I use x-editable for inline editing like this:
(also used it for group assigments - just like your case, but found it too clumsy on the UI side). Anyway, here's my code:
Template
<template name="profileName">
<td valign='top'>
<div id="profileNameID" class="editable" data-type="text" data-rows="1">{{profile.name}}</div>
</td>
</template>
And on the JS side
Template.profileName.rendered = function () {
var Users = Meteor.users;
var container, grabValue, editableColumns, mongoID,
_this = this;
var container = this.$('#profileNameID');
var editableColumns = container.size();
grabValue = function () {
var gValue = $.trim(container.html());
return gValue;
};
$.fn.editable.defaults.mode = 'inline';
return container.editable({
emptytext: 'Your name goes here',
success: function (response, newValue) {
var mongoID = removeInvisibleChars($(this).closest("tr").find(".mongoid").text());
var editedUser = _users.findOne({
_id: mongoID
});
Meteor.users.update(mongoID, {
$set: {
"profile.name": newValue
}
});
return container.data('editableContainer').formOptions.value = grabValue;
}
});
Update happens immediately on all subscribed authorized clients.
In my javascript file i have mix of knockout and jquery which contains two different view models and i am having trouble displaying the results:
Javascript:
POSITION ViewModel
var positionViewModel = function (data) {
var _self = this;
_self.PositionName = ko.observable(data.PositionName);
_self.PositionRank = ko.observable(data.PositionRank);
_self.ContentRole = ko.observable(data.ContentRole);
}
positionViewModel.AddPositions = function (data) {
$.each(data, function (index, value) {
positionViewModel.PushPosition(value);
});
};
positionViewModel.PushPosition = function (postion) {
viewModel.PositionTypes.push(new positionViewModel(position));
};
USER ViewModel
// the ViewModel for a single User
var userViewModel = function (data) {
var _self = this;
_self.ID = ko.observable(data.ID);
_self.Name = ko.observable(data.Name);
_self.Email = ko.observable(data.Email);
_self.ContentRole = ko.observable(data.ContentRole);
};
userViewModel.AddUsers = function (data) {
$.each(data, function (index, value) {
userViewModel.PushUser(value);
});
};
userViewModel.PushUser = function (user) {
viewModel.Users.push(new userViewModel(user));
};
Positions and Users
ko.utils.arrayForEach(viewModel.PositionTypes(), function(position){
var usersInPosition = ko.utils.arrayFilter(viewModel.Users(), function(user){
return user.ContentRole() == position.ContentRole();
});
ko.utils.arrayForEach(usersInPosition, function(user){
});
});
Binding
// Binds the main ViewModel
var bindModel = function (data) {
var _self = viewModel;
viewModel.TotalUser = ko.computed(function () {
return _self.Users().length;
});
userViewModel.AddUsers(data);
ko.applyBindings(viewModel, $('#UserView')[0]);
};
View Page
<ul data-bind="foreach:PositionTypes">
<li>
<div>
<span data-bind="text:PositionName"></span>
</div>
<ul data-bind="template: { name: 'grid', foreach: Users}">
</ul>
</li>
</ul>
Result example:
CEO
James
Vice President
John
Workers
Amy
Betsy
How can i alter my view to properly display results from javascript file?
So your architecture was wrong to start out. In your example you are showing a list of type Position and each Position has another list of type User. I have whipped up a fiddle with the correct architecture for you to be able to add on whatever functionality you need. I would seriously look into the knockout documentation as well as design a little bit before you start coding.
http://jsfiddle.net/zBmSN/1/