Stackoverflow: Caused by Nested Views

Stackoverflow: Caused by nested views?

What is the maximum number of nested layouts?

There is no specific value. You run out of stack space when you run out of stack space, and that will be contingent upon what you are doing. The main application thread has an 8KB stack, last I heard.

That being said, if Hierarchy View is showing a depth of 10 or more (root to deepest leaf), I start to get nervous, and by 15 you are likely to start running into exceptions. Your image, which is nearly illegible, appears to have a depth substantially higher than that.

You seem to have a number of wasted layers. If you have a layer in Hierarchy View, on the critical path, that has one parent and one child, that is a prime candidate to be removed, as it may not be adding any value. You have at least 5 of these candidates. Even cleaning all of that up may not be sufficient, but it is a starting point.

Is it OK to nest database views?

The main problem with nesting views is that the query optimiser is more likely to get confused and produce a sub-optimal plan. Apart from this, there is no specific overhead to using views on views unless they do something that the optimiser can't push predicates down into.

This means that the best option is to try the nested views. See if you get sensible query plans out of the reports. If it does cause problems then you may have to re-think your strategy.

T-SQL :: Enumerate all nested views

See if this gets you close to what you are looking for.

SELECT DISTINCT
sd.object_id,
referencing_view_name = OBJECT_NAME(sd.object_id),
sd.referenced_major_id,
referenced_view_name = OBJECT_NAME(sd.referenced_major_id)
FROM
sys.sql_dependencies sd
WHERE
EXISTS (SELECT 1 FROM sys.views v WHERE sd.object_id = v.object_id)
AND EXISTS (SELECT 1 FROM sys.views v WHERE sd.referenced_major_id = v.object_id);

EDIT: Same info as the original but using sys.sql_expression_dependencies in place of sys.sql_dependencies

SELECT 
sed.referencing_id,
referencing_view_name = OBJECT_NAME(sed.referencing_id),
sed.referenced_id,
referenced_view_name = sed.referenced_entity_name
FROM
sys.sql_expression_dependencies sed
WHERE
EXISTS (SELECT 1 FROM sys.views v WHERE sed.referencing_id = v.object_id)
AND EXISTS (SELECT 1 FROM sys.views v WHERE sed.referenced_id = v.object_id);

SwiftUI: matchedGeometryEffect With Nested Views

Here is an approach:

  • Get the size of MainView with GeometryReader and pass it down.
  • in DetailView use .overlay which can grow bigger than its parent view,
    if you specify an explicit .frame
  • You need another inner GeometryReaderto get the top pos of inner view for offset.

Sample Image

struct ContentView: View {

var body: some View {
// get size of overall view
GeometryReader { geo in
ScrollView(.vertical) {
Text("ShortCutView()")

PopularMoviesView(geo: geo)
}
}
}
}

struct PopularMoviesView: View {

// passed in geometry from parent view
var geo: GeometryProxy
// own view's top position, will be updated by GeometryReader further down
@State var ownTop = CGFloat.zero

@Namespace var namespace
@State var showDetails: Bool = false
@State var selectedMovie: Int?


var body: some View {

if !showDetails {
VStack {
HStack {
Text("Popular")
.font(.caption)
.padding()
Spacer()
Image(systemName: "arrow.forward")
.font(Font.title.weight(.medium))
.padding()

}
ScrollView(.horizontal) {
HStack {
ForEach(0..<10, id: \.self) { movie in
Text("MovieCell \(movie)")
.padding()
.matchedGeometryEffect(id: movie, in: namespace)
.frame(width: 200, height: 300)
.background(.yellow)

.onTapGesture {
self.selectedMovie = movie

withAnimation(Animation.interpolatingSpring(stiffness: 270, damping: 15)) {
showDetails.toggle()
}
}
}
}
}
}
}

if showDetails, let movie = selectedMovie {
// to get own view top pos
GeometryReader { geo in Color.clear.onAppear {
ownTop = geo.frame(in: .global).minY
print(ownTop)
}}

// overlay can become bigger than parent
.overlay (
Text("MovieDetail \(movie)")
.font(.largeTitle)
.matchedGeometryEffect(id: movie, in: namespace)
.frame(width: geo.size.width, height: geo.size.height)
.background(.gray)
.position(x: geo.frame(in: .global).midX, y: geo.frame(in: .global).midY - ownTop)

.onTapGesture {
withAnimation(Animation.interpolatingSpring(stiffness: 270, damping: 15)) {
showDetails.toggle()
}
}
)
}
}
}

Nested views and events in backbone

All your ModelViews are bound to the same DOM element:

class ModelView extends Backbone.View
el: "#resultsTableList"

And then each instance will bind to clicks on #resultsTableList:

events:
'click' : 'select'

Note that specifying an event in events without a selector binds it to the view's el:

Omitting the selector causes the event to be bound to the view's root element (this.el).

The result is that you have multiple views binding to the clicks on the exact same DOM element.

I think you want to drop the el and from your ModelView and let Backbone build the <li> like this:

class ModelView extends Backbone.View
tagName: 'li'
className: 'result'
#...

Then, the el for the ModelView will be an <li class="result"> and the click handler will be attached to the <li>. You'll have to adjust the rendering to put the els into the <ul id="resultsTableList"> in the caller as well.

Prism 7 throws and exception when working with nested views

This is quite a troublesome problem. I recommend videos from Brian Lagunas himself where he provides a solution and explanation. For example this one.
https://app.pluralsight.com/library/courses/prism-problems-solutions/table-of-contents

If you can watch it. If not I will try to explain.

The problem I believe is that IRegionManager from the container is a singleton and whenever you use it it is the same instance, so when you are trying to inject a region in an already injected region it will not work and you need to have a separate RegionManager for nested views.

This should fix it.
Create two interfaces

public interface ICreateRegionManagerScope
{
bool CreateRegionManagerScope { get; }
}
public interface IRegionManagerAware
{
IRegionManager RegionManager { get; set; }
}

Create a RegionManagerAwareBehaviour

public class RegionManagerAwareBehaviour : RegionBehavior
{
public const string BehaviorKey = "RegionManagerAwareBehavior";

protected override void OnAttach()
{
Region.Views.CollectionChanged += Views_CollectionChanged;
}

void Views_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
IRegionManager regionManager = Region.RegionManager;

// If the view was created with a scoped region manager, the behavior uses that region manager instead.
if (item is FrameworkElement element)
{
if (element.GetValue(RegionManager.RegionManagerProperty) is IRegionManager scopedRegionManager)
{
regionManager = scopedRegionManager;
}
}

InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = regionManager);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems)
{
InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = null);
}
}
}

private static void InvokeOnRegionManagerAwareElement(object item, Action<IRegionManagerAware> invocation)
{
if (item is IRegionManagerAware regionManagerAwareItem)
{
invocation(regionManagerAwareItem);
}

if (item is FrameworkElement frameworkElement)
{
if (frameworkElement.DataContext is IRegionManagerAware regionManagerAwareDataContext)
{
// If a view doesn't have a data context (view model) it will inherit the data context from the parent view.
// The following check is done to avoid setting the RegionManager property in the view model of the parent view by mistake.
if (frameworkElement.Parent is FrameworkElement frameworkElementParent)
{
if (frameworkElementParent.DataContext is IRegionManagerAware regionManagerAwareDataContextParent)
{
if (regionManagerAwareDataContext == regionManagerAwareDataContextParent)
{
// If all of the previous conditions are true, it means that this view doesn't have a view model
// and is using the view model of its visual parent.
return;
}
}
}

invocation(regionManagerAwareDataContext);
}
}
}
}

Create ScopedRegionNavigationContentLoader

public class ScopedRegionNavigationContentLoader : IRegionNavigationContentLoader
{
private readonly IServiceLocator serviceLocator;

/// <summary>
/// Initializes a new instance of the <see cref="RegionNavigationContentLoader"/> class with a service locator.
/// </summary>
/// <param name="serviceLocator">The service locator.</param>
public ScopedRegionNavigationContentLoader(IServiceLocator serviceLocator)
{
this.serviceLocator = serviceLocator;
}

/// <summary>
/// Gets the view to which the navigation request represented by <paramref name="navigationContext"/> applies.
/// </summary>
/// <param name="region">The region.</param>
/// <param name="navigationContext">The context representing the navigation request.</param>
/// <returns>
/// The view to be the target of the navigation request.
/// </returns>
/// <remarks>
/// If none of the views in the region can be the target of the navigation request, a new view
/// is created and added to the region.
/// </remarks>
/// <exception cref="ArgumentException">when a new view cannot be created for the navigation request.</exception>
public object LoadContent(IRegion region, NavigationContext navigationContext)
{
if (region == null) throw new ArgumentNullException("region");
if (navigationContext == null) throw new ArgumentNullException("navigationContext");

string candidateTargetContract = this.GetContractFromNavigationContext(navigationContext);

var candidates = this.GetCandidatesFromRegion(region, candidateTargetContract);

var acceptingCandidates =
candidates.Where(
v =>
{
var navigationAware = v as INavigationAware;
if (navigationAware != null && !navigationAware.IsNavigationTarget(navigationContext))
{
return false;
}

var frameworkElement = v as FrameworkElement;
if (frameworkElement == null)
{
return true;
}

navigationAware = frameworkElement.DataContext as INavigationAware;
return navigationAware == null || navigationAware.IsNavigationTarget(navigationContext);
});

var view = acceptingCandidates.FirstOrDefault();

if (view != null)
{
return view;
}

view = this.CreateNewRegionItem(candidateTargetContract);

region.Add(view, null, CreateRegionManagerScope(view));

return view;
}

private bool CreateRegionManagerScope(object view)
{
bool createRegionManagerScope = false;

if (view is ICreateRegionManagerScope viewHasScopedRegions)
createRegionManagerScope = viewHasScopedRegions.CreateRegionManagerScope;

return createRegionManagerScope;
}

/// <summary>
/// Provides a new item for the region based on the supplied candidate target contract name.
/// </summary>
/// <param name="candidateTargetContract">The target contract to build.</param>
/// <returns>An instance of an item to put into the <see cref="IRegion"/>.</returns>
protected virtual object CreateNewRegionItem(string candidateTargetContract)
{
object newRegionItem;
try
{
newRegionItem = this.serviceLocator.GetInstance<object>(candidateTargetContract);
}
catch (ActivationException e)
{
throw new InvalidOperationException(
string.Format(CultureInfo.CurrentCulture, "Cannot create navigation target", candidateTargetContract),
e);
}
return newRegionItem;
}

/// <summary>
/// Returns the candidate TargetContract based on the <see cref="NavigationContext"/>.
/// </summary>
/// <param name="navigationContext">The navigation contract.</param>
/// <returns>The candidate contract to seek within the <see cref="IRegion"/> and to use, if not found, when resolving from the container.</returns>
protected virtual string GetContractFromNavigationContext(NavigationContext navigationContext)
{
if (navigationContext == null) throw new ArgumentNullException(nameof(navigationContext));

var candidateTargetContract = UriParsingHelper.GetAbsolutePath(navigationContext.Uri);
candidateTargetContract = candidateTargetContract.TrimStart('/');
return candidateTargetContract;
}

/// <summary>
/// Returns the set of candidates that may satisfiy this navigation request.
/// </summary>
/// <param name="region">The region containing items that may satisfy the navigation request.</param>
/// <param name="candidateNavigationContract">The candidate navigation target as determined by <see cref="GetContractFromNavigationContext"/></param>
/// <returns>An enumerable of candidate objects from the <see cref="IRegion"/></returns>
protected virtual IEnumerable<object> GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
{
if (region == null) throw new ArgumentNullException(nameof(region));
return region.Views.Where(v =>
string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
}
}

In your App.xaml

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IRegionNavigationContentLoader,ScopedRegionNavigationContentLoader>();

}

protected override void ConfigureDefaultRegionBehaviors(IRegionBehaviorFactory regionBehaviors)
{
base.ConfigureDefaultRegionBehaviors(regionBehaviors);

regionBehaviors.AddIfMissing(RegionManagerAwareBehaviour.BehaviorKey, typeof(RegionManagerAwareBehaviour));
}

Coming to the finish.
Now in your ViewModelB implement IRegionManagerAware and have it as a normal property

public IRegionManager RegionManager { get; set; }

Then at your ViewB implement ICreateRegionManagerScope and have it as a get property

public bool CreateRegionManagerScope => true;

Now it should work.

Again I truly recommend the videos at Pluralsight from Brian on Prism. He has a couple of videos that help a lot when you are starting with a Prism.

Backbone.js - Should nested Views maintain references to each other?

(warning: my answer became a tl;dr treatise)

I had some of these same questions early on, and have done my homework in order to build a few pretty complex apps, so I'll offer my perspective.

The big challenge with learning backbone is that it is so unopinionated and can be (and is) used in so many different ways that it's hard to figure out how to do something "right" or at least in a good way when you're starting out. There isn't just one true way to use backbone, but its flexibility makes it an awesome structure for almost any app, and hopefully I can help provide some guidance. (I could probably attach "IMO" to every sentence in here).

First, my understanding of backbone views

In backbone apps, there are a lot of useful ways to use views. I generally see a few overlapping types of views in my apps:

I typically have one or more "root-level" views. Root-level views are often a place to initialize, render, and keep references to child views that handle specific parts of the page. The el of a root-level view is often "body" or another high level element within the body. In some cases, a root-level view has its own HTML to observe and/or render. In others, a root-level view may have no el at all and just manages child views. I keep a reference to each root-level view (there is often only one) in a global 'app' namespace object.

In addition to "root-level" views, there are typically "child views". A child view is initialized and rendered by a "parent" view, which may be a root-level view or another child view. Parent views are in charge of initializing, rendering, hiding, showing, and/or destroying their children as the app requires. Sometimes a parent view may keep track of a variable number of instances of a child View (e.g., a PlaylistView has N SongViews). Often, parents maintain references to children, but sometimes it is unnecessary (more on this below).

In addition to the 'root-level/parent/child' paradigm, I tend to see views fit into one of two categories: (1) static: meaning that once the view is initialized, the view and its el stick around, even if stuff changes inside it; and (2) dynamic, which come and go, based on various events. Typically, my root-level views are always static. They also typically correspond to an existing DOM element, e.g., 'body' or '#my-div'. Child views are often dynamic, but may be static as well. (Tips: use el: '#element-id' to use an existing DOM element as el when declaring a static View. Dynamic views typically don't specify an existing el; they use tagName id and className to describe the element that the dynamic view will generate.)

Views essentially have 3 functions: (1) render themselves and their children when told to do so by their parents or in response to events (or in the case of a root-level view, when initialized by a router or a 'main' function, etc.), (2) respond to UI events from DOM elements within their el (but not within the el of any child view) by updating models or collections or triggering custom Backbone events, and (3) observe and respond to Backbone (model, collection, etc.) events that require something to be rendered or changed within their el (but not within the el of any child view.) One sometimes-useful trick is that child views can trigger events on themselves (this.trigger('customEvent')) that parent views can observe ( childView.on('customEvent', this.handler, this) ).

For additional interesting perspectives on backbone view patterns see: this and this.

Now in that context, on to questions

1) Fear of garbage collection, scope, and of memory leaks

If you instantiate a child view as a local var in a parent's render (or other) method and render it, and then the function goes out of scope, I can understand your fear of garbage collection or that the view won't be able to do what it needs to do. No need to fear the garbage collector, just the zombies. If your view has any event handlers whatsoever, whether UI Event handlers declared in the "events" declaration, or bindings to other Backbone objects' events, or other DOM-based event listeners, your view will not be garbage collected even though you don't have a reference to it anymore--it will still exist in memory and respond to the events. On the other hand, if a view doesn't have any event handlers, then its only job is to render an element, so who cares if the javascript object that rendered it sticks around--it will probably be garbage collected because it should be. See this, for a great understanding of js garbage collection in general and how it relates to Backbone.js.

The bigger concern is of Zombie views. If views are to be removed from the DOM and essentially discarded at some point in your app, then make sure they either fully remove themselves or that parent views should keep a reference to them and remove them. And don't re-create and replace views that have already been created and not properly removed. Removal is accomplished by calling .remove() on the view, plus unbinding any external Backbone events that you were previously bound using on(...) using off(...). Recent versions (1.0+) of Backbone make this issue more easily addressed by adding the "listenTo" and "stopListening" methods to the View prototype. Understand and use these methods instead of on/off if you're dynamically adding the views to and from the DOM. Tip: Setting up a a hacky jquery "remove" event like this one can easily enable views to autonomously remove and clean-up themselves when their el is removed from the DOM (in the event that there isn't an event in your app flow that can serve the same purpose).

2) Should child views be maintained as data members of parent views?

It depends. I don't think parent views being aware of their child views for limited purposes violates any golden principles of MVC. Sometimes, a parent having member references to specific child view instances is a great way to manage child views if/when you need it. As I indicated, sometimes parent views respond to events that require them to render, re-render, hide, or remove their child views. Sometimes they may want to listen to events that child views trigger on themselves. Parents, however, shouldn't get too involved in anything inside of their child views' el's though.

That said, don't overuse these types of references. Many times, you will not need to use references to child views, because the children can take care of themselves. As I mentioned, views, once rendered, should only A) watch for UI events inside their el (but typically not inside any child view's el) and update models or collections or trigger events in response to these UI events, or B) watch for events from other backbone objects (typically models or collections or other views) and take actions (e.g., update their own UI elements) in response. In many instances, a view can take care of itself and even remove itself. If another View or other Backbone object cares about a UI event happening in your view, update a model or trigger an event on the view and let them observe it. Likewise, if something outside your view requires updated rendering in your view, listen for a change to a model or wait on a corresponding custom event. As a general principle, views should be blissfully unaware of each other, with the exception of parents minding their children when it makes sense.

3) Should child views maintain references to parent views?

No. Never. I can't think of a single scenario where you would need to accomplish something through a reference to a parent that couldn't be accomplished by either changing a model that the parent is observing, or triggering an event (e.g., a custom event saying "hey, X happened") on the child view itself or on another backbone "Events"-based object. In Backbone, I use models to represent both my data AND my state. So if something happens in a view that changes the state of my application, I then change the corresponding state attribute of a model and let other views (including the parent) listen to the automatic "change" event if they care. I also use a global "vent" bus-like object (just a basic javascript object that extends Backbone.Events) for triggering and listening on events across the app, and sometimes I trigger events on Views themselves to let parent objects know that something happened. Whatever works, while keeping your architecture as untangled as possible.

4) I don't really want top start throwing listeners around everywhere.

Well I guess one good thing about backbone is that you don't have to, but realize that the Observer pattern (ie events & listeners) and loose coupling are at the heart of backbone's flavor of MVC (notice that every single Backbone class extends Events?), and most people use it accordingly.

References?

I strongly recommend the PeepCode tutorials unless you feel you're already at a pretty advanced level. 12 bucks a piece, but you need to start with the first one or the second & third won't be very useful.

Also, here's a nice overview.

The end



Related Topics



Leave a reply



Submit