Kitchenware

The cookbook author’s top green cuisine picks

Chop chop. A food processor is a must in Johanne Mosgaard’s green kitchen. But what are her other favourite utensils and appliances? And how does she use them? Read on to discover how she wows with waffles (the waffle iron being one of her favourites).

Johanne Mosgaard is a cookbook author, recipe developer and content creator with thousands of followers. Her forthcoming fourth title translates as ‘Green Meals for Everyone’, and represents her approach to the recipes. And the same goes for kitchenware.

“Cooking great meals doesn’t require all kinds of kitchen appliances and cookware. Because there’s nice and need. And need is something like a great pan with a ceramic PFAS-free coating and an immersion blender.”

And then there are her personal favourites. Not need, strictly speaking. But still the equipment she’d be sorry to do without.

Food processor

“I chuck all the choppables in my food processor. For mincing, not blending. Because the great thing about a food processor is that it preserves the consistency of things like hummus or falafel dough. I also use it a lot for dates, as in date balls for snacking on and for my daughter’s packed lunch. And it’s a bit less hassle than using a manual grater. But your food processor is a multipurpose appliance. You can also use it for kneading dough.

If you don’t have the space for a food processor, you can go for a mini chopper.”

Power blender

“A power blender is pricey, but has that extra oomph so you avoid lumps and get that super creamy soup or smoothie. My blender has all these amazing settings. But for me, the main thing is the consistency, so a dressing made of nuts, say, can be made smooth and creamy as opposed to gritty.” 

Mini chopper

“My mini chopper is manual, and I use it for onions, mushrooms, and whatever needs chopping finely and easily without having to bring out a large appliance. Using the mini chopper is a bit like starting a string-pull lawn mower. I use it for things like veggie-bolo. For this, I chop the greens so finely that my daughter doesn’t twig just how many veggies are in the mix. She won’t eat mushrooms if I just pan-fry them, but by churning things in the mini chopper, I can sneak some extra vegetable fibre into the meal.”

Mandolin

“My mandolin slicer is totally simple with just a single function, and I use it to make those super thin slices of things like potato for pizza, or onion, courgette and carrot for a salad.”

Waffle iron

I use my waffle maker for savoury waffles like falafel waffles. For this, I add water to soften the falafel dough slightly. I serve the waffles with a tasty hummus topping and lots of green garnish. For sweet waffles, you can replace the sugar with a couple of (over)ripe bananas, if you happen to have a few languishing on the kitchen table.

Food mixer

I use my food mixer for kneading dough, for example. That extra love and attention just gets a yeast dough to bloom even more, for smooth bounce. I’m not as into baking, so I mostly use the food mixer for pizza bases or sour dough loaves, but I do actually bake a fair amount of them.

About Johanne Mosgaard

Photo credit: Maria Amme

Cookbook author, recipe developer and Instagrammer with her ‘Green Meals for Everyone’ food philosophy. Discover her inspiring green foodscape at @johannemosgaard.

In her own opinion: 90% health freak and 10% chill. “We eat sweets Friday night and it’s important to me not to be too restrictive in my diet. You won’t get healthy from a single salad or unhealthy from a single slice of cake.”

Johanne was a keen horse rider until the age of 13 when she saw a documentary on live animal transportation, which put her off meat. She stopped eating mammals, and then went all-in green following a bout of food poisoning from fish on a holiday.

Apart from all the funny looks and odd questions about her vegetarianism at that time in Denmark, the only food she was served out was sides like potato or lettuce. But inspired by her foodie dad, Johanne started a food blog, originally as inspiration if anyone asked what they could serve her. That blog turned into an Instagram profile, and then a book contract, and she is now working on her fourth cookbook for Politikens forlag, one of Denmark’s leading publishers.

Latest blog posts

