I started this journey trying to get some settings to persist with localStorage, has some problems and posted about it here (without a solution): Why won't this data bind? An odd case in Angularjs
I've abandoned that method as I learnt about ngStorage. In theory ngStorage lets you 2-way bind into and out of Angular models. It's a great, great theory.
I'm having problems with it though. It half works.
The ideas is this:
Test for permission selection (true or false).
If no selection (first time use) pop-up a choice.
Store the choice.
On restart use the stored choice to set the permission true or false.
Allow user to change the permission from within the app.
It works up to number 4.
Testing shows that although on first use I can set $storage.analytics to true or false subsequent changes are not being stored and retrieved from local storage.
Here is the code:
permissionCallback = function(permission){
if(permission===1){
console.log("analytics allowed");
analytics.startTrackerWithId('UA-45544004-1');
$scope.$storage.analytics=true;
navigator.notification.alert('You can turn analytics off in the Data Tracking section at any time.', null, 'Analytics On', 'OK');
}else{
console.log("analytics denied");
$scope.$storage.analytics=false;
navigator.notification.alert('You can turn analytics on in the Data Tracking section at any time.',null , 'Analytics Off', 'OK');
}
}
if(typeof $scope.$storage.analytics === 'undefined'){
navigator.notification.confirm('This app would like your permission to collect data on how you use the app. No personal or user identifiable data will be collected.', permissionCallback, 'Attention', ['Allow','Deny']);
}
else{
console.log('start analytics are', $scope.$storage.analytics);
if(typeof analytics !== 'undefined'){
console.log("analytics functioning");
analytics.startTrackerWithId('UA-45544004-1');
$scope.trackClick = function(category, action){
analytics.trackEvent(category, action);
console.log('Tracking category: ' + category + ', Section: ' + action + '.');
}
}
}
$scope.counter = 0;
$scope.change = function(){
$scope.counter++;
console.log('analytics are ' + $scope.$storage.analytics);
}
And here is the html.
<li class="item item-toggle">
<i class="icon ion-cloud"></i> Data Tracking is {{$storage.analytics}} {{counter}}
<label class="toggle toggle-balanced">
<input type="checkbox" ng-model="$storage.analytics" ng-change="change()">
<div class="track">
<div class="handle"></div>
</div>
</label>
</li>
It's either a fault with my logic or, and I think this more likely, a misunderstanding about the scope of the data.
The odd thing is the console log in the change() function (which is purely for tracking these things) is always correct. So using $storage.analytics in the html is the correct way to do it (using $scope.storage.analytics causes all sorts of errors) and it is indeed binding from the html into $scope.storage.analytics.
So why isn't it saving it to local storage when using the toggle?
I ran into a similar problem with ng-storage. When the page was loaded/reloaded anything bound to a value in $sessionStorage was updated correctly. However any changes to $sessionStorage afterwards were not reflected in my view. What I ended up doing was creating a service for storing changes and using $sessionStorage as a temporary data store.
app.controller('TestController', funciton($scope, $sessionStorage, Service) {
// if we have session data set our service
if($sessionStorage.data) {
Service.data = $sessionStorage.data;
} else {
$sessionStorage.data = {};
}
// now bind scope to service
scope.data = Service.data;
// on update we set both Service and $sessionStorage
// scope.data will be automatically updated
scope.update = function(val) {
Service.data.value = val;
$sessionStorage.data.value = val;
}
});
app.service('TestService', function() {
var service = {
data: {
value: 'Hello World'
}
};
return service;
});
<div ng-controller="TestController">{{data.value}}</div>
<button ng-click-"update('Hello Universe')">Update</button>
This is a very rudimentary example of how my solution works but hopefully it gets anyone else stuck in the same situation on the right track.
Related
I'm quite new here so if I do something wrong let me know, ok?
I'm quite new in web development as well.
I'm having a problem here with a post method in ASP.NET.
Please, don't mind the name of the buttons and methods, ok? I'm Brazilian and their names are all in portuguese.
I have a submit button that calls a ng-click (Angularjs) method called AdicionarCliente().
View
<div>
<input type="submit" class="btn btn-info" value="Salvar" ng-click="AdicionarCliente()"/>
</div>
JavaScript
myApp.controller('AdicionarClientesController', function ($scope, $http) {
$scope.NomeCliente = "";
$scope.Telefone1Cliente = "";
$scope.AdicionarCliente = function () {
var promisse = $http.post("/app/AdicionarCliente/", { NomeCliente: $scope.NomeCliente, Telefone1Cliente: $scope.Telefone1Cliente })
promisse.then(function () {
window.location.href = "CadastroPet";
return false;
});
};
It works well until this part. All the times that I hit the submit button, it comes here and enter the function in the variable "promisse".
Now - the problem is here:
Controller
[HttpPost]
public JsonResult AdicionarCliente(string NomeCliente, string Telefone1Cliente)
{
var db = new RexsoftEntities();
db.CLIENTES.Add(new CLIENTES() { NOME = NomeCliente,
TELEFONE1 = Telefone1Cliente});
db.SaveChanges();
var Clientes = db.CLIENTES.ToList();
return Json(Clientes, JsonRequestBehavior.AllowGet);
}
The first time that I hit the submit button, the code here goes until the db.CLIENTES.Add part of the code - then it doesn't run the DB.SAVECHANGES() nor the rest of the code here. The second time it works like a charm. The problems just happen on the first submit hit.
As the return of the controller doesn't happens properly, the final part of the Javascript code does not run as well. This part:
window.location.href = "CadastroPet";
return false;
Can anyone help me?
(All the view is inside this div
<div ng-controller="AdicionarClientesController">
)
UPDATE
I removed the TYPE of the submit button and put the simple button type. It seems to be working now. How can I submit my form then?
First,as per EF best practice, try to wrap the db operation in using() { } block. Thus your controller lokks like
[HttpPost]
public JsonResult AdicionarCliente(string NomeCliente, string Telefone1Cliente)
{
var Clientes = new CLIENTES();
using(var db = new RexsoftEntities())
{
var _Clientes = new CLIENTES()
{
NOME = NomeCliente,
TELEFONE1 = Telefone1Cliente
};
db.CLIENTES.Add(_Clientes);
db.SaveChanges();
Clientes = db.CLIENTES.ToList();
}
return Json(Clientes, JsonRequestBehavior.AllowGet);
}
Secondly, in javascript side, you are using angularjs. window.location.href will not work in angular(see this and this). You have to use $window service (source: using angularjs $window service) or $location service (source: using angularjs $location service). Also avoid using return false;.
In your case the below will work.
promisse.then(function () {
$location.path('/CadastroPet');
});
I removed the TYPE of the submit button and put the simple button type. It seems to be working now.
I created another way to validate my form using the same js script that I mentioned. If the criterias wasn't met, i would return a message and a return false statement.
In this scenario I'm using the ui-bootstrap typeahead to capture an object from an external api. Using the select callback I'm getting that object and have the results set in a separate function within my controller.
The issue is that I want to take those results and send them off to a separate api with a click function I already have set up. My question is how do i get the results of the type-ahead into the click function to post? The user flow is as follows.
<div>
<input type="text" placeholder="Find A Game"
typeahead-on-select="setGames($item)"
ng-model="asyncSelected"
typeahead="test.name for test in getGames($viewValue)"
typeahead-loading="loadingLocations" typeahead-min-length="3"
typeahead-wait-ms="500" typeahead-select-on-blur="true"
typeahead-no-results="noResults">
</div>
<div ng-show="noResults">
No Results Found
</div>
<button ng-disabled="!asyncSelected.length"
ng-click="addtodb(asyncSelected)">Add To Database</button>
As you can see the label is set to the items name and this works fine. When the user selects the name I then use typeahead-on-select="setGames($item)" to send off the entire object to its own funtion. From there I want to take the object and pass it to another function that you can see within the button tags ng-click. I currently have it passing the model, but what I really want is to pass the entire object within $item from the select event. So far my controller looks like this:
angular.module('2o2pNgApp')
.controller('GiantCtrl', function ($scope, $http, TermFactory, $window, SaveFactory) {
$scope.getGames = function(val) {
return $http.jsonp('http://www.example.com/api/search/?resources=game&api_key=s&format=jsonp&limit=5&json_callback=JSON_CALLBACK', {
params: {
query: val
}
}).then(function(response){
return response.data.results.map(function(item){
return item;
});
});
};
$scope.setGames = function (site) {
var newsite = site;
};
$scope.addtodb = function (asyncSelected, newsite) {
TermFactory.get({name: asyncSelected}, function(data){
var results = data.list;
if (results === undefined || results.length === 0) {
SaveFactory.save({vocabulary:'5', name:newsite.name, field_game_id:newsite.id}, function(data) {
$window.alert('All Set, we saved '+asyncSelected+' into our database for you!')
});
} else {
// do stuff
});
}
});
No matter what I do I cant seem to pass the entire $item object into this click function to post all the info i need.
Via New Dev in Comments:
$item is only available locally for typeahead-on-select... you can
either assign it to some model within your controller, or, in fact,
make the model of typeahead to be the item: typeahead="test as
test.name for test in getGames($viewValue)" – New Dev
I've an inherited project writed in django 1.4 and I've no time to update it to another version of django.
I'm introducing angularjs in that project being newbie with it.
So, I've a HTML filled with data from the database (very simplified code):
<div ng-app="myApp" ng-controller="commentController">
<input placeholder="say something!" type="text">
<button class="btn" ng-click="sendComment()" >
<li ng-repeat="comment in comments" id="aportacion{{comment.pk}}">
{{comment.username}} - {{comment.text}}
</li>
</div>
And angularjs app (simplified) to fill the table with comments:
var app = angular.module("myApp", []);
app.controller("commentController",function ($scope) {
$scope.comments = [];
// this is generated dinamically with django from db data on page generation;
$scope.comments[$scope.comments.length] = {"username":"inigod", "text":"this is sparta"};
$scope.comments[$scope.comments.length] = {"username":"another guy", "text":"this is NOT sparta"};
.......
};
});
This works great, it builds all the comments ok, nice.
Now I've a textbox to add new comment and want to send via ajax the new comment to db and with the response json add a new comment in the top of the comments in html.
I've tried modificating the angularjs code to this:
app.controller("commentController",function ($scope) {
$scope.comments = [];
// this is generated dinamically with django from db data on page generation;
$scope.comments[$scope.comments] = {"username":"inigod", "text":"this is sparta"};
$scope.comments[$scope.comments] = {"username":"another guy", "text":"this is NOT sparta"};
$scope.sendComment = function(){
Dajaxice.kolokvoweb.post_comment($scope.comment_callback, {'thread':'{{thread.pk}}',
'type': 0,
'text': $('#comment').val(),
});
}
$scope.comment_callback = function (data){
if (data.result){
data["image"]= "/img/comment-placeholder.png";
//data["$$hashKey"] = "003";
alert("adding element" +$scope.aportaciones.length);
$scope.comments.push(data);
alert("added element" +$scope.aportaciones.length);
}
}
So I run this and I get two alert, one saying "adding element n" and the next "added element n+1" so it appears to reach to $scope.comment_callback an push the data to the array but the DOM is not updated and I cannot see the inserted comment in the page.
I must be something wrong but cannot find what...
I've see the response from ajax and is the same kind of JSON but withouth the $$haskey key.
PD: received data from the ajax service is:
{"username":"inigo","texto":"ggggggggggggggggggggggg","date":"now","result":true,"pk":74,"foto":"/img/agora-placeholder.png"}
The one getted when loading page for that comment (and which is well shown in the page) is:
{"pk":"74","texto":"ggggggggggggggggggggggg","username":"inigo","date":"10/11/14","foto":"/img/agora-placeholder.png"}
You have to wrap the content of comment_callback in a $scope.$apply method to notify about $scope changes within async callbacks:
$scope.comment_callback = function (data){
if (data.result){
$scope.$apply(function() {
data["image"]= "/img/comment-placeholder.png";
$scope.comments.push(data);
});
}
}
I have the skeleton of a chat page but am having issues tying it all together. What I'm trying to do is have messages sent to the server whenever the user clicks send, and also, for the messages shown to update every 3 seconds. Any insights, tips, or general comments would be much appreciated.
Issues right now:
When I fetch, I append the <ul class="messages"></ul> but don't want to reappend messages I've already fetched.
Make sure my chatSend is working correctly but if I run chatSend, then chatFetch, I don't retrieve the message I sent.
var input1 = document.getElementById('input1'), sendbutton = document.getElementById('sendbutton');
function IsEmpty(){
if (input1.value){
sendbutton.removeAttribute('disabled');
} else {
sendbutton.setAttribute('disabled', '');
}
}
input1.onkeyup = IsEmpty;
function chatFetch(){
$.ajax({
url: "https://api.parse.com/1/classes/chats",
dataType: "json",
method: "GET",
success: function(data){
$(".messages").clear();
for(var key in data) {
for(var i in data[key]){
console.log(data[key][i])
$(".messages").append("<li>"+data[key][i].text+"</li>");
}
}
}
})
}
function chatSend(){
$.ajax({
type: "POST",
url: "https://api.parse.com/1/classes/chats",
data: JSON.stringify({text: $('input1.draft').val()}),
success:function(message){
}
})
}
chatFetch();
$("#sendbutton").on('click',chatSend());
This seems like a pretty good project for Knockout.js, especially if you want to make sure you're not re-appending messages you've already sent. Since the library was meant in no small part for that sort of thing, I think it would make sense to leverage it to its full potential. So let's say that your API already takes care of limiting how many messages have come back, searching for the right messages, etc., and focus strictly on the UI. We can start with our Javascript view model of a chat message...
function IM(msg) {
var self = this;
self.username = ko.observable();
self.message = ko.observable();
self.timestamp = ko.observable();
}
This is taking a few liberties and assuming that you get back an IM object which has the name of the user sending the message, and the content, as well as a timestamp for the message. Probably not too far fetched to hope you have access to these data elements, right? Moving on to the large view model encapsulating your IMs...
function vm() {
var self = this;
self.messages = ko.observableArray([]);
self.message = ko.observable(new IM());
self.setup = function () {
self.chatFetch();
self.message().username([user current username] || '');
};
self.chatFetch = function () {
$.getJSON("https://api.parse.com/1/classes/chats", function(results){
for(var key in data) {
// parse your incoming data to get whatever elements you
// can matching the IM view model here then assign it as
// per these examples as closely as possible
var im = new IM();
im.username(data[key][i].username || '');
im.message(data[key][i].message || '');
im.timestamp(data[key][i].message || '');
// the ([JSON data] || '') defaults the property to an
// empty strings so it fails gracefully when no data is
// available to assign to it
self.messages.push(im);
}
});
};
}
All right, so we have out Javascript models which will update the screen via bindings (more on that in a bit) and we're getting and populating data. But how do we update and send IMs? Well, remember that self.message object? We get to use it now.
function vm() {
// ... our setup and initial get code
self.chatSend = function () {
var data = {
'user': self.message().username(),
'text': self.message().message(),
'time': new Date()
};
$.post("https://api.parse.com/1/classes/chats", data, function(result) {
// do whatever you want with the results, if anything
});
// now we update our current messages and load new ones
self.chatFetch();
};
}
All right, so how do we keep track of all of this? Through the magic of bindings. Well, it's not magic, it's pretty intense Javascript inside Knockout.js that listens for changes and the updates the elements accordingly, but you don't have to worry about that. You can just worry about your HTML which should look like this...
<div id="chat">
<ul data-bind="foreach: messages">
<li>
<span data-bind="text: username"></span> :
<span data-bind="text: message"></span> [
<span data-bind="text: timestamp"></span> ]
</li>
</ul>
</div>
<div id="chatInput">
<input data-bind="value: message" type="text" placeholder="message..." />
<button data-bind="click: $root.chatSend()">Send</button>
<div>
Now for the final step to populate your bindings and keep them updated, is to call your view model and its methods...
$(document).ready(function () {
var imVM = new vm();
// perform your initial search and setup
imVM.setup();
// apply the bindings and hook it all together
ko.applyBindings(imVM.messages, $('#chat')[0]);
ko.applyBindings(imVM.message, $('#chatInput')[0]);
// and now update the form every three seconds
setInterval(function() { imVM.chatFetch(); }, 3000);
});
So this should give you a pretty decent start on a chat system in an HTML page. I'll leave the validation, styling, and prettifying as an exercise to the programmer...
They way I'm testing this is a simple for loop in the template to run through the elements available to the client and display them in a list.
I insert the elements through a text input identified by #query.
When I enter an element, it displays for a brief instant, and a console log that prints out Links.find().fetch() shows that the element exists, and then shortly afterwards, the element is seemingly automagically removed making any successive calls to Links.find().fetch() yield an empty list. Is this a bug within Meteor? Or is it expected behaviour and bad implementation?
UPDATE
Another weird development, I added setTimeout(function(){Links.find().fetch()},3000); to the server side to try and track what was going on. With this line, the inserts work correctly for a while, and then crashes with these errors: http://i.imgur.com/CUYDO67.png
. What is going on?
Below is my template file myapp.html
<head>
<title>myapp</title>
</head>
<body>
{{> search_bar}}
<br>
{{> list_of_links}}
</body>
<template name="search_bar">
<h1>Playlist</h1>
<input id="query" type="text" placeholder="Enter Query Here"/>
</template>
<template name="list_of_links">
<ul id="item-list">
{{#each my_playlist}}
{{> link_item}}
{{/each}}
</ul>
</template>
<template name="link_item">
<li class="link">
<div class="link-title">{{youtube_link}} {{sess}}</div>
</li>
</template>
And here follows myapp.js
//Setting up a collection of urls
Links = new Meteor.Collection("links");
if (Meteor.isClient) {
//"Subscribing" to server's published data
Deps.autorun( function(){
Meteor.subscribe( "links", Meteor.default_connection._lastSessionId);
});
//Nuke database helper function -- debugging
Template.list_of_links.clean = function(collection) {
if(collection) {
// clean items
_.each(collection.find().fetch(), function(item){
collection.remove({_id: item._id});
});
}
}
//Songs from session
Template.list_of_links.my_playlist = function () {
return Links.find();
};
Template.search_bar.events({
//http://stackoverflow.com/a/13945912/765409
'keypress #query' : function (evt,template) {
// template data, if any, is available in 'this'
if (evt.which === 13){
var url = template.find('#query').value;
//Find a nicer way of clearing shit.
$("#query").val('');
Links.insert({sess:Meteor.default_connection._lastSessionId,youtube_link:url});
var cursor = Links.find();
cursor.rewind();
console.log(cursor.fetch());
//Add to database.
}
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Meteor.publish("links", function( sess ) {
return Links.find({sess: sess}); //each client will only have links with that _lastSessionId
});
//Making sure permissions are correct
Links.allow({
insert: function (userId, doc) {
return true;
}
});
});
}
That kind of behavior is expected when user doesn't have enough privileges to create a document. The insert function creates a local copy of the doc instantly (thanks to latency compensation), and then sync it with the result of server operation. If that operation fails, the temporary document is purged from client's Minimongo.
Have you created proper rules with Collection.allow? That's the first place to look for the cause.