I'm building a chrome extension and have encountered a bug I cannot wrap my head around. The problem is a single object property that becomes null in chromes' storage.
I'm testing this by doing:
console.log("pre-storage", settings);
var obj = {};
obj[storage_key] = settings;
chrome.storage.sync.set(obj, function() {
chrome.storage.sync.get(storage_key, function(data) {
console.log("post-storage", data[storage_key]);
});
});
This is the output:
pre-storage, Object {
...
soundClip: Object {
options: Array[5],
selected: Object {
label: "Soft2",
value: "snd/soft2.wav"
}
}
}
post-storage, Object {
...
soundClip: Object {
options: Array[5],
selected: null
}
}
Storing JSON.parse(JSON.stringify(obj)) instead of obj directly seems to fix this. Anyone have any ideas what might cause this? Any help is appreciated!
Edit: Making a deep copy of obj does not fix it.
Edit2: I should expand on how settings.soundClip is set. I'm using Angular (1.x) and I'm using a custom select directive. The stripped down directive looks like this:
function mySelect() {
return {
restrict: "E",
templateUrl: "mySelect.html",
scope: {
options: "=",
selected: "="
},
link: function (scope) {
scope.select = function (item) {
scope.selected = item;
};
}
}
}
Directive template view (mySelect.html):
<div>
<div ng-repeat="item in options track by $index"
ng-click="select(item)">
</div>
</div>
The properties are then two-way bound like this:
<my-select selected="settings.soundClip.selected"
options="settings.soundClip.options">
</my-select >
Since calling JSON.parse(JSON.stringify(obj)) seems to fix it, my guess is that you're having a problem with encoding your settings object with a variable instead of a string. See the answer here which might help.
Is it possible that the total quota (or per item) is being hit? Consider displaying the runtime.lastError on the set callback to see if there are any error messages.
chrome.storage.sync.set(obj, function() {
console.log('Error', runtime.lastError);
chrome.storage.sync.get(storage_key, function(data) {
console.log("post-storage", data[storage_key]);
});
});
See the limits here chrome.storage.sync.set
Related
I am building an application that has a button that loads up help based on IDs from the JSON, when I have the JSON in the controller.js I see it on the web page but if I do it externally I do not see anything. I feel I am missing something extremely simple but may be over looking it. Any ideas?
controllerNEW.js
app.factory("UserService", function($rootScope, $http){
function getFile(){
return $http.get('stat.json').then(function(data) {
return data;
});
}
return{statErr: getFile,
//hide the displayed tooltip based on its id
hideTooltip: function(key, scopes, data)
{
controllerOLD.js
app.factory("UserService", function($rootScope){
return{
statErr: [
{
selector: "#userEmail",
fieldTitle: "Email",
placement: "right",
content: "test",
offsetTop: 500,
correctKey: "#test",
inErrorList: false
},
{
selector: "#userId",
fieldId: "id1",
fieldTitle: "ID",
placement: "right",
content: " number should contain 4 zeroes '...0000...'",
offsetTop: 500,
correctKey: "0000",
inErrorList: false
}
],
//hide the displayed tooltip based on its id
hideTooltip: function(key, scopes, data)
{
The issue is getFile() returns a promise not the data itself. It is true that you have a .then(function(data) {return data; });, but, .then() also returns a promise.
So to fix this issue, in your actual code, after you call getFile() you also need to add .then() to have access to your data:
getFile().then(function(data){
// now you have access to your data
})
you need to access the data object in the response object.
example:
var jsonResponse = $http.get('content.json').then(function(response) {
return response.data;
});
In your example, data is the whole response to your request, including headers and status. To see your data from the 'stat.json' file, you would access the data object of your data. So, the change is.
function getFile(){
return $http.get('stat.json').then(function(data) {
return data.data;
});
}
you can try something like this
$http.get('stat.json').then(function(result){
$scope.dataset = result.data
console.log($scope.dataset)
},function(error){
console.log(error)
})
I'm trying to implement the afterCellEdit function inside my gridOption.onREgisterApi function. I'm not using $scope in my program as is recommended to do in the guidelines.
In fact, my question is exactly the same as this one : question
Sadly it is not answered.
When I use null as one of the answer suggest I got an error.
Here is the code :
vm.gridOptions.onRegisterApi = function(gridApi){
vm.gridApi = gridApi;
vm.gridApi.edit.on.afterCellEdit(null,function(rowEntity, colDef, newValue, oldValue){
alert("afterCellEdit");
});
};
And here is my error :
typeError: Cannot read property '$on' of null
at Object.feature.on.(anonymous function) [as afterCellEdit]
Thanks !
Edit : for #SaurabhTiwari here is my $scope.gridData alternative
function onCustomerListComplete(data) {
vm.gridOptions.data = data;
}
function OnError(reason) {
$log.error(reason);
}
function activate() {
customerService.getCustomerList()
.then(onCustomerListComplete, OnError);
}
vm.gridOptions = {
enablePaginationControls: false,
useExternalSorting: true,
enableHorizontalScrollbar : uiGridConstants.scrollbars.NEVER,
enableVerticalScrollbar : uiGridConstants.scrollbars.WHEN_NEEDED,
columnDefs: [
// will be modified once the data model is set
{ field: 'id', name: '', cellTemplate: 'content/customerList/rowEditor/customerList.rowEditor.editButton.html', width: 34 },
{ name: 'customerNumber', },
{ name: 'customerName' },
{ name: 'city' },
{ name: 'creditLimit' },
{ name: 'postalCode' },
]
};
It might be late to provide some help here and you possibly have found a solution. But I would like to explain your condition using the below code.
function (scope, handler, _this) {
if ( scope !== null && typeof(scope.$on) === 'undefined' ){
gridUtil.logError('asked to listen on ' + featureName + '.on.' + eventName + ' but scope wasn\'t passed in the input parameters. It is legitimate to pass null, but you\'ve passed something else, so you probably forgot to provide scope rather than did it deliberately, not registering');
return;
}
var deregAngularOn = registerEventWithAngular(eventId, handler, self.grid, _this);
//track our listener so we can turn off and on
var listener = {handler: handler, dereg: deregAngularOn, eventId: eventId, scope: scope, _this:_this};
self.listeners.push(listener);
var removeListener = function(){
listener.dereg();
var index = self.listeners.indexOf(listener);
self.listeners.splice(index,1);
};
//destroy tracking when scope is destroyed
if (scope) {
scope.$on('$destroy', function() {
removeListener();
});
}
return removeListener;
}
This is the original code from the ui-grid Api. You can search for it in the library or from a proper scope in application you can just try doing:
$scope.gridApi.edit.on.afterCellEdit.toString()
So the point here is that this function explicitly requires a scope or a null. So You do need to pass a scope to this listener.
The point you mentioned in your question that you are not using $scope in your code is somewhat vague. There always is a scope associated if you have a controller. Angular is pretty much about scopes (atleast Angular 1 was).
What the referred guidelines says is that you should avoid heaping everything on your $scope. The guidelines also says you should use $scope only for listening and watching, which is exactly the case here
I want to use ng-model with an external model-service. This model has two methods: getValue(variable) and setValue(variable).
So in my html I want to be able to do:
<input type="text" ng-model="balance">
Note: balance is not defined on $scope in my controller. And because we are dealing with more then 4000 different variables, I don't want to define them all on $scope.
And then on change it must call the setValue() method of the model. So in my controller I would like to have something like:
$catchAllGetter = function(variable) { // e.g. variable = 'balance'
var value = Model.getValue(variable);
return value;
}
$catchAllSetter = function(variable, value) { // called on change
Model.setValue(variable, value);
}
Is something like this possible with Angular?
My approach is similar to #Dan Prince, but the implementation differs a bit
Create a directive, that accepts name of the model variable, and then inject your model service in the directive itself to perform the getting and setting.
Edit : As specified by #Werlang, writing an attribute that replaces
ngModel will refrain you from features like validation, formatting,
debounced update, ng-change etc. So instead of writing a replacement,
we will instead wire up a supplementary attribute
.
app.directive('dynamicInput', function() {
return {
restrict: 'A',
link: function(scope, el, attr) {
scope.variableName = angular.copy(attr.ngModel); // Saving the variable name
scope[attr.ngModel] = (attr.ngModel + '_1'); // Setting a dummy value in the scope variable.
// In your case it will look something like scope[attr.ngModel] = Model.getValue(attr.ngModel);
scope.$watch(attr.ngModel, function(newValue, oldValue) {
console.log(scope.variableName + " ==> " + newValue);
//Model.setValue(scope.variableName, newValue);
});
}
};
})
Then in your HTML :
<input ng-model='balance' dynamic-input />
You can create a new directive which implements this behaviour.
<input model-getter='getFn()' model-setter='setFn($value)' />
This would be fairly straightforward to implement:
app.directive('modelGetter', function() {
return {
restrict: 'A',
scope: {
get: '&modelGetter',
set: '&modelSetter'
},
link: function(scope, element) {
element.val(scope.get());
element.on('change', function() {
var val = element.val();
scope.set({ $value: val });
});
}
};
})
look at example, i created for you. I hope I have understood you correctly
$scope.$watch('variables', function(newValue) {
console.log("triggers on variables change");
angular.forEach(newValue, function(value, key) {
Model.setValue(key, value);
});
}, true);
ngModel supports getter and setters. Here's how it works:
<input ng-model="balance" ng-model-options="{ getterSetter: true }">
This works if balance is a getter/setter function:
$scope.balance(100); // sets 100
var b = $scope.balance(); // returns 100
You don't need to expose each variable on the scope - you could just expose the Model service that you use in your example:
$scope.Model = Model;
then, in the View, bind to whatever property you need:
<input ng-model="Model.balance" ng-model-options="{ getterSetter: true }">
Have all your variables in an object array:
[
{key: "Variable 1", value: 1, kind: "number"},
{key: "Variable 2", value: "some text", kind: "text"},
{key: "Variable 3", value: new Date(), kind: "date"}
]
Then in your view you shall create them with the help of an ng-repeat:
<div ng-repeat="variable in myVariables">
<input type="{{variable.kind}}" ng-model="variable.value" ng-change="changed(variable)">
</div>
If you need to update your external service, implement a method changed(variable) in your controller.
ES5 Object properties to the rescue:
Object.defineProperty($scope, 'balance', {
enumberable: true,
get: function () {
// -- call your getter here
},
set: function (val) {
// -- call the setter here
}
});
This is native Javascript, so it does not get faster than this.
You can evaluate your model function dynamically, e.g.
<input type="text" ng-model="myModel(var)">
And in the controller:
$scope.myModel = function(var) {
return function(newValue) {
// this is a regular model function but you can use 'var' here
...
}
}
I have written the following directive:
var gameOdds = function(){
return {
template: '{{games["#homeTeam"]}} vs {{games["#awayTeam"]}}',
scope: {
games: '#'
}
};
};
<div game-odds games="{{games}}">
This uses the following JSON data (part of the json is below):
{
#id: "69486",
#homeTeam: "Home Team",
#awayTeam: "Away Team",
otherNormalValues : {
etc: "normal..."
}
}
I know that the method of selecting these keys preceded with an # symbol works when put directly into the HTML bound to a controller. But in my directive I cannot select the fields in this ["#field"] way.
Does anyone know how to do this?
Instead of using the attribute object notation, #, you can use the = instead.
DEMO
JAVASCRIPT
.directive('gameOdds', function() {
return {
template: '{{games.homeTeam}} vs {{games.awayTeam}}',
scope: {
games: '='
}
}
});
HTML
<div game-odds games="games"></div>
Update: Sorry for the late reply, as what the accepted answer had mentioned, you can access them with the [] notation, if the key starts with special characters in it:
.directive('gameOdds', function() {
return {
template: '{{games['#homeTeam']}} vs {{games['#awayTeam']}}',
scope: {
games: '='
}
}
});
The # symbol on scope transforms whatever you pass to the attribute games into text, and passes it into your directive. If you use the = symbol, you can pass a scope variable into the directive.
With #, scope.games will be a string
With =, scope.games will be your json object
var gameOdds = function(){
return {
template: '{{games["#homeTeam"]}} vs {{games["#awayTeam"]}}',
scope: {
games: '='
}
};
};
<div game-odds games="games">
To begin this question a little differently than most SO questions, here's a working bit of code:
thingsToLoad: [{
id: 'thing1',
controller: function(el) {
el.my_project_controller1({});
}
}, {
id: 'thing2',
controller: function(el) {
el.my_project_controller2({});
}
}],
init: function() {
var self = this;
$.each(self.thingsToLoad, function(index, tool) {
tool.controller(self.find('#'+tool.id));
});
}
Figuring that, because functions are first class objects, I should be able to do this:
thingsToLoad: [{
id: 'thing1',
controller: my_project_controller1
}, {
id: 'thing2',
controller: my_project_controller2
}],
init: function() {
var self = this;
$.each(self.thingsToLoad, function(index, tool) {
self.find('#'+tool.id).tool.controller({});
});
}
However, it will stop at thingsToLoad at the first declaration of a controller. The error is: Uncaught ReferenceError: my_project_controller1 is not defined Why does this happen? I can't quite figure it out.
el.my_project_controller1 is not the same as my_project_controller1.
The first is a property of an object, the second is a standalone variable. The error tells you everything; that variable is not defined.