Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting base type and setting namespaces #46

Closed
mcintyre321 opened this issue Apr 6, 2017 · 17 comments
Closed

Setting base type and setting namespaces #46

mcintyre321 opened this issue Apr 6, 2017 · 17 comments

Comments

@mcintyre321
Copy link

mcintyre321 commented Apr 6, 2017

Hi again. I'm trying to replace RazorEngine with RazorLight in my FormFactory project, but am getting some errors when I run my templates.

My templates are very similar to MVC views (in fact they are MVC views when I run them inside MVC, but I read them and change them when running as standalone templates), e.g. one might look like this:

@using System.ComponentModel.DataAnnotations
@using System.Linq
@using System.Xml.Linq
@using FormFactory  

@{
    @Model.GetCustomAttribute<DisplayAttribute>().Name
    var value = Model.Value as XElement;
    if (value != null) { 
        @Html.Raw(value.ToString())
    }
}

So I have using statements, and am using @model and Model and also Html.

I got this to work with RazorEngine by making the template inherit from a base type with Html and Model on it. How can I set the base type with RazorEngine? It looks like Model is set from the .Parse(..., model) but that won't work for Html

-Also I'm getting errors like - The type or namespace name 'DataAnnotations' does not exist in the namespace 'System.ComponentModel' (are you missing an assembly reference?). Is there a way to tell RazorEngine what assemblies to use?- caused by DataAnnotations having a different namespace in 451/standard

thanks!

@toddams
Copy link
Owner

toddams commented Apr 9, 2017

HTML Helpers are not supported by RazorLight at the moment. @using, however, should work for specifying a namespaces

@mcintyre321
Copy link
Author

I have my own type FFHtmlHelper which I want to expose as a property Html on the views base type. Is there a way to set the base type?

Does it just use whatever assemblies are in the current appdomain, or do I need to specifically inject assemblies?

@toddams
Copy link
Owner

toddams commented Apr 10, 2017

Yep, inheritance is supported.

Create a class:

public class BasePage<T> : TemplatePage<T>
	{
		public override Task ExecuteAsync()
		{
			return Task.FromResult(0);
		}
	}

In your view:

@inherits YourApplicationNamespace.BasePage<dynamic>

Replace dynamic with your model type, or leave it as it is.

About assemblies:
It takes dependencies from entryAssembly, so make sure that you have everything there.

@mcintyre321
Copy link
Author

Is there a way to set some properties on the MyBasePage before executing the template?

@toddams
Copy link
Owner

toddams commented Apr 11, 2017

Yep, here is an example of code I use at RazorLight.MVC to inject properties values before rendering a page:

private static void AddEngineRenderCallbacks(IRazorLightEngine engine, IServiceProvider services)
{
	var injector = services.GetRequiredService<PropertyInjector>();
	engine.Configuration.PreRenderCallbacks.Add(template => injector.Inject(template));
}

Add your custom PreRenderCallback to EngineConfiguration

@toddams
Copy link
Owner

toddams commented Apr 12, 2017

Is everything clear @mcintyre321?

@mcintyre321
Copy link
Author

Not really, but I haven't had a chance to try it out yet!

I'm imagining I'm going to have to do something along the lines of:

class MyPageTempalteType<T> :  : TemplatePage<T>
{
    public HtmlHelper Html {get;set;}
    public override Task ExecuteAsync() => Task.FromResult(0);
}
...

var engine = EngineFactory.CreateEmbedded(typeof(MyPageTemplateType)) 



var injector = engine.services.GetRequiredService<PropertyInjector>();
engine.Configuration.PreRenderCallbacks.Add(template =>  {
    ((MyPageTemplateType) template).Html = GetHtmlHelper();
});

...

var model = new SomePageModel()
{
    Title = "Hello, world",
    Description = "Some text here"
};
string result = engine.Parse("SomePage", model); 

Is that about right?

@toddams
Copy link
Owner

toddams commented Apr 12, 2017

Part with injector is not correct. Is was just an example of how I use PreRenderCallback to fill in dependencies in templates.

Here is more complete example:

var configuration = EngineConfiguration.Default;

configuration.PreRenderCallbacks.Add(templatePage =>
{
	var page = (YourBaseType)templatePage;

	page.Html = /* Assign your property here */,
	page.MyOtherProperty = "Same here";
});

var engine = EngineFactory.CreatePhysical("C:\\", configuration);

/* Use engine then */

@mcintyre321
Copy link
Author

Is it performant to new up a new engine each time, or should it be cached? I may have different values for MyOtherProperty etc.

@toddams
Copy link
Owner

toddams commented Apr 13, 2017

Yea, that might be a problem. First solution that comes to my mind - is to add "Remove" method to PreRenderActionList, so you can replace a callback before each render

@toddams
Copy link
Owner

toddams commented Apr 14, 2017

Special for you, I added an additional overload to "Parse" method, which accepts Action<TemplatePage>, so you can add callbacks for each page individually, and not to rely on global PreRenderCallbacks of EngineConfiguration.

Here is a full example:

Code:

string result = engine.Parse("file.cshtml", model: 1337, viewBag: null, prerenderCallback: new Action<TemplatePage>((t) =>
{
	var page = (BasePage<int>)t;
	page.Property = 1337;
}));

View:

@inherits RazorLight.Sandbox.BasePage<int>

<html>
    @Property
</html>

BasePage.cs

public class BasePage<T> : TemplatePage<T>
{
    public int Property { get; set; }
    public override Task ExecuteAsync()
    {
        return Task.FromResult(1);
    }
}

Rendered result:

<html>
    1337
</html>

@toddams
Copy link
Owner

toddams commented Apr 14, 2017

Let me know if it works for you, so I can finally close this issue :)

@joetherod
Copy link

Regrading namespaces, I see there is a collection for namespaces. Can I just add the namespaces there? If so, how? I added them to that collection and it still didnt work. The razor I have uses the namespaces in the web.config, so they are not listed in the actual html.

@mcintyre321
Copy link
Author

Thanks for the new feature! I'm still trying to get this to work, have discovered NUnit doesn't work in netcore1.1 which is slowing me down a little...

@mcintyre321
Copy link
Author

mcintyre321 commented Apr 21, 2017

Still trying to get a custom base type to work...
I tried setting the using a custom PageFactory (in a custom EngineFactory copied from the Embedded one):

      IPageFactoryProvider pageFactory = new DefaultPageFactory((key) =>
            {
                ITemplateSource source = manager.Resolve(key);

                string razorTemplate = core.GenerateRazorTemplate(source, new ModelTypeInfo(typeof(FormFactoryTemplateBase)));
                var context = new CompilationContext(razorTemplate, configuration.Namespaces);

                CompilationResult compilationResult = configuration.CompilerService.Compile(context);

                return compilationResult;
            });

but the template always compiles with TemplatePage<TModel> not FormFactoryTemplateBase* - is it because [BaseType] is a const, and isn't being set from my ModelTypeInfo?

*which inherits TemplatePage

@toddams
Copy link
Owner

toddams commented Apr 21, 2017

Example that I provided works, I tested it. You cast TemplatePage to your template base type and it works. You don't need custom PageFactory

@mcintyre321
Copy link
Author

mcintyre321 commented Apr 21, 2017

I see, I had to use an explicit @inherits statement (rather than setting the base type in the factory). It seems to be working now! thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants