how to get context of views in view container in angular - angular6

In the following example, https://stackblitz.com/edit/angular-s8udwh
I have created EmbeddedViews in the ViewContainer. When creating the views, I pass a context object and when deleting the views, I use the context to find index of the view and delete it from the view container. This works fine.
I have added a button which when clicked should print the info of views currently in the container. I can find the length and views in the container using the APIs of ViewContainer but how do I get the context object from the views?
This is the function I am unable to write
printViewInfo(){
//I want to get all the views which are currently in the container and print their context objects.
let length = this.vc.length
for(let i=0;i<length;i++){
let view = this.vc.get(i);
//how to get the context back here from view??
}
}
What is the way to find context of views in a ViewContainer? I see that get returns a ViewRef and the ViewRef doesn't contain context. But ViewContainerRef doesn't provide APIs which return an EmbeddedViewRef

I got this working by typecasting the ViewRef into EmbeddedViewRef<any>. Is this the right way? My concern is that if a ViewContainerRef contains both EmbeddedView and ComponentView then how will I distinguish between them? Or would it be a wrong design to use both EmbeddedView and ComponentView in the same ViewContainer?
printViewInfo(){
//I want to get all the views which are currently in the container and print their context objects.
let length = this.vc.length
for(let i=0;i<length;i++){
let view = <EmbeddedViewRef<any>> this.vc.get(i);
//how to get the context back here from view??
console.log("view.context.option ",view.context.option.description);
}
}

Related

Load multiple models(aggregation) in forge viewer and load other models as hidden by default until user selects from model browser

I am trying to load multiple models(aggregation) on to the forge viewer(v.6.x) initially while loading, but I want to show only one model and other models as hidden by default(can be with ghost view). Later when user clicks on eye icon from model browser, then that model should be visible/hidden.
I tried calling viewer.hideModel(modelId) after loading the model. But even though the model is showing in model browser, when I click on it, it says error model is not loaded.
var showModel = false;
this.viewer.loadModel(url, {globalOffset: { x:0, y:0, z:0 }, modelNameOverride: modelName}, () =>
{
this.isModelLoaded = true;
this.viewer.caller = this;
this.addEventListenersToViewer();
if(!showModel){
this.viewer.hideModel(modelId); // This is to hide the model by default after loading.
}
},
errorMsg => {
this.isModelLoaded = false;
this.viewer.container.style.opacity = 0;
this.modelLoadError(this.fetchTranslationByKey('getModelError'));
}
);
Expected behavior is to allow user to select from model browser, which models to show/hide on the viewer among all the models loaded initially(linked models should be hidden by default).
Current result I am getting is the linked models are showing in the model browser but when I click on that it says - Error Model is not loaded
A few issues here:
When using arrow function the context that this points to is different so be careful with that
how did you obtain the modelId? Instead of using arbitrary counter better to iterate the model array via Viewer.impl.modelQueue()
I am unable to replicate the model not loaded error with model browser. Can you provide more details or a live sample (jsfiddle/jsbin)?

How to handle $ctrl. in AngularJS?

I have a Methode from an API. It returns a promise which resolves to an $ctrl(?) object. This objects should contain a measurement and will be updated whenever it receive a new data.
getMeasurements.latest(filter) //only a object to filter through all measurements
.then(function (latestMeasurement) {
$ctrl.latestMeasurement = latestMeasurement;
});
My problem is that I don't know how to work with this data or display it in my html file. How does $ctrl work?
Here the documentation of the API
$ctrl is the view model object in your controller. This $ctrl is a name you choose (vm is another most common name), if you check your code you can see the definition as $ctrl = this;, so basically its the this keyword of the controller function.
So now if you are using $ctrl.latestMeasurement = 'someValue', then its like you are adding a property latestMeasurement to controller function.
Now how to use it in HTML?
To access the latestMeasurement property in HTML your code must have <h1>{{$ctrl.latestMeasurement}}</h1> (H1 tag is just an example.)
Here $ctrl is different from what I explained above on controller part. Here $ctrl is the value used for controllerAs property of the controller. But $ctrl is the default value of the controllerAs property, so your code may not have the controllerAs property defined, so Angular will take default value $ctrl in HTML.
This is where most people gets confused. So let me explain,
Assume in your new controller you have declared your this keyword to variable vm, and you set your controllerAs property to myCtrl, i.e;
controllerAs: 'myCtrl' while defining controller properties.
var vm = this; in your controller function.
In this case in js you have to use vm for setting values, and in HTML you have to use myCtrl. For example,
in JS controller function vm.test = 'Hello world';
in HTML <span ng-bind="myCtrl.test"></span>
The result Hello world will be displayed in your page.
Why $ctrl and not $scope?
The view model object model concept is introduced in AngularJS 1.5, it is actually part of migrating to Angular 2 where $scope no longer exsist. So in 1.5 they introduced new approch but did not removed $scope completely.
Hope the answer helped.
For basic Javascript concepts you can see http://javascriptissexy.com/16-javascript-concepts-you-must-know-well/
For more detailed AngularJS $ctrl concept you can see https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/
I suppose you are toking about this.
In this case, the
$ctrl.latestMeasurement
can means:
$ctrl, the controller where you are running this code. You can change it by $scope for example, and get the same result.
latestMeasurement, the variable where you want to store the last value of the measurement.
To explain my point of view let see the code below
<div ng-app="MeasurementApp">
<div ng-controller="MeasurementController">
<h1>{{latestMeasurement2}}</h1>
</div>
</div>
There you can see a simple angularjs app that shows a variable called latestMeasurement2 in a div and its controller called MeasurementController. Then, to display the value let check your code.
angular.module('MeasurementApp', [])
// creating the controller
.controller('MeasurementController', function(c8yMeasurements, $scope) {
// creating the variable and let it empty by now.
$scope.latestMeasurement2 = "";
// Your code
var filter = {
device: 10300,
fragment: 'c8y_Temperature',
series: 'T'
};
var realtime = true;
c8yMeasurements.latest(filter, realtime)
.then(function (latestMeasurement) {
// The latestMeasurement is where the measurement comes
// Here we just assign it into our $scope.latestMeasurement2
$scope.latestMeasurement2 = latestMeasurement;
});
});
As the documentation says
// $scope.latestMeasurement2 will be updated as soon as a new measurement is received.
$scope.latestMeasurement2 = latestMeasurement;
Hope this helps!

Error on looping through a RelatedLinks property of dynamic Node in Razor

I have a Razor partial which displays my site navigation:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var home = CurrentPage.Site();
umbraco.NodeFactory.Node navigationSettingsNode = MySite.Umbraco.NavigationSettings;
dynamic navigationSettings = new umbraco.MacroEngines.DynamicNode(navigationSettingsNode.Id);
var settings = home.Children.Where("DocumentTypeAlias == \"Settings\"").First();
}
#if (navigationSettings.HasValue("topNavigation"))
{
<ul>
dynamic topNavigation = navigationSettings.topNavigation;
var topNavigation2 = settings.topNavigation;
<span>#topNavigation</span>
<span>#topNavigation2</span>
foreach(dynamic item in topNavigation)
{
<li>
#item.caption
</li>
}
</ul>
}
Initially I was looping through topNavigation2 items which worked fine and with no problem.
Now I'm looping through topNavigation items and it throws an error:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'char' does not contain a definition for 'link'
I don't want to use var settings anymore, I want to use only dynamic navigationSettings variable. In order to get the right node of navigationSettings I need to some operation and I don't fancy to paste the same code in every view I want to use it so I want it to be accessible from dll and available to use anywhere.
Also the navigationSettings node in my Umbraco is outside of main content tree so is not a child of Home.
Why isn't it working? Both
dynamic topNavigation = navigationSettings.topNavigation;
var topNavigation2 = settings.topNavigation;
produce the same json result and both are dynamic objects.
How to make it work correctly?
I'm using MVC 5.2.3
It looks like your topNavigation property is a string, and so when you call for each on it, it's iterating through the characters in the string.
Also, don't use NodeFactory, it's deprecated. You should be using IPublishedContent instead.
I'd use the strongly typed content objects instead of dynamic, as a) they're faster, and b) they're easier to work with.
Here's a great article explaining the different ways of getting content: https://24days.in/umbraco-cms/2015/strongly-typed-vs-dynamic-content-access/

angularjs save rendered values in html in a variable

