This is sort of a follow-up to my previous question. I'm trying to figure out how to edit an existing item that is stored in my Firebase. My items are repeated on the page, and each of them have an "Edit" button next to them.
HTML
<h3>Editing {{ editedProvider.title }}</h3>
<form>
<input ng-model="editedProvider.title">
<button type="submit" ng-click="updateProvider()">Submit</button>
</form>
<div ng-repeat="provider in providers">
<h3>{{ provider.title }}</h3>
<button type="button" ng-click="setEditedProvider()">Edit</button>
</div>
</div>
This is how I'm currently adding an item to my list:
JS
var rootRef = new Firebase(FBURL);
var providersRef = rootRef.child('providers');
$scope.newProvider = {};
$scope.providers = [];
providersRef.on('child_added', function(snapshot) {
$timeout(function() {
var snapshotVal = snapshot.val();
console.log(snapshotVal);
$scope.providers.push({
title: snapshotVal.title,
name: snapshot.name()
});
});
});
$scope.createProvider = function() {
var newProvider = {
title: $scope.title
};
providersRef.push(newProvider);
};
I've then created a function setEditedProvider and binded it to the edit button, that when clicked brings up the edit form for that particular item. When I've made my changes however, I need to run a function called updateProvider, and I'm having problems creating that function.
$scope.editedProvider = null;
$scope.setEditedProvider = function(provider) {
$scope.editedProvider = angular.copy(provider);
}
$scope.updateProvider = function(provider) {
// need to take that edited function and push the updated version inside here
}
Can I utilise Firebase's data snapshot for this, like I am for creating the item?
Some of this is finally clicking for me, I think I understand how it needs to be done, I just can't work out how to achieve it.
Any help with this problem is appreciated. Thanks in advance!
$scope.updateProvider = function(provider) {
providersRef.child(provider.$id).update({tile:Provider.title});
}
Related
I have the following controller
angular.module('publicApp')
.controller('URLSummaryCtrl', function ($scope, $location, Article, $rootScope, $timeout) {
$scope._url = "";
$scope._title = "";
$scope._article = "";
$scope._authors = "";
$scope._highlights = [];
$scope._docType = "";
$scope.summarizeURL = function(){
Article.getArticleInfo($scope.url, "").then(
function(data){
$scope._url = data.url;
$scope._title = data.title;
$scope._authors = data.authors.join(', ');
$scope._highlights = data.highlights;
$scope._docType = data.documentType;
if($scope._docType == 'html'){
$scope._article = data.article[0].article;
}
else{
$scope._article = data.article;
}
var _highlights = [];
$scope._highlights.forEach(function (obj) {
_highlights.push(obj.sentence);
});
// wait for article text to render, then highlight
$timeout(function () {
$('#article').highlight(_highlights, { element: 'em', className: 'highlighted' });
}, 200);
}
);
}
and the following view
<form role="form" ng-submit="summarizeURL()">
<div class="form-group">
<input id="url" ng-model="url" class="form-control" placeholder="Enter URL" required>
</div>
<button class="btn btn-success" type="submit">Summarize</button>
</form>
<div class="col-lg-8">
<h2>{{ _title }}</h2>
<p> <b>Source: </b> {{_url}}</p>
<p> <b>Author: </b> {{_authors}} </p>
<p> <b>Article: </b><p id="article">{{_article}}</p></p>
</div>
When I give a url in the text field initially and click Summarize it works as expected. But when I change the value in the text field and click the button again every thing is updated properly, with the new values, but the $scope._article gets the new value and doesn't remove the old value. It displays both the new and the old value that was there before.
Why is this happening?
EDIT #1: I added more code that I had. I found that when I remove the $timeout(function(){...}) part it works as expected. So now the question is, why is $scope._article keeping the old value and pre-pending the new value?
EDIT #2: I found that $timeout(...) is not the problem. If I change
$timeout(function () {
$('#article').highlight(_highlights, { element: 'em', className: 'highlighted' });
}, 200);
to
$('#article').highlight(_highlights, { element: 'em', className: 'highlighted' });
it still behaves the same way. So now I'm assuming it's because I'm changing the $scope._article to be something else? What's happening is that I'm displaying the $scope._article value and then modifying what's displayed to contain highlights <em class='highlighed'> ... </em> on what ever I want to highlight.
EDIT #3: I tried to remove the added html before making the request to get new data but that doesn't work either. Here's the code I tried.
angular.module('publicApp')
.controller('URLSummaryCtrl', function ($scope, $location, Article, $rootScope, $timeout) {
$scope._url = "";
$scope._title = "";
$scope._article = "";
$scope._authors = "";
$scope._highlights = [];
$scope._docType = "";
$scope.summarizeURL = function(){
//Remove added html before making call to get new data
$('.highlighted').contents().unwrap();
Article.getArticleInfo($scope.url, "").then(
function(data){ ... }
);
Jquery in angular controllers = headache.
The problem is probably here for you
$timeout(function () {
$('#article').highlight(_highlights, { element: 'em', className: }, 200);
#article.html() here, is going to give weird output, because angular has it's own sync system and the jquery library you're using has it's own way of working with the DOM. Throw in the fact that asynchronous javascript is already a pain if you're working with multiple things.
What you want instead is to set the html to the angular scope variable before you work with it in jquery so you know what the jquery is working with, i.e.:
$timeout(function () {
$('#article').html($scope._article);
$('#article').highlight(_highlights, { element: 'em', className: }, 200);
I am new to Angular and need your help on an issue with the ng-repeat of my app.
Issue:
I have an html page (event.html) and in the corresponding controller of the file, I make a request to a firebase collection and update an array ($scope.events). The issue is that the data from firebase takes a few seconds to load and by the time data arrives to $scope.events, ng-repeat has already been executed and it displays an empty screen. The items are displayed correctly the moment I hit on a button in the HTML page (event.html).
Sequence of events:
I have a login page (login.html) where I enter a user name and phone number and I click on the register button. I've configured this click on the register button to go to the new state (event.html).
Here is the controller code for login.html:
$scope.register = function (user) {
$scope.user = user.name;
$scope.phonenumber = user.phonenumber;
var myuser = users.child($scope.user);
myuser.set({
phone : $scope.phonenumber,
Eventid : " ",
name : $scope.user
})
var userid = myuser.key();
console.log('id is ' +userid);
$state.go('event');
}
The controller of event.html (the state: event) has the following code:
var ref = new Firebase("https://glowing-torch-9862.firebaseio.com/Users/Anson/Eventid/");
var eventref = new Firebase("https://glowing-torch-9862.firebaseio.com/Events");
var myevent = " ";
$scope.events = [];
$scope.displayEvent = function (Eventid) {
UserData.eventDescription(Eventid)
//UserData.getDesc()
$state.go('myevents');
//console.log(Eventid);
};
function listEvent(myevents) {
$scope.events.push(myevents);
console.log("pushed to array");
console.log($scope.events);
};
function updateEvents(myevents) {
EventService.getEvent(myevents);
//console.log("success");
};
ref.once('value', function (snapshot) {
snapshot.forEach(function (childSnapshot) {
$scope.id = childSnapshot.val();
angular.forEach($scope.id, function(key) {
eventref.orderByChild("Eventid").equalTo(key).on("child_added", function(snapshot) {
myevents = snapshot.val();
console.log(myevents) // testing 26 Feb
listEvent(myevents);
updateEvents(myevents);
});
});
});
});
$scope.createEvent = function () {
$state.go('list');
}
event.html contains the following code:
<ion-view view-title="Events">
<ion-nav-buttons side="primary">
<button class="button" ng-click="createEvent()">Create Event</button>
<button class="button" ng-click="showEvent()">Show Event</button>
</ion-nav-buttons>
<ion-content class="has-header padding">
<div class="list">
<ion-item align="center" >
<button class= "button button-block button-light" ng-repeat="event in events" ng-click="displayEvent(event.Eventid)"/>
{{event.Description}}
</ion-item>
</div>
</ion-content>
</ion-view>
The button showEvent is a dummy button that I added to the HTML file to test ng-repeat. I can see in the console that the data takes about 2 secs to download from firebase and if I click on the 'Show Events' button after the data is loaded, ng-repeat works as expected. It appears to me that when ng-repeat operates on the array $scope.events, the data is not retrieved from firebase and hence its empty and therefore, it does not have any data to render to the HTML file. ng-repeat works as expected when I click the dummy button ('Show Event') because a digest cycle is triggerred on that click. My apologies for this lengthy post and would be really thankful if any of you could give me a direction to overcome this issue. I've been hunting in the internet and in stackoverflow and came across a number of blogs&threads which gives me an idea of what the issue is but I am not able to make my code work.
Once you update your events array call $scope.$apply(); or execute the code that changes the events array as a callback of the $scope.$apply function
$scope.$apply(function(){
$scope.events.push(<enter_your_new_events_name>);
})
If you are working outside of controller scope, like in services, directive, or any external JS. You will need to trigger digest cycle after change in data.
You can trigger digest cycle by
$scope.$digest(); or using $scope.$apply();
I hope it will be help you.
thanks
In your case you have to delay the binding time. Use $timeout function or ng-options with debounce property in your view.
you have to set a rough time taken to get the data from the rest API call. By using any one of the methods below will resolve your issue.
Method 1:
var myapp = angular.module("myapp", []);
myapp.controller("DIController", function($scope, $timeout){
$scope.callAtTimeout = function() {
console.log("$scope.callAtTimeout - Timeout occurred");
}
$timeout( function(){ $scope.callAtTimeout(); }, 3000);
});
Method 2:
// in your view
<input type="text" name="userName"
ng-model="user.name"
ng-model-options="{ debounce: 1000 }" />
I'm building an app using AngularJS and LocalStorage. I've run into a problem that it's a tad too complex for me.
I have a list of people, and the idea is to be able to add arrays of names. I choose X names, click add, creates an object in an array, it resets the list, and I can start over, choose X names, click add, etc.
Here's how I create the temporary array that then I push into LocalStorage:
HTML:
<form>
<div class="col-md-3" ng-repeat="staff in stafflist | orderBy: 'name'">
<button class="btn form-control" ng-show="!staff.chosen" ng-click="pushStaff(staff)">{{staff.name}}</button>
<button class="btn btn-primary form-control" ng-show="staff.chosen" ng-click="unpushStaff(staff)">{{staff.name}}</button>
</div>
<button class="btn ng-click="addRecord()">Add passangers</button>
</form>
JS:
$scope.paxlist = [];
$scope.pushStaff = function (staff) {
staff.chosen = true;
$scope.paxlist.push(staff);
console.log($scope.paxlist);
};
$scope.unpushStaff = function (staff) {
staff.chosen = false;
var index=$scope.paxlist.indexOf(staff)
$scope.paxlist.splice(index,1);
console.log($scope.paxlist);
}
My problem is that I can create objects into the array, but when I add an object, the selected items of the list of names won't reset, so they will be pre-selected when adding the next object.
At the same time, it will also stay linked to the last object added, so when I modify the selection, the last object will also get modified.
This also messes with the possibility of adding an editing capability for each object of the array.
I've created a Plnkr that illustrates the issue.
If you could shed some light on the issue, that would be brilliant.
In addRecord you need reset property chosen
$scope.addRecord = function () {
$scope.recordlist.push({ pax: angular.copy($scope.paxlist) });
jsonToRecordLocalStorage($scope.recordlist);
$scope.editItem = false;
$scope.paxlist = [];
$scope.stafflist.forEach(function (el) {
el.chosen = false;
});
};
Demo: http://plnkr.co/edit/vV8OuKiTKYkFyy7SrjOS?p=preview
I am having some difficulties understanding the properties/functions available through ui-grid. I often get confused with its previous version ng-grid. I am trying to find what the best way of deleting a checked-entry would be. Here is my markup, but due to not quite understanding if I have an index, or count available through a row entity:
HTML:
<div class="form-group">
<button type="button" id="addData" class="btn btn-success" ng-click="addData()">Add Data</button>
<button type="button" id="removeData" class="btn btn-success" ng-click="removeData()">Remove First Row</button>
<br>
<br>
<div id="grid1" ui-grid="gridOptions" ui-grid-edit ui-grid-selection external-scopes="myViewModel" class="grid"></div>
</div>
which lies under my controller:
$scope.removeData = function () {
console.log($scope.gridApi.selection.getSelectedRows());
var items = $scope.gridApi.selection.getSelectedRows();
angular.forEach($scope.myData, function (data, index) {
angular.forEach(items, function (item) {
if (item.displayValue = data.displayValue)
{
$scope.myData.splice(index, 1);
}
});
where displayValue is a property of my column and $scope.myData is my data. Is there a different way to send that selection to the controller for removal? The current way I have it does NOT work (obviously). Any help will be appreciated. If my markup is incomplete, I'll update it as necessary. Thank you!
Your solution looks a little complicated. Here is my delete function:
$scope.deleteSelected = function(){
angular.forEach($scope.gridApi.selection.getSelectedRows(), function (data, index) {
$scope.gridOptions.data.splice($scope.gridOptions.data.lastIndexOf(data), 1);
});
}
Here is a plunker based on the 210_selection tutorial.
ui-grid has problem with array.splice() method
This method is giving a problem which is making array replaced by the deleted row or item.
$scope.gridOptions.data.splice(index,1)
So the better way to handle delete of a row is by doing two things
Step 1:
$scope.gridApi.core.setRowInvisible(row)
The line above will hide the selected row
Step 2: Call the service which deletes the data at the database
I don't know how proper my solution is, but here is my code (maybe it can guide someone in the right direction or if they have a better method, please share :) )
$scope.removeData = function () {
angular.forEach($scope.gridOptions.data, function (data) {
angular.forEach($scope.gridApi.selection.getSelectedRows(), function (entity, index) {
if (entity.$$hashKey == data.$$hashKey) {
$scope.gridApi.selection.unSelectRow(entity);
//timeout needed to give time to the previous call to unselect the row,
//then delete it
$timeout(function () {
$scope.gridOptions.data.splice($scope.gridOptions.data.indexOf(entity), 1);
}, 0,1);
}
});
});
};
Hope it helps somebody!
I have a button that looks like this, which I specify in the cellTemplate value in my grid columnDefs...
columnDefs: [
// snipped other columns
{ name: '_delete', displayName: '', width: '5%', cellTemplate: '<div class="ui-grid-cell-contents ng-binding ng-scope"><button class="btn btn-danger btn-xs btn-block" ng-click="getExternalScopes().delete($event, row)"><span class="glyphicon glyphicon-trash"></span></button></div>', enableFiltering: false, disableColumnMenu: true }
My controller has a line which (IIRC) enables the getExternalScopes() call to work
$scope.$scope = $scope;
Then I handle the delete event I'm triggering like this in my controller...
$scope.delete = function (event, row) {
MyService.Delete({ action: row.entity.MyIdField }); // tells the server to delete the entity
$scope.gridApi.core.setRowInvisible(row); // hides that row in the UI
}
Perhaps this helps?
// $scope.gridApi.grid.cellNav.lastRowCol = null;
$scope.gridApi.grid.cellNav.focusedCells = [];
var cells = $(".ui-grid-cell");
// var focused = $(".ui-grid-cell-focus");
for (var i = 0; i < cells.length; i++) {
var div = $(cells[i].children[0]);
div.removeClass('ui-grid-cell-focus');
}
To answer #dileep's query extending on #Kevin Sage answer. This approach uses a service to send a delete request to the server and only delete the row once a successful response has been received. You do not want to delete the row from the grid if something went wrong and the record is still on the database.
$scope.deleteSelected = function(){
angular.forEach($scope.gridApi.selection.getSelectedRows(), function (data, index) {
YourService.delete({
id: data.id
}, function(response) {
$scope.gridOptions.data.splice($scope.gridOptions.data.lastIndexOf(data), 1);
}, function(error) {
// Run any error code here
});
});
}
The below code shows a list from firebase and shows a corresponding comment field for each item in the list. The user can make a comment on that item and it will update the comment field for that item in the list. Currently, each time a comment is made, it overwrites the previous one, but I'd like for all comments to be saved.
How do I make it so that every time a comment is added, the previous ones are saved as well?
http://jsfiddle.net/chrisguzman/PS9J2/
indx.html
<div ng-app="MyApp" ng-controller="MyCtrl">
<div ng-repeat="(id,item) in data">
<h2>{{item.title}}</h2>
<input ng-model="item.comment"></input>
<button type="submit" ng-click="CommentAdd(id)">Comment</button>
</div>
</div>
app.js
angular.module('MyApp', ['firebase'])
.controller('MyCtrl',
function MyCtrl($scope, $firebase) {
var furl = "https://helloworldtest.firebaseio.com";
var ref = new Firebase(furl);
$scope.data = $firebase(ref);
$scope.CommentAdd = function (id) {
$scope.data.$save(id);
};
});
The following is the data structure within firebase that is generated
{helloworldtest:
{-JSQhsAnY5zhf0oVKfbb: {title: "nameA", comment:"Second Comment"},
-JSQhsAnY5zhf0oVKfbb: {title: "nameB", comment:"Second Comment"}}
}
However, I'd like to create the following where there is a 'comments' branch that holds all comments.
{helloworldtest:
{-JSQhsAnY5zhf0oVKfbb: {title: "nameA", comments:{-JSQhsAnY5zhf0oVKfbb:{Comment:"Second Comment"},-JSQhsAnY5zhf0oVKfbb:{Comment:"First Comment"}}},
{-JSQhsAnYdfdfdffbb: {title: "nameA", comments:{-JSQhsAnY5zhf0oVKfAb:{Comment:"Another Comment"},-JSQhsAnY5zhf0oVKfbb:{Comment:"First Comment"}}}
}
I've tried to do this by replacing
$scope.data.$save(id);
with
$scope.data.$add(id);
I've also tried using :
$scope.data[id].$add({foo: "bar"})
You are saving the comment into a field called comment. Instead, use a list called comments and utilize push or $add.
<div ng-app="MyApp" ng-controller="MyCtrl">
<div ng-repeat="(id,item) in data">
<h2>{{item.title}}</h2>
<input ng-model="newComment"></input>
<button type="submit" ng-click="addComment(id, newComment)">Comment</button>
</div>
</div>
function MyCtrl($scope, $firebase) {
var furl = "https://helloworldtest.firebaseio.com";
var ref = new Firebase(furl+'/items');
$scope.data = $firebase(ref);
var $comments = $firebase( commentsRef );
$scope.addComment = function (id, newComment) {
ref.child(id).child('comments').push(newComment);
};
});
Also Don't nest data just because you can. Instead, consider putting comments in their own path, items in their own path.