Error executing template "Designs/Swift/Paragraph/Swift_ArticleList_Custom.cshtml"
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Dynamicweb.Content.Items.Queries.Repository.IsPageAllowed(Page page)
   at Dynamicweb.Content.Items.Queries.Repository.GetPagesByIds(IEnumerable`1 parentIds, Boolean includeChildItems, Boolean checkPermissions, List`1& childPages)
   at Dynamicweb.Content.Items.Queries.Repository.SelectByParentPageIds(IEnumerable`1 parentIds, Query query, Boolean includeParagraphs, Boolean includeChildItems, Boolean checkPermissions, Boolean includeInheritedItems)
   at Dynamicweb.ItemPublisher.Frontend.GetItems()
   at Dynamicweb.ItemPublisher.Frontend.List()
   at Dynamicweb.ItemPublisher.Frontend.GetContent()
   at Dynamicweb.ItemPublisher.Frontend.GetContentBySettings(String settings)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Dynamicweb.Extensibility.AddIns.AddInManager.InvokeFunction(Object instance, String functionName, Object[] arguments)
   at Dynamicweb.Rendering.TemplateBase`1.RenderItemList(Object settings)
   at CompiledRazorTemplates.Dynamic.RazorEngine_c0607b58bde547a49bbe813c31f1a74f.<>c__DisplayClass0_0.<RenderArticleList>b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\twodayco3\evasolo.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ArticleList_Custom.cshtml:line 128
   at CompiledRazorTemplates.Dynamic.RazorEngine_c0607b58bde547a49bbe813c31f1a74f.Execute() in D:\dynamicweb.net\Solutions\twodayco3\evasolo.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ArticleList_Custom.cshtml:line 67
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using System.Web 3 @using Dynamicweb 4 @using Dynamicweb.Core 5 @using Dynamicweb.Frontend 6 @using Dynamicweb.Environment 7 8 @* TODO: REFACTURE/UPGRADE TO SWIFT (v1.25.0) *@ 9 10 @{ 11 var relationType = Model.Item.GetRawValueString("RelationType", "article"); 12 string listSource = !string.IsNullOrEmpty(Model.Item.GetString("ListSource")) ? Model.Item.GetString("ListSource") : Model.PageID.ToString(); // STANDARD ARTICLES 13 var listBehaviour = Model.Item.GetRawValueString("ListBehaviour", "articles"); // STANDARD ARTICLES 14 string articleListSortOrder = !string.IsNullOrEmpty(Model.Item.GetString("ArticleListSortOrder")) ? Model.Item.GetString("ArticleListSortOrder") : "Descending"; 15 int maxItemsInList = !string.IsNullOrEmpty(Model.Item.GetInt32("MaxItemsInList").ToString()) ? Model.Item.GetInt32("MaxItemsInList") : 10; 16 string articleListLayout = !string.IsNullOrEmpty(Model.Item.GetString("ArticleListLayout")) ? Model.Item.GetString("ArticleListLayout") : "grid"; 17 string columnTheme = !string.IsNullOrEmpty(Model.Item.GetString("ColumnTheme")) ? Model.Item.GetString("ColumnTheme") : string.Empty; 18 string columnThemeClass = columnTheme != string.Empty ? " theme " + columnTheme + " p-3" + (articleListLayout == "carousel" ? " px-lg-4" : string.Empty) : string.Empty; 19 20 <div class="h-100@(columnThemeClass) item_@Model.Item.SystemName.ToLower()"> 21 @switch (articleListLayout) 22 { 23 case "grid": 24 if (relationType == "article") 25 { 26 if (listBehaviour == "articles") 27 {@RenderArticleList("Swift_Article", listSource, maxItemsInList, articleListSortOrder) } 28 if (listBehaviour == "lists") 29 { @RenderArticleList("Swift_ArticleListPage", listSource, maxItemsInList, articleListSortOrder) } 30 } 31 else if (relationType == "clerk") 32 { 33 @RenderArticleListClerk("Swift_Article", maxItemsInList, articleListSortOrder) 34 } 35 break; 36 37 case "carousel": 38 var carouselSettings = Model.Item.GetRawValueString("CarouselSettings", "4"); 39 string slidesPerPage = $"slider-item-show{carouselSettings}"; 40 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 41 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 42 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", string.Empty)}"; 43 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 44 string sliderItemsGap = Model.Item.GetRawValueString("SliderItemsGap", "slider-item-gap") == "slider-item-nogap" ? "slider-item-nogap" : string.Empty; 45 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 46 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 47 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 48 string scrollBarForceMobile = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty; 49 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 50 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 51 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 52 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 53 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 54 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 55 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 56 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 57 string animation = Model.Item.GetRawValueString("Animation", string.Empty) != string.Empty ? $"slider-nav-animation {Model.Item.GetRawValueString("Animation")}" : string.Empty; 58 string autoplay = (Model.Item.GetBoolean("Autoplay")) ? "slider-nav-autoplay" : string.Empty; 59 string autoplayInterval = Model.Item.GetRawValueString("AutoplayInterval", string.Empty); 60 bool hideSliderNavigation = (navigationStyle == "slider-nav-none"); 61 62 <div id="Slider_@Model.ID" class="swiffy-slider @(slidesPerPage) @(navigationStyle) @(revealSlides) @(navigationPlacement) @(navigationAlwaysVisible) @(navigationVisibleOnTouch) @(sliderItemsGap) @(indicatorStyle) @(navigationShowScrollbar) @(navigationSmall) @(navigationInvertColors) @(indicatorsOutsideSlider) @(navigationNoLoop) @(indicatorsHighlightActive) @(indicatorsInvertColors) @(indicatorsVisibleOnSmallDevices) @(navigationSlideEntirePage) @(animation) @(autoplay) item_@Model.Item.SystemName.ToLower()" style="--swiffy-slider-nav-light:var(--swift-foreground-color);--swiffy-slider-nav-dark:var(--swift-background-color);visibility:hidden;opacity:0;@(scrollBarForceMobile)" data-slider-nav-autoplay-interval="@(autoplayInterval)"> 63 <div class="slider-container pb-3 py-lg-3 mt-lg-n3"> 64 @if (relationType == "article") 65 { 66 if (listBehaviour == "articles") 67 {@RenderArticleList("Swift_Article", listSource, maxItemsInList, articleListSortOrder) } 68 if (listBehaviour == "lists") 69 { @RenderArticleList("Swift_ArticleListPage", listSource, maxItemsInList, articleListSortOrder) } 70 } 71 else if (relationType == "clerk") 72 { 73 @RenderArticleListClerk("Swift_Article", maxItemsInList, articleListSortOrder) 74 } 75 </div> 76 77 @if (!hideSliderNavigation) 78 { 79 <button type="button" title="@Dynamicweb.Core.Encoders.HtmlEncoder.HtmlAttributeEncode(Translate("Previous slide"))" class="slider-nav" style="z-index:1;"> 80 <span class="visually-hidden">@Translate("Previous slide")</span> 81 </button> 82 <button type="button" title="@Dynamicweb.Core.Encoders.HtmlEncoder.HtmlAttributeEncode(Translate("Next slide"))" class="slider-nav slider-nav-next" style="z-index:1;"> 83 <span class="visually-hidden">@Translate("Next slide")</span> 84 </button> 85 } 86 @if (indicatorStyle != "slider-indicators-hidden") 87 { 88 string isActive = "active"; 89 int slideCount = 1; 90 91 <div class="slider-indicators" style="z-index:1;"> 92 @for (int i = 0; i < maxItemsInList; i++) 93 { 94 <button class="@isActive" type="button" title='@Dynamicweb.Core.Encoders.HtmlEncoder.HtmlAttributeEncode(Translate("Go to slide")) @slideCount'> 95 <span class="visually-hidden">@Translate("Go to slide") @slideCount</span> 96 </button> 97 98 slideCount++; 99 isActive = null; 100 } 101 </div> 102 } 103 104 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 105 <script type="module"> 106 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 107 document.addEventListener('load.swift.assetloader', () => { 108 swiffyslider.initSlider(document.querySelector('#Slider_@Model.ID')); 109 document.querySelector('#Slider_@Model.ID').style.opacity = 1; 110 document.querySelector('#Slider_@Model.ID').style.visibility = "visible"; 111 }); 112 </script> 113 </div> 114 115 break; 116 } 117 </div> 118 } 119 120 @helper RenderArticleList(string itemType, string listSource, int maxItemsInList, string articleListSortOrder) 121 { 122 var parent = Dynamicweb.Context.Current.Request.QueryString.Get("list"); 123 var query = string.IsNullOrEmpty(parent) ? listSource : parent; 124 var includeAllChildren = Model.Item.GetRawValueString("ListDepth", "all") == "all" ? true : false; // STANDARD ARTICLES 125 var listContext = Model.Item?.GetRawValueString("ListContext", string.Empty).Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); // STANDARD ARTICLES 126 var filterString = string.Join(" or ", listContext.Select(item => $"Tags == \"{item}\" or Tags ends with \",{item}\" or Tags starts with \"{item},\" or Tags contains \",{item},\"")); 127 128 @RenderItemList(new 129 { 130 ItemType = itemType, 131 ListTemplate = "ItemPublisher/List/List_Custom.cshtml", 132 ItemFieldsList = "*", 133 ListSourceType = "Page", 134 ListSourcePage = query, 135 ListPageSize = maxItemsInList, 136 IncludeParagraphItems = true, 137 ListOrderBy = "PublishedDate", 138 ListSecondOrderBy = "Updated", 139 ListOrderByDirection = articleListSortOrder, 140 IncludeAllChildItems = includeAllChildren, 141 Filter = itemType == "Swift_Article" ? filterString : null // Filter only if "articles only" 142 }) 143 } 144 145 @helper RenderArticleListClerk(string itemType, int maxItemsInList, string articleListSortOrder) 146 { 147 // Doc.: https://docs.clerk.io/reference/recommendationspagesubstituting 148 // - recommendations/page/substituting - Pages that are similar to any given page 149 // - recommendations/page/product - Pages that are related to any given product - NOTE: ProductID param is required 150 // - recommendations/page/category - Pages that are related to any given category - NOTE: GroupID param is required 151 152 var isProductDetail = (Pageview.Page.NavigationTag.ToLower() == "shop" && Dynamicweb.Context.Current.Request.HasRequest("ProductID")); 153 var renderClerk = (!isProductDetail || GetPageIdByNavigationTag("ProductDetailPage") == PageView.Current().CurrentParagraph.Page.ID); 154 155 var clerkEndPoint = Model.Item.GetString("ClerkEndpoint"); 156 var clerkQuery = new List<string>(); 157 158 switch (clerkEndPoint) 159 { 160 case "recommendations/page/substituting": 161 clerkQuery.Add("page=" + Model.PageID); 162 break; 163 164 case "recommendations/page/product": 165 if (Context.Current.Request.HasRequest("ProductID")) 166 { 167 clerkQuery.Add("product=" + Context.Current.Request.GetString("ProductID")); 168 } 169 else 170 { 171 renderClerk = false; 172 } 173 break; 174 175 case "recommendations/page/category": 176 if (Context.Current.Request.HasRequest("GroupID")) 177 { 178 clerkQuery.Add("category=" + Context.Current.Request.GetString("GroupID")); 179 } 180 else 181 { 182 renderClerk = false; 183 } 184 break; 185 } 186 187 if (renderClerk) 188 { 189 var response = Smartpage.EvaSolo.Clerk.Services.ClerkApi.Request(clerkEndPoint, string.Join("&", clerkQuery), null, Model.Item.GetString("ClerkLabels"), maxItemsInList); 190 if (response != null && response.Any()) 191 { 192 var itemEntries = Dynamicweb.Content.Services.Pages.GetPages(response.Select(i => Convert.ToInt32(i)).ToArray()).Where(i => i != null).Select(i => i.ItemId).ToArray(); 193 if (itemEntries.Any()) 194 { 195 Dynamicweb.Context.Current.Items["Custom_ClerkSortOrder"] = itemEntries; // NOTE: Ordered by ItemId. Used to sort in List_Custom.cshtml 196 197 @RenderItemList(new 198 { 199 ItemType = itemType, 200 ListTemplate = "ItemPublisher/List/List_Custom.cshtml", 201 ItemFieldsList = "*", 202 ListSourceType = "ItemEntries", 203 ListPageSize = itemEntries.Count(), 204 SourceItemEntries = string.Join(",", itemEntries), 205 IncludeParagraphItems = true, 206 ListOrderByDirection = articleListSortOrder 207 }) 208 } 209 } 210 } 211 else if (Pageview.IsVisualEditorMode) 212 { 213 switch (clerkEndPoint) 214 { 215 case "recommendations/page/product": 216 <div class="alert alert-dark m-0" role="alert"> 217 <span>@Translate("Articlelist: The articlelist will be shown here, if a product is used")</span> 218 </div> 219 break; 220 221 case "recommendations/page/category": 222 <div class="alert alert-dark m-0" role="alert"> 223 <span>@Translate("Articlelist: The articlelist will be shown here, if a product group is used")</span> 224 </div> 225 break; 226 } 227 } 228 } 229