I hope someone can help me with this, It's a strange question maybe as I didn't find an answer online.
I call the database and retrieve a list (in json) of items.
Then in angularjs,I render this list by extracting relevant pieces of data(name,age,etc) and show it properly in a table as a list of rows.
I have then an edit button that takes me to another page where I want to put a dropdown list.
What I want to know if is possible to add to that dropdown list the rendered list I previously created in my previous page.
is it possible to save the previously rendered list in a variable and then use that variable in the dropdown?
thank you
You could store the list within a controller and make this data availablte to this dropdown, I think.
Instead of trying to query for the list, add the list to the template, get the list from the template and render somewhere else, I'd suggest query for the list, save the list in a service , and then when you want to use that list again, get it from the service. Something like:
service:
var services = angular.module('services');
services.factory('getListService',['$http',function($http){
var getListOfStuff = function(){
//call to database
return //your json
};
var extractNameAgeEtc = function(){
var myListOfStuff = //get list of stuff from $http or database
var myListOfNameAgeEtc = //make a list of tuples or {name,age,etc} objects
return myListOfNameAgeEtc;
};
return {
extractNameAgeEtc : extractNameAgeEtc
};
}]);
controllers:
angular.module('controllers',['services']);
var controllersModule = angular.module('controllers');
controllersModule.controller('tableRenderController',['getListService','$scope',function(getListService,$scope){
//use this with your table rendering template, probably with ng-repeat
$scope.MyTableValue = getListService.extractNameAgeEtc();
}]);
controllersModule.controller('dropdownRenderController',['getListService','$scope',function(getListService,$scope){
//use this with your dropdown rendering template, probably with ng-repeat
$scope.MyDropDownValue = getListService.extractNameAgeEtc();
}]);

Special JSON binding in WinJS ListView

I have problems binding this JSON to my list view.
http://pubapi.cryptsy.com/api.php?method=marketdatav2
No data is displayed.
Data.js
(function () {
"use strict";
var _list;
WinJS.xhr({ url: 'http://pubapi.cryptsy.com/api.php?method=marketdatav2' }).then(
function (response) {
var json = JSON.parse(response.responseText);
_list = new WinJS.Binding.List(json.return.markets);
},
function (error) {
//handle error
}
);
var publicMembers =
{
itemList: _list
};
WinJS.Namespace.define("DataExample", publicMembers);
})();
HTML:
<section aria-label="Main content" role="main">
<div id="listItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="listItem">
<div class="listItemTemplate-Detail">
<h4 data-win-bind="innerText: label"></h4>
</div>
</div>
</div>
<div id="listView" data-win-control="WinJS.UI.ListView" data-win-options="{itemDataSource : DataExample.itemList, itemTemplate: select('#listItemTemplate'), layout: {type: WinJS.UI.GridLayout}}"></div>
</section>
I feel that the API is not that well formed.
Isnt this part a bit odd?
"markets":{"ADT/XPM":{...}...}
There are three things going on in your code here.
First, a ListView must be bound to a WinJS.Binding.List's dataSource property, not the List directly. So in your HTML you can use itemDataSource: DataExample.itemList.dataSource, or you can make your DataExample.itemList dereference the dataSource at that level.
Second, you're also running into the issue that the declarative binding of itemDataSource in data-win-options is happening well before DataExample.itemList is even populated. At the point that the ListView gets instantiated, _list and therefore itemList will be undefined. This causes a problem with trying to dereference .dataSource.
The way around this is to make sure that DataExample.itemList is initialized with at least an empty instance of WinJS.Binding.List on startup. So putting this and the first bit together, we have this:
var _list = new WinJS.Binding.List();
var publicMembers =
{
itemList: _list.dataSource
};
With this, you can later replace _list with a different List instance, and the ListView will refresh itself.
This brings us to the third issue, populating the List with your HTTP response data. The WinJS.Binding.List takes an array in its constructor, not an object. You're passing the parsed JSON object straight from the HTTP request, which won't work.
Now if you have a WinJS.Binding.List instance already in _list as before, then you can just walk the object and add items directly to the List as follows:
var jm = json.return.markets;
for (var i in jm) {
_list.push(jm[i]);
}
Alternately, you could populate a separate array and then create a new List from that. In this case, however, you'll need to assign that new List.dataSource to the ListView in code:
var jm = json.return.markets;
var markets = [];
for (var i in jm) {
markets.push(jm[i]);
}
_list = new WinJS.Binding.List(markets);
var listview = document.getElementById("listView").winControl;
listview.itemDataSource = _list.dataSource;
Both ways will work (I tested them). Although the first solution is simpler and shorter, you'll need to make sure to clear out the List if you make another HTTP request and repopulate from that. With the second solution you just create a new List with each request and hand that to the ListView, which might work better depending on your particular needs.
Note also that in the second solution you can remove the itemDataSource option from the HTML altogether, and also eliminate the DataExample namespace and its variables because you'll assign the data source in code each time. Then you can also keep _list entirely local to the HTTP request.
Hope that helps. If you want to know more about ListView intricacies, see Chapter 7 of my free ebook from MSPress, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition.