In the project I am working on I have a lot of serial true/false data that needs to be displayed in different ways. I have been working on a directive that will allow me to pass in the model, the table header, and heres the tricky part, number of columns.
<serial-checkbox-table title="Title" columns="2" ng-model="items"></serial-checkbox-table>
I cant seem to get the columns parameter to be respected. I built this thing at one point where the html was generated in the link phase but I was having a hard time updating the model that was passed into the directive.
heres a jsfiddle of the current state of the directive.
http://jsfiddle.net/peledies/2tVAe/
Anyone have any ideas?
What you need is a $scope.$watch on the model variable.
Here is your old cold:
var model = $scope.$eval($attrs.ngModel);
...
// compile and write to dom
Needs to updated to
$scope.$watch($attrs.ngModel,function(newVal,oldVal) {
//var model = $scope.$eval($attrs.ngModel);
// can just take model value from watch function result
var model = newVal;
...
// compile and write to dom
});
And here is your jsfiddle updated with this logic: http://jsfiddle.net/callado4/2tVAe/7/
Related
I've tried to prepare data from an OData source to show it in a bar graph in my fiori app. For this, I setup the OData model in the manifest.json. A test with a list, simply using
items="{path : 'modelname>/dataset'}
works fine and shows the content.
To prepare data for a diagram (VizFrame), I used the onInit() function in the controller of the view (mvc:XMLView). The data preparation is similar to the one discussed in question.
At first I obtain the ODataModel:
var oODataModel = this.getOwnerComponent().getModel("modelname");
Next I do the binding:
var oBindings = oODataModel.bindList("/dataset");
Unfortunately, the oBindings().getContexts() array is always empty, and also oBindings.getLength() is zero. As a consequence, the VizFrame shows only "No Data".
May it be that the data model is not fully loaded during the onInit() function, or do I misunderstand the way to access data?
Thanks in advance
Update
I temporary solved the problem by using the automatically created bind from the view displaying the data as list. I grep the "dataReceived" event from the binding getView().byId("myList").getBindings("items") and do my calculation there. The model for the diagram (since it is used in a different view) is created in the Component.js, and registered in the Core sap.ui.getCore().setModel("graphModel").
I think this solution is dirty, because the graph data depends on the list data from a different view, which causes problems, e.g. when you use a growing list (because the data in the binding gets updated and a different range is selected from the odata model).
Any suggestions, how I can get the odata model entries without depending on a different list?
The following image outlines the lifecycle of your UI5 application.
Important are the steps which are highlighted with a red circle. Basically, in your onInit you don't have full access to your model via this.getView().getModel().
That's probably why you tried using this.getOwnerComponent().getModel(). This gives you access to the model, but it's not bound to the view yet so you don't get any contexts.
Similarly metadataLoaded() returns a Promise that is fullfilled a little too early: Right after the metadata has been loaded, which might be before any view binding has been done.
What I usually do is
use onBeforeRendering
This is the lifecycle hook that gets called right after onInit. The view and its models exist, but they are not yet shown to the user. Good possibility to do stuff with your model.
use onRouteMatched
This is not really a lifecycle hook but an event handler which can be bound to the router object of your app. Since you define the event handler in your onInit it will be called later (but not too late) and you can then do your desired stuff. This obviously works only if you've set up routing.
You'll have to wait until the models metadata has been loaded. Try this:
onInit: function() {
var oBindings;
var oODataModel = this.getComponent().getModel("modelname");
oODataModel.metadataLoaded().then(function() {
oBindings = oODataModel.bindList("/dataset");
}.bind(this));
},
May it be that the data model is not fully loaded during the onInit()
function, or do I misunderstand the way to access data?
You could test if your model is fully loaded by console log it before you do the list binding
console.log(oODataModel);
var oBindings = oODataModel.bindList("/dataset");
If your model contains no data, then that's the problem.
My basic misunderstanding was to force the use of the bindings. This seems to work only with UI elements, which organize the data handling. I switched to
oODataModel.read("/dataset", {success: function(oEvent) {
// do all my calculations on the oEvent.results array
// write result into graphModel
}
});
This whole calculation is in a function attached to the requestSent event of the graphModel, which is set as model for the VizFrame in the onBeforeRendering part of the view/controller.
Ok, seems that I was having too many issues with the way in which my Angular site is setup, so I put it in a plunker as then anyone can see it.
Original question: Angular retrieve specific data into $scope variable is not working
Plunker
http://plnkr.co/edit/NsE29zjraQp9UeklJBiI?p=preview
My issues are
1. i'm not understanding how to use app.filter
2. Issue with app name
3. forEach with push inside $http.get throws error not defined
The plunker Index.html has the template code loop , app.module.js is root and the device.controller.js is where I'm using controller with $http.get call using json file to fake it.
I was attempting to use the other persons answer so this code
$scope.devices = result.data.Devices; // gives all data ...
Filter I was wondering if this with work
<div ng-repeat="device in devices">
{{ device.DeviceStatus }}
</div>
Then this code I'm not sure it in the correct "place"
seems like i'm not understanding "app"
app.filter('deviceStatus', function () {
return function (status_id) {
var statuses = ['Old Device', 'New Device', 'Activated', 'Unactivated'];
return statuses[status_id];
};
});
Example filter:
<td>{{device.DeviceId | deviceStatus}}</td>
Let me try to understand your issue.
As per your question, it seems that you have problems understanding what app is and how to use filter.
This is the working version of your plunkr. Check this url
app in your project is the ng-app directive. The ng-app directive tells AngularJS that the element is the "owner" of an AngularJS application.
For understanding filter functionality. check the below example.
You were trying to push into $scope.statuses which is not defined yet. So first define $scope.statuses to be an empty array i.e `$scope.statuses = [];
Hope this works for you!`
// To declare a filter we pass in two parameters to app.filter
// The first parameter is the name of the filter
// second is a function that will return another function that does the actual work of the filter
//here app is the module name of your project
app.filter('myFilter', function() {
// In the return function, we must pass in a single parameter which will be the data we will work on.
// We have the ability to support multiple other parameters that can be passed into the filter optionally
return function(input, optional1, optional2) {
var output;
// Do filter work here
return output;
}
});
I am using a backemnd service (parse in this case but that doesn't really matter for this question) and wanted to simply search it. I have a textbox that upon text being entered searches the server and returns an array of matchs.
My next step is to simply display my returned objects nicely in a list. Easy enough with ng-repeat but because the view has already been loaded the UI won't update to reflect the array being loading into the list. Does that make sense?
I was wondering if there was a technique to Refresh the list and show the returned search elements, and hopefully I am not being to greedy here but doing it in a way that looks good and not clunky.
I did a lot of googling with NO luck :( any advice would be amazing.
Without any code provided it is hard to guess what is wrong. Angular has two-way binding, so view should be updated automatically after changing content of an array. If it's not, it means that you probably did something wrong in your code. I present an example code which should work in this case.
Controller
angular.module('moduleName')
.controller('ViewController', ['ViewService', ViewController]);
function ViewController(ViewService) {
var self = this;
self.arrayWithData = [];
self.searchText = "";
// ---- Public functions ----
self.searchData = searchData;
// Function which loads data from service
function searchData(searchText) {
ViewService.getData(searchText).then(function(dataResponse) {
// Clear the array with data
self.arrayWithData.splice(0);
// Fill it again with new data from response
angular.forEach(dataResponse, function(item) {
self.arrayWithData.push(item);
});
});
}
// --- Private functions ---
// Controller initialization
function _initialize() {
self.searchData(self.searchText);
}
_initialize();
}
View
<div ng-controller="ViewController as view">
<input type="text" ng-model="view.searchText" />
<input type="button" value="Search!" ng-click="view.searchData(view.searchText)" />
<!-- A simple ngRepeat -->
<div ng-repeat="item in view.arrayWithData">
<!-- Do what you want with the item -->
</div>
</div>
Conclusion
By using splice() and push() you make sure that reference to your array is not changed. If you are using controllerAs syntax (as in the example), assigning new data with '=' would probably work. However, if you are using $scope to store your data in controller, losing reference to the array is the most probable reason why your code doesn't work.
I'm using SAPUI5 to make an application and I'm having an issue with an update to the model not being reflected immediately in the view.
A slightly simplified scenario: I created a text field that is taking its text from the model:
currentText = new sap.m.Text({
width: '100%',
text: '{/currentTrip/perHour}'
});
Then I create a select and attach a change handler:
sourceSelect.attachChange(oController.changeTripSource);
Inside that controller function I'm updating the view model:
var model = this.getView().getModel().getData();
model.currentTrip.perHour = 5;
All the things I do with this number flowing from this change event work (like updating a connected graph, which uses this number directly). The view itself however will not reflect the change to the model until I take another action like push a button or something. I'm not understanding why that is, or what triggers UI5 to check the changed values to the model and update the view. Does anyone have a fix for this?
Why don't you call setProperty() on the model directly? This saves you superfluous calls to update your bindings as the model will do it internally.
model.setProperty("/currentTrip/perHour", 5);
By the way, I don't see why updateBindings() shall work while refresh does not. Both methods call the checkUpdate() behind the curtains.
Always good practice to call updateBindings() whenever data is updating. Try:
model.updateBindings()
I have a bit of an interesting problem I am trying to currently figure out using angular.
I have 1 json object I need to use split between 2 controllers, I want to be able to change (save new items and delete json) the json in each controller separately. So when I modify something in one controller it just saves that part of the json - not the whole thing.
I will explain this as clearly as I can -
I have a factory that sends the json to both controllers, here's what it looks like(fake json data for now)
.factory("UserService", function($http) {
var LevelsHere = $http.get("/assets/images/generated.json")
.success(function(data){
return data;
});
return {
all: function() {
return LevelsHere;
}
};
})
Each controller then lists it as a dependancy then calls it like so -
UserService.all().then(function(data){
$scope.listTable = data.data;
});
This works great, and the 2way data binding is awesome in this case because if i change something in 1 controller, it changes immediately in the other. My real problem with this is I want to be able to save the content changed in the json in each controller individually.
So, the Json has a parent (name) and 2 children. Each controller has access to the parent and 1 of the children so for example. controller 1 has parent and child 1, and controller 2 has parent and child 2. When I want to click my save function - for example in controller 1 I want to save the json to the backend to overwrite the old stuff but only from the controller I am choosing to save. So Even if i made changes in controller 2, when I save 1, i Just want it to send parent(it's index) and child 1 to overwrite.
I think the problem may root from the fact that I am fully binding the data in each controller to all the the json maybe? Currently I am just trying to send the $scope.listTable back, and it's registering all the changes (between both sections).
Apologies if I rambled on here - I am trying to be as clear as possible. Thanks for taking the time to read!
EDIT I was thinking - and I don't know if this is possible so bear with me - Can it be designated that the json i bring into listTable only binds to what it needs. So for example - controller 1 only binded to all the parent and child 1 (and not child 2). Is this possible?
EDIT 2 Following up on the previous edit - I think I boiled down the question to - can I only save certain parts of the json instead of the whole object. I cannot have it save both child 1 and 2 at the same time because it might overwrite things that are not meant to be saved at that time. so it's either - can I just use the selected pieces of json, or can I just save the selected pieces of json. thanks again for reading!